|
|
|
/*
|
|
|
|
* https://github.com/morethanwords/tweb
|
|
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
function resizeableImage(originalImage: HTMLImageElement, canvas?: HTMLCanvasElement) {
|
|
|
|
let cropComponent: HTMLDivElement,
|
|
|
|
container: HTMLDivElement,
|
|
|
|
cropImage: HTMLImageElement,
|
|
|
|
event_state: Partial<{
|
|
|
|
mouse_x: number,
|
|
|
|
mouse_y: number,
|
|
|
|
container_width: number,
|
|
|
|
container_height: number,
|
|
|
|
container_left: number,
|
|
|
|
container_top: number
|
|
|
|
}> = {},
|
|
|
|
keyZoomValue = 4.0,
|
|
|
|
MINWIDTH = 50,
|
|
|
|
MINHEIGHT = 50,
|
|
|
|
CROPWIDTH = 200,
|
|
|
|
CROPHEIGHT = 200,
|
|
|
|
cropLeft = 0,
|
|
|
|
cropTop = 0,
|
|
|
|
cropWidth = 0,
|
|
|
|
cropHeight = 0,
|
|
|
|
scaledRatio = 0;
|
|
|
|
|
|
|
|
if(originalImage.complete) init();
|
|
|
|
else originalImage.onload = init;
|
|
|
|
|
|
|
|
function removeHandlers() {
|
|
|
|
container.removeEventListener('mousedown', startMoving);
|
|
|
|
container.removeEventListener('touchstart', startMoving);
|
|
|
|
container.removeEventListener('wheel', resizing);
|
|
|
|
|
|
|
|
document.removeEventListener('mouseup', endMoving);
|
|
|
|
document.removeEventListener('touchend', endMoving);
|
|
|
|
document.removeEventListener('mousemove', moving);
|
|
|
|
document.removeEventListener('touchmove', moving);
|
|
|
|
document.removeEventListener('keypress', keyHandler);
|
|
|
|
|
|
|
|
cropComponent.remove();
|
|
|
|
container.remove();
|
|
|
|
cropImage.remove();
|
|
|
|
}
|
|
|
|
|
|
|
|
function addHandlers() {
|
|
|
|
container.addEventListener('mousedown', startMoving, false);
|
|
|
|
container.addEventListener('touchstart', startMoving, false);
|
|
|
|
container.addEventListener('wheel', resizing, false);
|
|
|
|
|
|
|
|
document.addEventListener('keypress', keyHandler, false);
|
|
|
|
//document.querySelector('.btn-crop').addEventListener('click', openCropCanvasImg);
|
|
|
|
}
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
originalImage.classList.add('crop-blur');
|
|
|
|
originalImage.draggable = false;
|
|
|
|
|
|
|
|
cropImage = new Image();
|
|
|
|
cropImage.src = originalImage.src;
|
|
|
|
cropImage.draggable = false;
|
|
|
|
cropImage.classList.add('crop-overlay-image');
|
|
|
|
|
|
|
|
if(!canvas) {
|
|
|
|
canvas = document.createElement('canvas');
|
|
|
|
}
|
|
|
|
|
|
|
|
cropComponent = document.createElement('div');
|
|
|
|
cropComponent.classList.add('crop-component');
|
|
|
|
|
|
|
|
container = document.createElement('div');
|
|
|
|
container.classList.add('crop-overlay');
|
|
|
|
|
|
|
|
const overlayColor = document.createElement('div');
|
|
|
|
overlayColor.classList.add('crop-overlay-color');
|
|
|
|
|
|
|
|
cropComponent.appendChild(container);
|
|
|
|
const wrapper = originalImage.parentNode as HTMLElement;
|
|
|
|
wrapper.appendChild(cropComponent);
|
|
|
|
cropComponent.appendChild(cropImage);
|
|
|
|
cropComponent.appendChild(originalImage);
|
|
|
|
cropComponent.appendChild(overlayColor);
|
|
|
|
container.appendChild(cropImage);
|
|
|
|
|
|
|
|
cropImage.style.maxWidth = originalImage.width + 'px';
|
|
|
|
|
|
|
|
scaledRatio = originalImage.naturalWidth / originalImage.offsetWidth;
|
|
|
|
|
|
|
|
const left = originalImage.offsetWidth / 2 - CROPWIDTH / 2;
|
|
|
|
const top = originalImage.offsetHeight / 2 - CROPHEIGHT / 2;
|
|
|
|
|
|
|
|
updateCropSize(CROPWIDTH, CROPHEIGHT);
|
|
|
|
updateCropImage(left, top);
|
|
|
|
updateContainer(left, top);
|
|
|
|
addHandlers();
|
|
|
|
//crop();
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateCropSize(width: number, height: number) {
|
|
|
|
cropWidth = width * scaledRatio;
|
|
|
|
cropHeight = height * scaledRatio;
|
|
|
|
|
|
|
|
container.style.width = width + 'px';
|
|
|
|
container.style.height = height + 'px';
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateCropImage(left: number, top: number) {
|
|
|
|
cropTop = top * scaledRatio;
|
|
|
|
cropLeft = left * scaledRatio;
|
|
|
|
|
|
|
|
cropImage.style.top = -top + 'px';
|
|
|
|
cropImage.style.left = -left + 'px';
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateContainer(left: number, top: number) {
|
|
|
|
container.style.top = top + 'px';
|
|
|
|
container.style.left = left + 'px';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the initial event details and container state
|
|
|
|
function saveEventState(e: any) {
|
|
|
|
event_state.container_width = container.offsetWidth;
|
|
|
|
event_state.container_height = container.offsetHeight;
|
|
|
|
|
|
|
|
event_state.container_left = container.offsetLeft;
|
|
|
|
event_state.container_top = container.offsetTop;
|
|
|
|
|
|
|
|
event_state.mouse_x = (e.clientX || e.pageX || e.touches && e.touches[0].clientX) + window.scrollX;
|
|
|
|
event_state.mouse_y = (e.clientY || e.pageY || e.touches && e.touches[0].clientY) + window.scrollY;
|
|
|
|
}
|
|
|
|
|
|
|
|
function imgZoom(zoom: number) {
|
|
|
|
zoom = zoom * Math.PI * 2
|
|
|
|
let newWidth = Math.floor(container.clientWidth + zoom),
|
|
|
|
newHeight = Math.floor(container.clientHeight + zoom),
|
|
|
|
w = cropImage.clientWidth,
|
|
|
|
h = cropImage.clientHeight,
|
|
|
|
left: number,
|
|
|
|
top: number,
|
|
|
|
right: number,
|
|
|
|
bottom: number;
|
|
|
|
|
|
|
|
if(newWidth < MINWIDTH) {
|
|
|
|
return;
|
|
|
|
} else if(newWidth > w) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
left = container.offsetLeft - (zoom / 2);
|
|
|
|
top = container.offsetTop - (zoom / 2);
|
|
|
|
right = left + newWidth;
|
|
|
|
bottom = top + newHeight;
|
|
|
|
|
|
|
|
if(left < 0) left = 0;
|
|
|
|
if(top < 0) top = 0;
|
|
|
|
|
|
|
|
if(right > w) return;
|
|
|
|
if(bottom > h) return;
|
|
|
|
|
|
|
|
updateCropSize(newWidth, newWidth);
|
|
|
|
updateCropImage(left, top);
|
|
|
|
updateContainer(left, top);
|
|
|
|
//crop();
|
|
|
|
}
|
|
|
|
|
|
|
|
function keyHandler(e: KeyboardEvent) {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
switch (String.fromCharCode(e.charCode)) {
|
|
|
|
case '+' :
|
|
|
|
imgZoom(keyZoomValue);
|
|
|
|
break;
|
|
|
|
case '-' :
|
|
|
|
imgZoom(-keyZoomValue);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function resizing(e: any) {
|
|
|
|
e.preventDefault();
|
|
|
|
imgZoom(e.deltaY > 0 ? 1 : -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
function startMoving(e: MouseEvent | TouchEvent) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
saveEventState(e);
|
|
|
|
|
|
|
|
document.addEventListener('mousemove', moving);
|
|
|
|
document.addEventListener('touchmove', moving);
|
|
|
|
document.addEventListener('mouseup', endMoving);
|
|
|
|
document.addEventListener('touchend', endMoving);
|
|
|
|
}
|
|
|
|
|
|
|
|
function endMoving(e: MouseEvent | TouchEvent) {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
document.removeEventListener('mouseup', endMoving);
|
|
|
|
document.removeEventListener('touchend', endMoving);
|
|
|
|
document.removeEventListener('mousemove', moving);
|
|
|
|
document.removeEventListener('touchmove', moving);
|
|
|
|
}
|
|
|
|
|
|
|
|
function moving(e: any) {
|
|
|
|
let currentTouch = {x: 0, y: 0},
|
|
|
|
left: number,
|
|
|
|
top: number,
|
|
|
|
w: number,
|
|
|
|
h: number;
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
currentTouch.x = e.pageX || e.touches && e.touches[0].pageX;
|
|
|
|
currentTouch.y = e.pageY || e.touches && e.touches[0].pageY;
|
|
|
|
|
|
|
|
left = currentTouch.x - (event_state.mouse_x - event_state.container_left);
|
|
|
|
top = currentTouch.y - (event_state.mouse_y - event_state.container_top);
|
|
|
|
w = container.offsetWidth;
|
|
|
|
h = container.offsetHeight;
|
|
|
|
|
|
|
|
if(left < 0) left = 0;
|
|
|
|
else if(left > cropImage.offsetWidth - w) left = cropImage.offsetWidth - w;
|
|
|
|
|
|
|
|
if(top < 0) top = 0;
|
|
|
|
else if(top > cropImage.offsetHeight - h) top = cropImage.offsetHeight - h;
|
|
|
|
|
|
|
|
updateCropImage(left, top);
|
|
|
|
updateContainer(left, top);
|
|
|
|
//crop();
|
|
|
|
}
|
|
|
|
|
|
|
|
function crop() {
|
|
|
|
canvas.width = cropWidth;
|
|
|
|
canvas.height = cropHeight;
|
|
|
|
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
ctx.drawImage(originalImage,
|
|
|
|
cropLeft, cropTop,
|
|
|
|
cropWidth, cropHeight,
|
|
|
|
0, 0,
|
|
|
|
cropWidth, cropHeight
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {crop, removeHandlers};
|
|
|
|
}
|
|
|
|
|
|
|
|
export default resizeableImage;
|