Skip to content

Instantly share code, notes, and snippets.

@RubenVerg
Created September 8, 2020 17:35
Show Gist options
  • Save RubenVerg/8dce5e964541f4bdd9a50c3bde97b2e0 to your computer and use it in GitHub Desktop.
Save RubenVerg/8dce5e964541f4bdd9a50c3bde97b2e0 to your computer and use it in GitHub Desktop.
/**
* @var {JQueryStatic} $ jQuery
*/
/**
* @typedef {'deg'|'rad'|'grad'|'turn'} RotationUnit
*/
/**
* Rotates an image.
* @param {string} img Base64 image to be rotated
* @param {[RotationUnit, string | number | bigint]} [degrees=['turn', 0.5]] How much to rotate, in `[unit, amount]` format
* @param {string} [mimeType='image/png'] The MIME type of both the input image and the output one.
* @returns {string}
*/
function rotateImage(img, degrees, mimeType) {
degrees = degrees || ['turn', 0.5];
mimeType = mimeType || 'image/png';
const canvas = document.createElement('canvas'), ctx = canvas.getContext('2d');
const imgEl = new Image();
imgEl.src = `data:${mimeType};base64,${img}`;
canvas.width = imgEl.width;
canvas.height = imgEl.height;
ctx.drawImage(imgEl, 0, 0);
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(changeRotationUnits(degrees[0], 'rad')(parseFloat(degrees[1].toString())));
return canvas.toDataURL(mimeType);
}
/**
* @param {RotationUnit} from
* @param {RotationUnit} to
* @returns {(rot: number) => number}
*/
function changeRotationUnits(from, to) {
const transforms = {
'deg|deg': rot => rot,
'deg|rad': rot => rot * Math.PI / 180,
'deg|grad': rot => rot * 200 / 180,
'deg|turn': rot => rot / 360,
'rad|deg': rot => rot * 180 / Math.PI,
'rad|rad': rot => rot,
'rad|grad': rot => rot * 200 / Math.PI,
'rad|turn': rot => rot * 180 / Math.PI / 360,
'grad|deg': rot => rot * 180 / 200,
'grad|rad': rot => rot * Math.PI / 200,
'grad|grad': rot => rot,
'grad|turn': rot => rot * 180 / 200 / 360,
'turn|deg': rot => rot * 360,
'turn|rad': rot => rot * 360 * Math.PI / 180,
'turn|grad': rot => rot * 360 * 200 / 180,
'turn|turn': rot => rot,
}
return transforms[`${from}|${to}`];
}
const spinnerB64 = 'iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAACvlBMVEUAAAAAAP8AgIAAVaoAgIAAZpkAVaoAbZIAYJ8AcY4AXaIAapUAYp0AbZIAZpkAYJ8AaZYAY5wAa5QAZpkAYZ4AZJsAapUAZpkAYp0AaJcAZJsAapUAZpkAY5wAaJcAZJsAZpkAY5wAZ5gAZZoAaZYAZpkAZJsAZ5gAZZoAaJcAZpkAZJsAZ5gAZZoAaJcAZJsAZ5gAZZoAaJcAZJsAZ5gAZZoAaJcAZpkAZJsAZ5gAZZoAaJcAZpkAZJsAZ5gAZZoAZ5gAZpkAZZoAZ5gAZZoAZ5gAZpkAZZoAZ5gAZpkAZZoAZ5gAZpkAZZoAZ5gAZZoAZ5gAZpkAZZoAZ5gAZZoAZ5gAZpkAZZoAZ5gAZZoAZ5gAZpkAZZoAZ5kAZpkAZ5gAZpkAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZpkAZZoAZpkAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZpkAZZoAZpkAZpkAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZ5gAZpkAZ5gAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZpkAZ5gAZpkAZZoAZpkAZ5gAZpkAZZoAZpkAZ5kAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpkAZpn////053lDAAAA6HRSTlMAAQIDBAUGBwgJCwwNDg8QERITFBUXGBkaGxwdHh8gISMkJSYnKCkqKywtLi8wMTM0NTY4OTo7PD0+P0BBQkNERUZHSElKS0xPUFNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpa2xtbm9wcXN0dXZ3eHl6e3x9fn+AgoOEh4qLjI2Oj5CRkpOUlpeYmZucnZ6foKGi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'
/**
* Whether the modal for loading should be shown
* @type {boolean}
*/
let inLoadingState = true;
/**
* The last loading state seen by the modal shower, to understand where to change
* @type {boolean}
*/
let cachedLoadingState = null;
/**
* The ID for the latest animation frame of the modal checker
* @type {number}
*/
let loadModalRAFID = null;
let degs = 0;
/**
* Shows or hides the modal.
* @type {() => void)}
*/
function showLoadModal() {
if (cachedLoadingState !== inLoadingState) {
$('#loading').modal();
}
document.querySelector('#fav').href = rotateImage(spinnerB64, ['turn']);
degs += 1;
cachedLoadingState = inLoadingState;
loadModalRAFID = requestAnimationFrame(showLoadModal)
}
loadModalRAFID = requestAnimationFrame(showLoadModal);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment