Image Uploader

Resize image so the file isn't so large, and allow the user to rotate the image orientation before uploading.

by alienbush

HTML

<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">&#8634;</button>
        <button id="clockwiseBtn" class="clockwise-btn rotate-btn">&#8635;</button>
        <button id="uploadBtn" class="upload-btn">Upload</button>
    </div>
</div>

<canvas id="canvas" class="canvas"></canvas>

CSS

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:...

JavaScript

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
   ...