feat: proper mobile zoom

This commit is contained in:
2025-07-08 13:20:27 +02:00
parent 133795f744
commit 05ad4128e4
3 changed files with 282 additions and 16 deletions

View File

@ -8,6 +8,7 @@
y?: number;
zIndex?: number;
canvasOffset?: { x: number; y: number };
canvasZoom?: number;
onMove?: (id: number, x: number, y: number) => void;
onClick?: (id: number) => void;
onDrop?: (id: number, x: number, y: number) => void;
@ -22,6 +23,7 @@
y = $bindable(0),
zIndex = $bindable(0),
canvasOffset = { x: 0, y: 0 },
canvasZoom = 1,
onMove,
onClick,
onDrop
@ -38,10 +40,11 @@
isDragging = true;
hasMoved = false;
const rect = cardElement.getBoundingClientRect();
// Calculate offset relative to card position in canvas coordinates
dragOffset = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
x: (event.clientX - canvasOffset.x) / canvasZoom - x,
y: (event.clientY - canvasOffset.y) / canvasZoom - y
};
dragStartPos = {
x: event.clientX,
@ -61,8 +64,8 @@
hasMoved = true;
}
const newX = event.clientX - dragOffset.x - canvasOffset.x;
const newY = event.clientY - dragOffset.y - canvasOffset.y;
const newX = (event.clientX - canvasOffset.x) / canvasZoom - dragOffset.x;
const newY = (event.clientY - canvasOffset.y) / canvasZoom - dragOffset.y;
x = newX;
y = newY;
@ -84,6 +87,60 @@
}
}
function handleTouchStart(event: TouchEvent) {
if (event.touches.length !== 1) return;
event.stopPropagation(); // Prevent canvas panning
const touch = event.touches[0];
isDragging = true;
hasMoved = false;
// Calculate offset relative to card position in canvas coordinates
dragOffset = {
x: (touch.clientX - canvasOffset.x) / canvasZoom - x,
y: (touch.clientY - canvasOffset.y) / canvasZoom - y
};
dragStartPos = {
x: touch.clientX,
y: touch.clientY
};
event.preventDefault();
}
function handleTouchMove(event: TouchEvent) {
if (!isDragging || event.touches.length !== 1) return;
const touch = event.touches[0];
const deltaX = Math.abs(touch.clientX - dragStartPos.x);
const deltaY = Math.abs(touch.clientY - dragStartPos.y);
if (deltaX > 10 || deltaY > 10) {
hasMoved = true;
}
const newX = (touch.clientX - canvasOffset.x) / canvasZoom - dragOffset.x;
const newY = (touch.clientY - canvasOffset.y) / canvasZoom - dragOffset.y;
x = newX;
y = newY;
onMove?.(id, newX, newY);
event.preventDefault();
}
function handleTouchEnd(event: TouchEvent) {
if (isDragging) {
onDrop?.(id, x, y);
if (!hasMoved) {
flipped = !flipped;
onClick?.(id);
}
}
isDragging = false;
event.preventDefault();
}
$effect(() => {
if (isDragging) {
document.addEventListener('mousemove', handleMouseMove);
@ -104,6 +161,9 @@
style="left: {x}px; top: {y}px; z-index: {zIndex};"
onmousedown={handleMouseDown}
onclick={handleClick}
ontouchstart={handleTouchStart}
ontouchmove={handleTouchMove}
ontouchend={handleTouchEnd}
role="button"
tabindex="0"
>

View File

@ -7,6 +7,7 @@
zIndex: number;
color: string;
canvasOffset?: { x: number; y: number };
canvasZoom?: number;
onMove?: (id: number, x: number, y: number) => void;
onUpdate?: (id: number, text: string) => void;
onDelete?: (id: number) => void;
@ -20,6 +21,7 @@
zIndex = $bindable(0),
color,
canvasOffset = { x: 0, y: 0 },
canvasZoom = 1,
onMove,
onUpdate,
onDelete
@ -38,10 +40,11 @@
isDragging = true;
hasMoved = false;
const rect = noteElement.getBoundingClientRect();
// Calculate offset relative to note position in canvas coordinates
dragOffset = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
x: (event.clientX - canvasOffset.x) / canvasZoom - x,
y: (event.clientY - canvasOffset.y) / canvasZoom - y
};
dragStartPos = {
x: event.clientX,
@ -61,8 +64,8 @@
hasMoved = true;
}
const newX = event.clientX - dragOffset.x - canvasOffset.x;
const newY = event.clientY - dragOffset.y - canvasOffset.y;
const newX = (event.clientX - canvasOffset.x) / canvasZoom - dragOffset.x;
const newY = (event.clientY - canvasOffset.y) / canvasZoom - dragOffset.y;
x = newX;
y = newY;
@ -102,6 +105,59 @@
onDelete?.(id);
}
function handleTouchStart(event: TouchEvent) {
if (event.touches.length !== 1) return;
event.stopPropagation(); // Prevent canvas panning
const touch = event.touches[0];
isDragging = true;
hasMoved = false;
// Calculate offset relative to note position in canvas coordinates
dragOffset = {
x: (touch.clientX - canvasOffset.x) / canvasZoom - x,
y: (touch.clientY - canvasOffset.y) / canvasZoom - y
};
dragStartPos = {
x: touch.clientX,
y: touch.clientY
};
event.preventDefault();
}
function handleTouchMove(event: TouchEvent) {
if (!isDragging || event.touches.length !== 1) return;
const touch = event.touches[0];
const deltaX = Math.abs(touch.clientX - dragStartPos.x);
const deltaY = Math.abs(touch.clientY - dragStartPos.y);
if (deltaX > 5 || deltaY > 5) {
hasMoved = true;
}
const newX = (touch.clientX - canvasOffset.x) / canvasZoom - dragOffset.x;
const newY = (touch.clientY - canvasOffset.y) / canvasZoom - dragOffset.y;
x = newX;
y = newY;
onMove?.(id, newX, newY);
event.preventDefault();
}
function handleTouchEnd(event: TouchEvent) {
if (isDragging) {
if (!hasMoved) {
isEditing = true;
setTimeout(() => textareaElement?.focus(), 0);
}
}
isDragging = false;
event.preventDefault();
}
$effect(() => {
if (isDragging) {
document.addEventListener('mousemove', handleMouseMove);
@ -122,6 +178,9 @@
style="left: {x}px; top: {y}px; z-index: {zIndex}; background-color: {color};"
onmousedown={handleMouseDown}
onclick={handleClick}
ontouchstart={handleTouchStart}
ontouchmove={handleTouchMove}
ontouchend={handleTouchEnd}
role="button"
tabindex="0"
>