feat: proper mobile zoom
This commit is contained in:
@ -8,6 +8,7 @@
|
|||||||
y?: number;
|
y?: number;
|
||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
canvasOffset?: { x: number; y: number };
|
canvasOffset?: { x: number; y: number };
|
||||||
|
canvasZoom?: number;
|
||||||
onMove?: (id: number, x: number, y: number) => void;
|
onMove?: (id: number, x: number, y: number) => void;
|
||||||
onClick?: (id: number) => void;
|
onClick?: (id: number) => void;
|
||||||
onDrop?: (id: number, x: number, y: number) => void;
|
onDrop?: (id: number, x: number, y: number) => void;
|
||||||
@ -22,6 +23,7 @@
|
|||||||
y = $bindable(0),
|
y = $bindable(0),
|
||||||
zIndex = $bindable(0),
|
zIndex = $bindable(0),
|
||||||
canvasOffset = { x: 0, y: 0 },
|
canvasOffset = { x: 0, y: 0 },
|
||||||
|
canvasZoom = 1,
|
||||||
onMove,
|
onMove,
|
||||||
onClick,
|
onClick,
|
||||||
onDrop
|
onDrop
|
||||||
@ -38,10 +40,11 @@
|
|||||||
|
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
hasMoved = false;
|
hasMoved = false;
|
||||||
const rect = cardElement.getBoundingClientRect();
|
|
||||||
|
// Calculate offset relative to card position in canvas coordinates
|
||||||
dragOffset = {
|
dragOffset = {
|
||||||
x: event.clientX - rect.left,
|
x: (event.clientX - canvasOffset.x) / canvasZoom - x,
|
||||||
y: event.clientY - rect.top
|
y: (event.clientY - canvasOffset.y) / canvasZoom - y
|
||||||
};
|
};
|
||||||
dragStartPos = {
|
dragStartPos = {
|
||||||
x: event.clientX,
|
x: event.clientX,
|
||||||
@ -61,8 +64,8 @@
|
|||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newX = event.clientX - dragOffset.x - canvasOffset.x;
|
const newX = (event.clientX - canvasOffset.x) / canvasZoom - dragOffset.x;
|
||||||
const newY = event.clientY - dragOffset.y - canvasOffset.y;
|
const newY = (event.clientY - canvasOffset.y) / canvasZoom - dragOffset.y;
|
||||||
|
|
||||||
x = newX;
|
x = newX;
|
||||||
y = newY;
|
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(() => {
|
$effect(() => {
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
document.addEventListener('mousemove', handleMouseMove);
|
document.addEventListener('mousemove', handleMouseMove);
|
||||||
@ -104,6 +161,9 @@
|
|||||||
style="left: {x}px; top: {y}px; z-index: {zIndex};"
|
style="left: {x}px; top: {y}px; z-index: {zIndex};"
|
||||||
onmousedown={handleMouseDown}
|
onmousedown={handleMouseDown}
|
||||||
onclick={handleClick}
|
onclick={handleClick}
|
||||||
|
ontouchstart={handleTouchStart}
|
||||||
|
ontouchmove={handleTouchMove}
|
||||||
|
ontouchend={handleTouchEnd}
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
zIndex: number;
|
zIndex: number;
|
||||||
color: string;
|
color: string;
|
||||||
canvasOffset?: { x: number; y: number };
|
canvasOffset?: { x: number; y: number };
|
||||||
|
canvasZoom?: number;
|
||||||
onMove?: (id: number, x: number, y: number) => void;
|
onMove?: (id: number, x: number, y: number) => void;
|
||||||
onUpdate?: (id: number, text: string) => void;
|
onUpdate?: (id: number, text: string) => void;
|
||||||
onDelete?: (id: number) => void;
|
onDelete?: (id: number) => void;
|
||||||
@ -20,6 +21,7 @@
|
|||||||
zIndex = $bindable(0),
|
zIndex = $bindable(0),
|
||||||
color,
|
color,
|
||||||
canvasOffset = { x: 0, y: 0 },
|
canvasOffset = { x: 0, y: 0 },
|
||||||
|
canvasZoom = 1,
|
||||||
onMove,
|
onMove,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onDelete
|
onDelete
|
||||||
@ -38,10 +40,11 @@
|
|||||||
|
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
hasMoved = false;
|
hasMoved = false;
|
||||||
const rect = noteElement.getBoundingClientRect();
|
|
||||||
|
// Calculate offset relative to note position in canvas coordinates
|
||||||
dragOffset = {
|
dragOffset = {
|
||||||
x: event.clientX - rect.left,
|
x: (event.clientX - canvasOffset.x) / canvasZoom - x,
|
||||||
y: event.clientY - rect.top
|
y: (event.clientY - canvasOffset.y) / canvasZoom - y
|
||||||
};
|
};
|
||||||
dragStartPos = {
|
dragStartPos = {
|
||||||
x: event.clientX,
|
x: event.clientX,
|
||||||
@ -61,8 +64,8 @@
|
|||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newX = event.clientX - dragOffset.x - canvasOffset.x;
|
const newX = (event.clientX - canvasOffset.x) / canvasZoom - dragOffset.x;
|
||||||
const newY = event.clientY - dragOffset.y - canvasOffset.y;
|
const newY = (event.clientY - canvasOffset.y) / canvasZoom - dragOffset.y;
|
||||||
|
|
||||||
x = newX;
|
x = newX;
|
||||||
y = newY;
|
y = newY;
|
||||||
@ -102,6 +105,59 @@
|
|||||||
onDelete?.(id);
|
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(() => {
|
$effect(() => {
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
document.addEventListener('mousemove', handleMouseMove);
|
document.addEventListener('mousemove', handleMouseMove);
|
||||||
@ -122,6 +178,9 @@
|
|||||||
style="left: {x}px; top: {y}px; z-index: {zIndex}; background-color: {color};"
|
style="left: {x}px; top: {y}px; z-index: {zIndex}; background-color: {color};"
|
||||||
onmousedown={handleMouseDown}
|
onmousedown={handleMouseDown}
|
||||||
onclick={handleClick}
|
onclick={handleClick}
|
||||||
|
ontouchstart={handleTouchStart}
|
||||||
|
ontouchmove={handleTouchMove}
|
||||||
|
ontouchend={handleTouchEnd}
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
|
@ -101,9 +101,13 @@
|
|||||||
|
|
||||||
let deckPosition = $state({ x: 50, y: 300 });
|
let deckPosition = $state({ x: 50, y: 300 });
|
||||||
let canvasOffset = $state({ x: 0, y: 0 });
|
let canvasOffset = $state({ x: 0, y: 0 });
|
||||||
|
let canvasZoom = $state(1);
|
||||||
let isDragging = $state(false);
|
let isDragging = $state(false);
|
||||||
let dragStart = $state({ x: 0, y: 0 });
|
let dragStart = $state({ x: 0, y: 0 });
|
||||||
let dragStartOffset = $state({ x: 0, y: 0 });
|
let dragStartOffset = $state({ x: 0, y: 0 });
|
||||||
|
let isPinching = $state(false);
|
||||||
|
let lastPinchDistance = $state(0);
|
||||||
|
let pinchCenter = $state({ x: 0, y: 0 });
|
||||||
|
|
||||||
let cards = $state<CardData[]>([]);
|
let cards = $state<CardData[]>([]);
|
||||||
let maxZIndex = $state(0);
|
let maxZIndex = $state(0);
|
||||||
@ -304,12 +308,36 @@
|
|||||||
|
|
||||||
function handleCanvasWheel(event: WheelEvent) {
|
function handleCanvasWheel(event: WheelEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const scrollSpeed = 1;
|
|
||||||
canvasOffset.x -= event.deltaX * scrollSpeed;
|
|
||||||
canvasOffset.y -= event.deltaY * scrollSpeed;
|
|
||||||
|
|
||||||
canvasOffset.x = Math.max(-2000, Math.min(2000, canvasOffset.x));
|
if (event.ctrlKey || event.metaKey) {
|
||||||
canvasOffset.y = Math.max(-2000, Math.min(2000, canvasOffset.y));
|
// Zoom with mouse wheel + ctrl/cmd towards viewport center
|
||||||
|
const zoomFactor = event.deltaY > 0 ? 0.9 : 1.1;
|
||||||
|
const newZoom = Math.max(0.1, Math.min(3, canvasZoom * zoomFactor));
|
||||||
|
|
||||||
|
// Get viewport center
|
||||||
|
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
||||||
|
const centerX = rect.width / 2;
|
||||||
|
const centerY = rect.height / 2;
|
||||||
|
|
||||||
|
// Convert viewport center to canvas coordinates before zoom
|
||||||
|
const canvasCenterX = (centerX - canvasOffset.x) / canvasZoom;
|
||||||
|
const canvasCenterY = (centerY - canvasOffset.y) / canvasZoom;
|
||||||
|
|
||||||
|
// Update zoom
|
||||||
|
canvasZoom = newZoom;
|
||||||
|
|
||||||
|
// Adjust offset to keep the viewport center fixed
|
||||||
|
canvasOffset.x = centerX - canvasCenterX * canvasZoom;
|
||||||
|
canvasOffset.y = centerY - canvasCenterY * canvasZoom;
|
||||||
|
} else {
|
||||||
|
// Pan with mouse wheel
|
||||||
|
const scrollSpeed = 1 / canvasZoom; // Adjust pan speed for zoom level
|
||||||
|
canvasOffset.x -= event.deltaX * scrollSpeed;
|
||||||
|
canvasOffset.y -= event.deltaY * scrollSpeed;
|
||||||
|
|
||||||
|
canvasOffset.x = Math.max(-2000, Math.min(2000, canvasOffset.x));
|
||||||
|
canvasOffset.y = Math.max(-2000, Math.min(2000, canvasOffset.y));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCanvasMouseDown(event: MouseEvent) {
|
function handleCanvasMouseDown(event: MouseEvent) {
|
||||||
@ -344,6 +372,96 @@
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTouchDistance(touches: TouchList) {
|
||||||
|
if (touches.length < 2) return 0;
|
||||||
|
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 getTouchCenter(touches: TouchList) {
|
||||||
|
if (touches.length < 2) return { x: 0, y: 0 };
|
||||||
|
const touch1 = touches[0];
|
||||||
|
const touch2 = touches[1];
|
||||||
|
return {
|
||||||
|
x: (touch1.clientX + touch2.clientX) / 2,
|
||||||
|
y: (touch1.clientY + touch2.clientY) / 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCanvasTouchStart(event: TouchEvent) {
|
||||||
|
isDragging = true;
|
||||||
|
if (event.touches.length >= 2) {
|
||||||
|
// Two or more touches - pinch zoom
|
||||||
|
isPinching = true;
|
||||||
|
lastPinchDistance = getTouchDistance(event.touches);
|
||||||
|
pinchCenter = getTouchCenter(event.touches);
|
||||||
|
|
||||||
|
// Convert pinch center to canvas coordinates
|
||||||
|
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
||||||
|
pinchCenter.x -= rect.left;
|
||||||
|
pinchCenter.y -= rect.top;
|
||||||
|
} else if (event.touches.length === 1) {
|
||||||
|
// Single touch - pan
|
||||||
|
const touch = event.touches[0];
|
||||||
|
dragStart.x = touch.clientX;
|
||||||
|
dragStart.y = touch.clientY;
|
||||||
|
dragStartOffset.x = canvasOffset.x;
|
||||||
|
dragStartOffset.y = canvasOffset.y;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCanvasTouchMove(event: TouchEvent) {
|
||||||
|
if (event.touches.length === 1 && isDragging && !isPinching) {
|
||||||
|
// Single touch pan
|
||||||
|
const touch = event.touches[0];
|
||||||
|
const deltaX = touch.clientX - dragStart.x;
|
||||||
|
const deltaY = touch.clientY - dragStart.y;
|
||||||
|
|
||||||
|
canvasOffset.x = Math.max(-2000, Math.min(2000, dragStartOffset.x + deltaX));
|
||||||
|
canvasOffset.y = Math.max(-2000, Math.min(2000, dragStartOffset.y + deltaY));
|
||||||
|
} else if (event.touches.length === 2 && isPinching) {
|
||||||
|
// Pinch zoom
|
||||||
|
const currentDistance = getTouchDistance(event.touches);
|
||||||
|
const zoomFactor = currentDistance / lastPinchDistance;
|
||||||
|
const newZoom = Math.max(0.1, Math.min(3, canvasZoom * zoomFactor));
|
||||||
|
|
||||||
|
// Convert pinch center to canvas coordinates before zoom
|
||||||
|
const canvasCenterX = (pinchCenter.x - canvasOffset.x) / canvasZoom;
|
||||||
|
const canvasCenterY = (pinchCenter.y - canvasOffset.y) / canvasZoom;
|
||||||
|
|
||||||
|
// Update zoom
|
||||||
|
canvasZoom = newZoom;
|
||||||
|
|
||||||
|
// Adjust offset to keep the pinch center fixed
|
||||||
|
canvasOffset.x = pinchCenter.x - canvasCenterX * canvasZoom;
|
||||||
|
canvasOffset.y = pinchCenter.y - canvasCenterY * canvasZoom;
|
||||||
|
|
||||||
|
lastPinchDistance = currentDistance;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCanvasTouchEnd(event: TouchEvent) {
|
||||||
|
if (event.touches.length === 0) {
|
||||||
|
isDragging = false;
|
||||||
|
isPinching = false;
|
||||||
|
} else if (event.touches.length === 1) {
|
||||||
|
isPinching = false;
|
||||||
|
// A pinch ended. Reset pan state for the remaining finger to prevent a jump.
|
||||||
|
const touch = event.touches[0];
|
||||||
|
dragStart.x = touch.clientX;
|
||||||
|
dragStart.y = touch.clientY;
|
||||||
|
dragStartOffset.x = canvasOffset.x;
|
||||||
|
dragStartOffset.y = canvasOffset.y;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
function addStickyNote() {
|
function addStickyNote() {
|
||||||
const randomColor = noteColors[Math.floor(Math.random() * noteColors.length)];
|
const randomColor = noteColors[Math.floor(Math.random() * noteColors.length)];
|
||||||
const newNote: StickyNote = {
|
const newNote: StickyNote = {
|
||||||
@ -387,6 +505,7 @@
|
|||||||
cards: cards.map(card => ({...card})),
|
cards: cards.map(card => ({...card})),
|
||||||
stickyNotes: stickyNotes.map(note => ({...note})),
|
stickyNotes: stickyNotes.map(note => ({...note})),
|
||||||
canvasOffset,
|
canvasOffset,
|
||||||
|
canvasZoom,
|
||||||
deckPosition,
|
deckPosition,
|
||||||
maxZIndex,
|
maxZIndex,
|
||||||
nextNoteId,
|
nextNoteId,
|
||||||
@ -431,6 +550,7 @@
|
|||||||
cards = canvasState.cards || [];
|
cards = canvasState.cards || [];
|
||||||
stickyNotes = canvasState.stickyNotes || [];
|
stickyNotes = canvasState.stickyNotes || [];
|
||||||
canvasOffset = canvasState.canvasOffset || { x: 0, y: 0 };
|
canvasOffset = canvasState.canvasOffset || { x: 0, y: 0 };
|
||||||
|
canvasZoom = canvasState.canvasZoom || 1;
|
||||||
deckPosition = canvasState.deckPosition || { x: 50, y: 300 };
|
deckPosition = canvasState.deckPosition || { x: 50, y: 300 };
|
||||||
maxZIndex = canvasState.maxZIndex || cards.length;
|
maxZIndex = canvasState.maxZIndex || cards.length;
|
||||||
nextNoteId = canvasState.nextNoteId || 1;
|
nextNoteId = canvasState.nextNoteId || 1;
|
||||||
@ -485,16 +605,20 @@
|
|||||||
onmousemove={handleCanvasMouseMove}
|
onmousemove={handleCanvasMouseMove}
|
||||||
onmouseup={handleCanvasMouseUp}
|
onmouseup={handleCanvasMouseUp}
|
||||||
oncontextmenu={handleCanvasContextMenu}
|
oncontextmenu={handleCanvasContextMenu}
|
||||||
|
ontouchstart={handleCanvasTouchStart}
|
||||||
|
ontouchmove={handleCanvasTouchMove}
|
||||||
|
ontouchend={handleCanvasTouchEnd}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="canvas-content"
|
class="canvas-content"
|
||||||
style="transform: translate({canvasOffset.x}px, {canvasOffset.y}px);"
|
style="transform: translate({canvasOffset.x}px, {canvasOffset.y}px) scale({canvasZoom});"
|
||||||
>
|
>
|
||||||
{#each cards as card (card.id)}
|
{#each cards as card (card.id)}
|
||||||
<Card
|
<Card
|
||||||
{...card}
|
{...card}
|
||||||
backImage={card.isRiskCategory ? 'risks/risikocat_back.png' : backImage}
|
backImage={card.isRiskCategory ? 'risks/risikocat_back.png' : backImage}
|
||||||
canvasOffset={canvasOffset}
|
canvasOffset={canvasOffset}
|
||||||
|
canvasZoom={canvasZoom}
|
||||||
bind:flipped={card.flipped}
|
bind:flipped={card.flipped}
|
||||||
bind:x={card.x}
|
bind:x={card.x}
|
||||||
bind:y={card.y}
|
bind:y={card.y}
|
||||||
@ -509,6 +633,7 @@
|
|||||||
<StickyNote
|
<StickyNote
|
||||||
{...note}
|
{...note}
|
||||||
canvasOffset={canvasOffset}
|
canvasOffset={canvasOffset}
|
||||||
|
canvasZoom={canvasZoom}
|
||||||
bind:text={note.text}
|
bind:text={note.text}
|
||||||
bind:x={note.x}
|
bind:x={note.x}
|
||||||
bind:y={note.y}
|
bind:y={note.y}
|
||||||
@ -597,18 +722,40 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
|
touch-action: none;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-area:active {
|
.game-area:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.controls {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
gap: 5px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group {
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls button {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.canvas-content {
|
.canvas-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 500vw;
|
width: 500vw;
|
||||||
height: 500vh;
|
height: 500vh;
|
||||||
min-width: 500vw;
|
min-width: 500vw;
|
||||||
min-height: 500vh;
|
min-height: 500vh;
|
||||||
|
transform-origin: 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.deck-area {
|
.deck-area {
|
||||||
|
Reference in New Issue
Block a user