let imgPreview = document.getElementById('imgPreview')
let canvas = document.getElementById('canvas')
let statusMessage = document.getElementById('statusMessage')
let img = new Image
let maxSize = {
width: 800,
height: 600
}
let rotationDegrees = 0
// canvas.toBlob Polyfill from https://gist.github.com/salzhrani/02a6e807f24785a4d34b
if (!HTMLCanvasElement.prototype.toBlob) {
Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
value: function(callback, type, quality) {
var bin = atob(this.toDataURL(type, quality).split(',')[1]),
len = bin.length,
len32 = len >> 2,
a8 = new Uint8Array(len),
a32 = new Uint32Array(a8.buffer, 0, len32);
for (var i = 0, j = 0; i < len32; i++) {
a32[i] = bin.charCodeAt(j++) |
bin.charCodeAt(j++) << 8 |
bin.charCodeAt(j++) << 16 |
bin.charCodeAt(j++) << 24;
}
var tailLength = len & 3;
while (tailLength--) {
a8[j] = bin.charCodeAt(j++);
}
callback(new Blob([a8], {
'type': type || 'image/png'
}));
}
});
}
document.getElementById('fileInput').addEventListener('change', function(e) {
if (!e.target.files.length) {
return
}
let file = e.target.files[0]
if (isValidMIME(file, ['image/bmp', 'image/jpeg', 'image/png'])) {
img.src = window.URL.createObjectURL(file)
} else {
alert('Invalid file type. The image file must be one of the following: .jpg .jpeg .png .bmp')
}
})
let isValidMIME = function(file, MIMEtypes) {
for (let i = 0; i < MIMEtypes.length; i++) {
if (MIMEtypes[i] === file.type) {
return true
}
}
return false
}
img.addEventListener('load', function() {
rotationDegrees = 0
removeStatusMessage()
drawOptimizedImage(canvas, img, maxSize)
updateImgPreview(canvas, imgPreview)
})
let removeStatusMessage = function() {
statusMessage.textContent = ''
statusMessage.style.display = 'none'
}
let drawOptimizedImage = function(canvas, image, maxSize, rotationDirection) {
let degrees = updateRotationDegrees(rotationDirection)
let newSize = determineSize(image.width, image.height, maxSize.width, maxSize.height, degrees)
canvas.width = newSize.width
canvas.height = newSize.height
let ctx = canvas.getContext('2d')
ctx.save()
ctx.clearRect(0, 0, canvas.width, canvas.height)
if (degrees === 0) {
ctx.drawImage(image, 0, 0, newSize.width, newSize.height)
} else {
ctx.translate(canvas.width / 2, canvas.height / 2)
ctx.rotate(degrees * Math.PI / 180)
if (Math.abs(degrees) === 180) {
ctx.drawImage(image, -newSize.width / 2, -newSize.height / 2, newSize.width, newSize.height)
} else { // 90 or 270 degrees (values for width and height are swapped for these rotation positions)
ctx.drawImage(image, -newSize.height / 2, -newSize.width / 2, newSize.height, newSize.width)
}
}
ctx.restore()
}
let updateRotationDegrees = function(rotationDirection) {
if (rotationDirection === 'clockwise') {
rotationDegrees += 90
} else if (rotationDirection === 'anticlockwise') {
rotationDegrees -= 90
}
if (Math.abs(rotationDegrees) === 360) {
rotationDegrees = 0
}
return rotationDegrees
}
let determineSize = function(width, height, maxW, maxH, degrees) {
let w, h;
degrees = Math.abs(degrees)
if (degrees === 90 || degrees === 270) { // values for width and height are swapped for these rotation positions
w = height
h = width
} else {
w = width
h = height
}
if (w > h) {
if (w > maxW) {
h = h * maxW / w
w = maxW
}
} else {
if (h > maxH) {
w = w * maxH / h
h = maxH
}
}
return {
width: w,
height: h
}
}
let updateImgPreview = function(canvas, div) {
if (canvas.width < div.clientWidth && canvas.height < div.clientHeight) {
div.style.backgroundSize = 'auto'
} else {
div.style.backgroundSize = 'contain'
}
div.style.backgroundImage = 'url(' + canvas.toDataURL() + ')'
}
document.getElementById('clockwiseBtn').addEventListener('click', function() {
removeStatusMessage()
drawOptimizedImage(canvas, img, maxSize, 'clockwise')
updateImgPreview(canvas, imgPreview)
})
document.getElementById('anticlockwiseBtn').addEventListener('click', function() {
removeStatusMessage()
drawOptimizedImage(canvas, img, maxSize, 'anticlockwise')
updateImgPreview(canvas, imgPreview)
})
document.getElementById('uploadBtn').addEventListener('click', function() {
let fileInput = document.getElementById('fileInput')
if (!fileInput.files.length) {
alert('Please choose a file first');
return;
}
let formData = new FormData()
formData.append('fileName', 'yourCustomFileNameHere')
canvas.toBlob(function(blob) {
formData.append('image', blob)
let url = 'theURLtoSendTheFileTo'
sendForm(url, formData, doAfterUploadSuccess)
}, 'image/jpeg', 1.0)
})
let sendForm = function(url, formData, callback) {
// Simulating upload. Use the commented code below for a real upload.
statusMessage.style.display = 'block'
statusMessage.textContent = 'Uploading, please wait...'
setTimeout(callback, 2000)
// let xhr = new XMLHttpRequest()
// addUploadListeners(xhr)
// xhr.open('POST', url, true)
// xhr.onload = function () {
// if (xhr.status == 200) {
// if (callback) { callback(xhr) }
// }
// else if (xhr.status === 0) {
// alert('No response from server. Check network connection.')
// }
// else {
// alert('There was a problem uploading: ' + xhr.statusText)
// }
// }
// statusMessage.style.display = 'block'
// xhr.send(formData)
}
let addUploadListeners = function(xhr) {
xhr.addEventListener('loadstart', function(e) {
statusMessage.textContent = 'Uploading, please wait...'
})
xhr.addEventListener('abort', function(e) {
statusMessage.textContent = 'Aborted upload'
})
xhr.addEventListener('error', function(e) {
statusMessage.textContent = 'Error during upload'
})
}
let doAfterUploadSuccess = function(xhr) {
statusMessage.textContent = 'Success!'
}
body {
background-color: lightgray;
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 11pt;
font-weight: 400;
-webkit-font-smoothing: antialiased;
margin: 0;
}
button {
outline: none !important;
border-style: none;
background-color: rgb(115, 115, 250);
color: white;
padding: 10px 15px 12px 15px;
cursor: pointer;
border-radius: 2px;
font: inherit;
}
.canvas {
/* You can use display: none; to hide the canvas. The image will upload the same, whether or not the canvas is diplayed. */
display: block;
margin: 0px auto 40px auto;
}
.card {
background: white;
border-radius: 2px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.img-form {
padding: 15px 15px 0px 15px;
width: 320px;
margin: 0 auto;
}
.img-uploader-panel {
padding: 10px;
width: 340px;
margin: 0px auto 20px auto;
}
.img-uploader-header {
margin-top: 5px;
text-align: center;
}
.img-preview {
display: flex;
background-repeat: no-repeat;
background-position-x: center;
background-position-y: center;
background-size: contain;
border: 1px solid rgb(160, 160, 160);
width: 320px;
height: 320px;
margin: 0 auto;
}
.status-message {
display: none;
align-self: flex-end;
margin: 0;
line-height: 50px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
width: 100%;
text-align: center;
}
.uploader-btn-panel {
display: flex;
padding-top: 10px;
justify-content: center;
align-items: center;
}
.rotate-btn {
font-size: 32px;
padding: 5px 12px 9px 13px;
margin-right: 10px;
background-color: transparent;
color: rgba(83, 83, 249, 1);
}
.clockwise-btn {
transform: rotate(90deg);
}
.anticlockwise-btn {
transform: rotate(270deg);
}
.upload-btn {
margin-left: 20px;
}
<form action="post" id="imgForm" class="img-form">
<label for="fileInput">Add an image:</label><br>
<input type="file" name="fileInput" id="fileInput" accept=".jpg, .jpeg, .png, .bmp">
</form><br>
<div class="img-uploader-panel card">
<p class="img-uploader-header">Image Preview</p>
<div id="imgPreview" class="img-preview">
<p id="statusMessage" class="status-message"></p>
</div>
<div class="uploader-btn-panel">
<button id="anticlockwiseBtn" class="anticlockwise-btn rotate-btn">↺</button>
<button id="clockwiseBtn" class="clockwise-btn rotate-btn">↻</button>
<button id="uploadBtn" class="upload-btn">Upload</button>
</div>
</div>
<canvas id="canvas" class="canvas"></canvas>