mirror of
https://github.com/tsl0922/EPD-nRF5.git
synced 2025-12-06 15:42:48 +08:00
219 lines
7.4 KiB
JavaScript
219 lines
7.4 KiB
JavaScript
class CropManager {
|
|
constructor(canvas, ctx) {
|
|
this.canvas = canvas;
|
|
this.ctx = ctx;
|
|
this.backgroundZoom = 1;
|
|
this.backgroundPanX = 0;
|
|
this.backgroundPanY = 0;
|
|
this.isPanning = false;
|
|
this.lastPanX = 0;
|
|
this.lastPanY = 0;
|
|
this.lastTouchDistance = 0;
|
|
|
|
// Bind event handlers
|
|
this.handleBackgroundZoom = this.handleBackgroundZoom.bind(this);
|
|
this.handleBackgroundPanStart = this.handleBackgroundPanStart.bind(this);
|
|
this.handleBackgroundPan = this.handleBackgroundPan.bind(this);
|
|
this.handleBackgroundPanEnd = this.handleBackgroundPanEnd.bind(this);
|
|
this.handleTouchStart = this.handleTouchStart.bind(this);
|
|
this.handleTouchMove = this.handleTouchMove.bind(this);
|
|
}
|
|
|
|
resetStates() {
|
|
this.backgroundZoom = 1;
|
|
this.backgroundPanX = 0;
|
|
this.backgroundPanY = 0;
|
|
this.isPanning = false;
|
|
this.lastPanX = 0;
|
|
this.lastPanY = 0;
|
|
this.lastTouchDistance = 0;
|
|
}
|
|
|
|
isCropMode() {
|
|
return this.canvas.parentNode.classList.contains('crop-mode');
|
|
}
|
|
|
|
exitCropMode() {
|
|
this.canvas.parentNode.classList.remove('crop-mode');
|
|
setCanvasTitle("");
|
|
|
|
this.canvas.removeEventListener('wheel', this.handleBackgroundZoom);
|
|
this.canvas.removeEventListener('mousedown', this.handleBackgroundPanStart);
|
|
this.canvas.removeEventListener('mousemove', this.handleBackgroundPan);
|
|
this.canvas.removeEventListener('mouseup', this.handleBackgroundPanEnd);
|
|
this.canvas.removeEventListener('mouseleave', this.handleBackgroundPanEnd);
|
|
this.canvas.removeEventListener('touchstart', this.handleTouchStart);
|
|
this.canvas.removeEventListener('touchmove', this.handleTouchMove);
|
|
this.canvas.removeEventListener('touchend', this.handleBackgroundPanEnd);
|
|
this.canvas.removeEventListener('touchcancel', this.handleBackgroundPanEnd);
|
|
}
|
|
|
|
initializeCrop() {
|
|
const imageFile = document.getElementById('imageFile');
|
|
if (imageFile.files.length == 0) {
|
|
fillCanvas('white');
|
|
return;
|
|
}
|
|
|
|
this.exitCropMode();
|
|
this.resetStates();
|
|
|
|
this.canvas.style.backgroundImage = `url(${URL.createObjectURL(imageFile.files[0])})`;
|
|
this.canvas.style.backgroundSize = '100%';
|
|
this.canvas.style.backgroundPosition = '';
|
|
this.canvas.style.backgroundRepeat = 'no-repeat';
|
|
|
|
// add event listeners for zoom and pan
|
|
this.canvas.addEventListener('wheel', this.handleBackgroundZoom);
|
|
this.canvas.addEventListener('mousedown', this.handleBackgroundPanStart);
|
|
this.canvas.addEventListener('mousemove', this.handleBackgroundPan);
|
|
this.canvas.addEventListener('mouseup', this.handleBackgroundPanEnd);
|
|
this.canvas.addEventListener('mouseleave', this.handleBackgroundPanEnd);
|
|
|
|
// Touch events for mobile devices
|
|
this.canvas.addEventListener('touchstart', this.handleTouchStart);
|
|
this.canvas.addEventListener('touchmove', this.handleTouchMove);
|
|
this.canvas.addEventListener('touchend', this.handleBackgroundPanEnd);
|
|
this.canvas.addEventListener('touchcancel', this.handleBackgroundPanEnd);
|
|
|
|
// Make the canvas transparent
|
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
|
|
setCanvasTitle("裁剪模式: 可用鼠标滚轮或双指触摸缩放图片");
|
|
this.canvas.parentNode.classList.add('crop-mode');
|
|
}
|
|
|
|
finishCrop(callback) {
|
|
const imageFile = document.getElementById('imageFile');
|
|
if (imageFile.files.length == 0) return;
|
|
|
|
const image = new Image();
|
|
image.onload = () => {
|
|
URL.revokeObjectURL(image.src);
|
|
|
|
const fieldsetRect = this.canvas.getBoundingClientRect();
|
|
const scale = (image.width / fieldsetRect.width) / this.backgroundZoom;
|
|
|
|
const sx = -this.backgroundPanX * scale;
|
|
const sy = -this.backgroundPanY * scale;
|
|
const sWidth = fieldsetRect.width * scale;
|
|
const sHeight = fieldsetRect.height * scale;
|
|
|
|
fillCanvas('white');
|
|
this.ctx.drawImage(image, sx, sy, sWidth, sHeight, 0, 0, this.canvas.width, this.canvas.height);
|
|
|
|
this.exitCropMode();
|
|
if (callback) callback();
|
|
};
|
|
image.src = URL.createObjectURL(imageFile.files[0]);
|
|
}
|
|
|
|
handleTouchStart(e) {
|
|
e.preventDefault();
|
|
if (e.touches.length === 1) {
|
|
this.handleBackgroundPanStart(e.touches[0]);
|
|
} else if (e.touches.length === 2) {
|
|
this.isPanning = false; // Stop panning when zooming
|
|
this.lastTouchDistance = this.getTouchDistance(e.touches);
|
|
}
|
|
}
|
|
|
|
handleTouchMove(e) {
|
|
e.preventDefault();
|
|
if (this.isPanning && e.touches.length === 1) {
|
|
this.handleBackgroundPan(e.touches[0]);
|
|
} else if (e.touches.length === 2) {
|
|
const newDist = this.getTouchDistance(e.touches);
|
|
if (this.lastTouchDistance > 0) {
|
|
const zoomFactor = newDist / this.lastTouchDistance;
|
|
this.backgroundZoom *= zoomFactor;
|
|
this.backgroundZoom = Math.max(0.1, Math.min(5, this.backgroundZoom)); // Limit zoom range
|
|
this.updateBackgroundTransform();
|
|
}
|
|
this.lastTouchDistance = newDist;
|
|
}
|
|
}
|
|
|
|
handleBackgroundZoom(e) {
|
|
e.preventDefault();
|
|
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
|
|
this.backgroundZoom *= zoomFactor;
|
|
this.backgroundZoom = Math.max(0.1, Math.min(5, this.backgroundZoom)); // Limit zoom range
|
|
this.updateBackgroundTransform();
|
|
}
|
|
|
|
handleBackgroundPanStart(e) {
|
|
this.isPanning = true;
|
|
this.lastPanX = e.clientX;
|
|
this.lastPanY = e.clientY;
|
|
this.canvas.style.cursor = 'grabbing';
|
|
}
|
|
|
|
handleBackgroundPan(e) {
|
|
if (this.isPanning) {
|
|
const deltaX = e.clientX - this.lastPanX;
|
|
const deltaY = e.clientY - this.lastPanY;
|
|
this.backgroundPanX += deltaX;
|
|
this.backgroundPanY += deltaY;
|
|
this.lastPanX = e.clientX;
|
|
this.lastPanY = e.clientY;
|
|
this.updateBackgroundTransform();
|
|
}
|
|
}
|
|
|
|
handleBackgroundPanEnd() {
|
|
this.isPanning = false;
|
|
this.lastTouchDistance = 0; // Reset touch distance
|
|
this.canvas.style.cursor = 'grab';
|
|
}
|
|
|
|
updateBackgroundTransform() {
|
|
this.canvas.style.backgroundSize = `${100 * this.backgroundZoom}%`;
|
|
this.canvas.style.backgroundPosition = `${this.backgroundPanX}px ${this.backgroundPanY}px`;
|
|
}
|
|
|
|
getTouchDistance(touches) {
|
|
const touch1 = touches[0];
|
|
const touch2 = touches[1];
|
|
return Math.sqrt(
|
|
Math.pow(touch2.clientX - touch1.clientX, 2) +
|
|
Math.pow(touch2.clientY - touch1.clientY, 2)
|
|
);
|
|
}
|
|
|
|
initCropTools() {
|
|
document.getElementById('crop-zoom-in').addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
this.handleBackgroundZoom({ preventDefault: () => { }, deltaY: -1 });
|
|
});
|
|
|
|
document.getElementById('crop-zoom-out').addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
this.handleBackgroundZoom({ preventDefault: () => { }, deltaY: 1 });
|
|
});
|
|
|
|
document.getElementById('crop-move-left').addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
this.backgroundPanX -= 10;
|
|
this.updateBackgroundTransform();
|
|
});
|
|
|
|
document.getElementById('crop-move-right').addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
this.backgroundPanX += 10;
|
|
this.updateBackgroundTransform();
|
|
});
|
|
|
|
document.getElementById('crop-move-up').addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
this.backgroundPanY -= 10;
|
|
this.updateBackgroundTransform();
|
|
});
|
|
|
|
document.getElementById('crop-move-down').addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
this.backgroundPanY += 10;
|
|
this.updateBackgroundTransform();
|
|
});
|
|
}
|
|
} |