update html

This commit is contained in:
Shuanglei Tao
2025-11-09 09:23:41 +08:00
parent 826b669d8b
commit fcd0079181
6 changed files with 992 additions and 962 deletions

View File

@@ -1,208 +1,224 @@
let backgroundZoom = 1;
let backgroundPanX = 0;
let backgroundPanY = 0;
let isPanning = false;
let lastPanX = 0;
let lastPanY = 0;
let lastTouchDistance = 0;
class CropManager {
constructor(canvas, ctx, paintManager) {
this.canvas = canvas;
this.ctx = ctx;
this.paintManager = paintManager;
this.backgroundZoom = 1;
this.backgroundPanX = 0;
this.backgroundPanY = 0;
this.isPanning = false;
this.lastPanX = 0;
this.lastPanY = 0;
this.lastTouchDistance = 0;
function resetCropStates() {
backgroundZoom = 1;
backgroundPanX = 0;
backgroundPanY = 0;
isPanning = false;
lastPanX = 0;
lastPanY = 0;
}
function isCropMode() {
return canvas.parentNode.classList.contains('crop-mode');
}
function exitCropMode() {
canvas.parentNode.classList.remove('crop-mode');
setCanvasTitle("");
canvas.removeEventListener('wheel', handleBackgroundZoom);
canvas.removeEventListener('mousedown', handleBackgroundPanStart);
canvas.removeEventListener('mousemove', handleBackgroundPan);
canvas.removeEventListener('mouseup', handleBackgroundPanEnd);
canvas.removeEventListener('mouseleave', handleBackgroundPanEnd);
canvas.removeEventListener('touchstart', handleTouchStart);
canvas.removeEventListener('touchmove', handleTouchMove);
canvas.removeEventListener('touchend', handleBackgroundPanEnd);
canvas.removeEventListener('touchcancel', handleBackgroundPanEnd);
}
function initializeCrop() {
const imageFile = document.getElementById('imageFile');
if (imageFile.files.length == 0) {
fillCanvas('white');
return;
// 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);
}
exitCropMode();
resetCropStates();
canvas.style.backgroundImage = `url(${URL.createObjectURL(imageFile.files[0])})`;
canvas.style.backgroundSize = '100%';
canvas.style.backgroundPosition = '';
canvas.style.backgroundRepeat = 'no-repeat';
// add event listeners for zoom and pan
canvas.addEventListener('wheel', handleBackgroundZoom);
canvas.addEventListener('mousedown', handleBackgroundPanStart);
canvas.addEventListener('mousemove', handleBackgroundPan);
canvas.addEventListener('mouseup', handleBackgroundPanEnd);
canvas.addEventListener('mouseleave', handleBackgroundPanEnd);
// Touch events for mobile devices
canvas.addEventListener('touchstart', handleTouchStart);
canvas.addEventListener('touchmove', handleTouchMove);
canvas.addEventListener('touchend', handleBackgroundPanEnd);
canvas.addEventListener('touchcancel', handleBackgroundPanEnd);
// Make the canvas transparent
ctx.clearRect(0, 0, canvas.width, canvas.height);
setCanvasTitle("裁剪模式: 可用鼠标滚轮或双指触摸缩放图片");
canvas.parentNode.classList.add('crop-mode');
}
function finishCrop() {
const imageFile = document.getElementById('imageFile');
if (imageFile.files.length == 0) return;
const image = new Image();
image.onload = function () {
URL.revokeObjectURL(this.src);
const fieldsetRect = canvas.getBoundingClientRect();
const scale = (image.width / fieldsetRect.width) / backgroundZoom;
const sx = -backgroundPanX * scale;
const sy = -backgroundPanY * scale;
const sWidth = fieldsetRect.width * scale;
const sHeight = fieldsetRect.height * scale;
fillCanvas('white');
ctx.drawImage(image, sx, sy, sWidth, sHeight, 0, 0, canvas.width, canvas.height);
redrawTextElements();
redrawLineSegments();
convertDithering();
exitCropMode();
saveToHistory(); // Save after finishing crop
};
image.src = URL.createObjectURL(imageFile.files[0]);
}
function handleTouchStart(e) {
e.preventDefault();
if (e.touches.length === 1) {
handleBackgroundPanStart(e.touches[0]);
} else if (e.touches.length === 2) {
isPanning = false; // Stop panning when zooming
lastTouchDistance = getTouchDistance(e.touches);
resetStates() {
this.backgroundZoom = 1;
this.backgroundPanX = 0;
this.backgroundPanY = 0;
this.isPanning = false;
this.lastPanX = 0;
this.lastPanY = 0;
this.lastTouchDistance = 0;
}
}
function handleTouchMove(e) {
e.preventDefault();
if (isPanning && e.touches.length === 1) {
handleBackgroundPan(e.touches[0]);
} else if (e.touches.length === 2) {
const newDist = getTouchDistance(e.touches);
if (lastTouchDistance > 0) {
const zoomFactor = newDist / lastTouchDistance;
backgroundZoom *= zoomFactor;
backgroundZoom = Math.max(0.1, Math.min(5, backgroundZoom)); // Limit zoom range
updateBackgroundTransform();
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;
}
lastTouchDistance = newDist;
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');
}
}
function handleBackgroundZoom(e) {
e.preventDefault();
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
backgroundZoom *= zoomFactor;
backgroundZoom = Math.max(0.1, Math.min(5, backgroundZoom)); // Limit zoom range
updateBackgroundTransform();
}
finishCrop() {
const imageFile = document.getElementById('imageFile');
if (imageFile.files.length == 0) return;
function handleBackgroundPanStart(e) {
isPanning = true;
lastPanX = e.clientX;
lastPanY = e.clientY;
canvas.style.cursor = 'grabbing';
}
const image = new Image();
image.onload = () => {
URL.revokeObjectURL(image.src);
function handleBackgroundPan(e) {
if (isPanning) {
const deltaX = e.clientX - lastPanX;
const deltaY = e.clientY - lastPanY;
backgroundPanX += deltaX;
backgroundPanY += deltaY;
lastPanX = e.clientX;
lastPanY = e.clientY;
updateBackgroundTransform();
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.paintManager.redrawTextElements();
this.paintManager.redrawLineSegments();
convertDithering();
this.exitCropMode();
this.paintManager.saveToHistory(); // Save after finishing crop
};
image.src = URL.createObjectURL(imageFile.files[0]);
}
}
function handleBackgroundPanEnd() {
isPanning = false;
lastTouchDistance = 0; // Reset touch distance
canvas.style.cursor = 'grab';
}
function updateBackgroundTransform() {
canvas.style.backgroundSize = `${100 * backgroundZoom}%`;
canvas.style.backgroundPosition = `${backgroundPanX}px ${backgroundPanY}px`;
}
function 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)
);
}
function initCropTools() {
document.getElementById('crop-zoom-in').addEventListener('click', (e) => {
handleTouchStart(e) {
e.preventDefault();
handleBackgroundZoom({ preventDefault: () => {}, deltaY: -1 });
});
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);
}
}
document.getElementById('crop-zoom-out').addEventListener('click', (e) => {
handleTouchMove(e) {
e.preventDefault();
handleBackgroundZoom({ preventDefault: () => {}, deltaY: 1 });
});
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;
}
}
document.getElementById('crop-move-left').addEventListener('click', (e) => {
handleBackgroundZoom(e) {
e.preventDefault();
backgroundPanX -= 10;
updateBackgroundTransform();
});
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();
}
document.getElementById('crop-move-right').addEventListener('click', (e) => {
e.preventDefault();
backgroundPanX += 10;
updateBackgroundTransform();
});
handleBackgroundPanStart(e) {
this.isPanning = true;
this.lastPanX = e.clientX;
this.lastPanY = e.clientY;
this.canvas.style.cursor = 'grabbing';
}
document.getElementById('crop-move-up').addEventListener('click', (e) => {
e.preventDefault();
backgroundPanY -= 10;
updateBackgroundTransform();
});
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();
}
}
document.getElementById('crop-move-down').addEventListener('click', (e) => {
e.preventDefault();
backgroundPanY += 10;
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();
});
}
}