fix zomming

This commit is contained in:
Haapy
2026-05-14 22:12:32 +00:00
parent a858a3415d
commit eaeb4c2d92
8 changed files with 59 additions and 47 deletions

1
dist/assets/index-BKho0BkD.css vendored Normal file
View File

@@ -0,0 +1 @@
.canvas-container{position:fixed;top:0;right:0;bottom:0;left:0;overflow:hidden;cursor:default;background:var(--bg);touch-action:none;overscroll-behavior:contain}.canvas-container.pan-mode{cursor:grab}.canvas-container.panning{cursor:grabbing}.canvas-grid{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;background-image:radial-gradient(circle,var(--bg-grid) 1px,transparent 1px);background-repeat:repeat}.canvas-hud{position:fixed;bottom:12px;left:12px;display:flex;gap:12px;padding:6px 10px;background:#00000080;border-radius:6px;font-size:11px;font-family:ui-monospace,Menlo,monospace;color:var(--text);pointer-events:none;z-index:1000}.card{position:absolute;background:var(--card-bg);border:calc(1px * var(--scale, 1)) solid var(--card-border);border-radius:calc(8px * var(--scale, 1));box-shadow:0 calc(4px * var(--scale, 1)) calc(16px * var(--scale, 1)) #0000004d;overflow:hidden;display:flex;flex-direction:column}.card-header{padding:.46em .77em;background:#0003;border-bottom:calc(1px * var(--scale, 1)) solid var(--card-border);font-size:.85em;cursor:move;flex-shrink:0}.card-body{flex:1;overflow:auto}.note-card textarea{width:100%;height:100%;background:transparent;border:none;outline:none;resize:none;color:var(--text);font-family:inherit;font-size:1em;padding:.77em}:root{--bg: #1a1a1f;--bg-grid: #25252c;--card-bg: #2a2a32;--card-border: #3a3a45;--text: #e8e8ec;--accent: #6a8cff;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;color-scheme:dark}*{box-sizing:border-box;margin:0;padding:0}html,body,#root{width:100%;height:100%;overflow:hidden;background:var(--bg);color:var(--text);-webkit-user-select:none;user-select:none}

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.canvas-container{position:fixed;top:0;right:0;bottom:0;left:0;overflow:hidden;cursor:default;background:var(--bg);touch-action:none;overscroll-behavior:contain}.canvas-container.pan-mode{cursor:grab}.canvas-container.panning{cursor:grabbing}.canvas-grid{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;background-image:radial-gradient(circle,var(--bg-grid) 1px,transparent 1px);background-repeat:repeat}.canvas-world{position:absolute;top:0;left:0;width:0;height:0;transform-origin:0 0}.canvas-hud{position:fixed;bottom:12px;left:12px;display:flex;gap:12px;padding:6px 10px;background:#00000080;border-radius:6px;font-size:11px;font-family:ui-monospace,Menlo,monospace;color:var(--text);pointer-events:none}.card{position:absolute;background:var(--card-bg);border:1px solid var(--card-border);border-radius:8px;box-shadow:0 4px 16px #0000004d;overflow:hidden;display:flex;flex-direction:column}.card-header{padding:6px 10px;background:#0003;border-bottom:1px solid var(--card-border);font-size:11px;cursor:move;flex-shrink:0}.card-body{flex:1;overflow:auto}.note-card textarea{width:100%;height:100%;background:transparent;border:none;outline:none;resize:none;color:var(--text);font-family:inherit;font-size:13px;padding:10px}:root{--bg: #1a1a1f;--bg-grid: #25252c;--card-bg: #2a2a32;--card-border: #3a3a45;--text: #e8e8ec;--accent: #6a8cff;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;color-scheme:dark}*{box-sizing:border-box;margin:0;padding:0}html,body,#root{width:100%;height:100%;overflow:hidden;background:var(--bg);color:var(--text);-webkit-user-select:none;user-select:none}

4
dist/index.html vendored
View File

@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Infinite</title>
<script type="module" crossorigin src="/assets/index-CK2X1Wwi.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DDld2teB.css">
<script type="module" crossorigin src="/assets/index-CdDqLPP_.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BKho0BkD.css">
</head>
<body>
<div id="root"></div>

View File

@@ -1,5 +1,6 @@
import { useEffect, useRef, useState, useCallback } from "react";
import type { Card, Viewport } from "./types";
import { ViewportContext } from "./viewport";
import { NoteCardView } from "./cards/NoteCardView";
import "./canvas.css";
@@ -119,17 +120,14 @@ export function Canvas({ initialCards }: CanvasProps) {
backgroundSize: `${40 * vp.scale}px ${40 * vp.scale}px`,
}}
/>
<div
className="canvas-world"
style={{ transform: `translate(${vp.x}px, ${vp.y}px) scale(${vp.scale})` }}
>
<ViewportContext.Provider value={vp}>
{cards.map((c) => {
if (c.kind === "note") {
return <NoteCardView key={c.id} card={c} onUpdate={(p) => updateCard(c.id, p)} />;
}
return null;
})}
</div>
</ViewportContext.Provider>
<div className="canvas-hud">
<span>x {vp.x.toFixed(0)}</span>
<span>y {vp.y.toFixed(0)}</span>

View File

@@ -25,15 +25,6 @@
background-repeat: repeat;
}
.canvas-world {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
transform-origin: 0 0;
}
.canvas-hud {
position: fixed;
bottom: 12px;
@@ -47,24 +38,25 @@
font-family: ui-monospace, Menlo, monospace;
color: var(--text);
pointer-events: none;
z-index: 1000;
}
.card {
position: absolute;
background: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
border: calc(1px * var(--scale, 1)) solid var(--card-border);
border-radius: calc(8px * var(--scale, 1));
box-shadow: 0 calc(4px * var(--scale, 1)) calc(16px * var(--scale, 1)) rgba(0, 0, 0, 0.3);
overflow: hidden;
display: flex;
flex-direction: column;
}
.card-header {
padding: 6px 10px;
padding: 0.46em 0.77em;
background: rgba(0, 0, 0, 0.2);
border-bottom: 1px solid var(--card-border);
font-size: 11px;
border-bottom: calc(1px * var(--scale, 1)) solid var(--card-border);
font-size: 0.85em;
cursor: move;
flex-shrink: 0;
}
@@ -83,6 +75,6 @@
resize: none;
color: var(--text);
font-family: inherit;
font-size: 13px;
padding: 10px;
font-size: 1em;
padding: 0.77em;
}

View File

@@ -1,12 +1,16 @@
import { useRef } from "react";
import type { NoteCard } from "../types";
import { useViewport } from "../viewport";
interface Props {
card: NoteCard;
onUpdate: (patch: Partial<NoteCard>) => void;
}
const BODY_FONT_BASE = 13;
export function NoteCardView({ card, onUpdate }: Props) {
const vp = useViewport();
const dragState = useRef<{ startX: number; startY: number; cardX: number; cardY: number } | null>(
null,
);
@@ -19,25 +23,43 @@ export function NoteCardView({ card, onUpdate }: Props) {
};
const onHeaderPointerMove = (e: React.PointerEvent) => {
if (!dragState.current) return;
const worldEl = (e.currentTarget as HTMLElement).closest(".canvas-world") as HTMLElement;
const scale = worldEl ? parseTransformScale(worldEl.style.transform) : 1;
const dx = (e.clientX - dragState.current.startX) / scale;
const dy = (e.clientY - dragState.current.startY) / scale;
onUpdate({ x: dragState.current.cardX + dx, y: dragState.current.cardY + dy });
const ds = dragState.current;
if (!ds) return;
const dx = (e.clientX - ds.startX) / vp.scale;
const dy = (e.clientY - ds.startY) / vp.scale;
onUpdate({ x: ds.cardX + dx, y: ds.cardY + dy });
};
const onHeaderPointerUp = (e: React.PointerEvent) => {
if (dragState.current) {
try {
(e.target as Element).releasePointerCapture(e.pointerId);
} catch {
// already released
}
dragState.current = null;
}
};
const screenLeft = vp.x + card.x * vp.scale;
const screenTop = vp.y + card.y * vp.scale;
const screenW = card.width * vp.scale;
const screenH = card.height * vp.scale;
return (
<div
className="card note-card"
style={{ left: card.x, top: card.y, width: card.width, height: card.height, zIndex: card.z }}
style={
{
left: screenLeft,
top: screenTop,
width: screenW,
height: screenH,
zIndex: card.z,
fontSize: BODY_FONT_BASE * vp.scale,
"--scale": vp.scale,
} as React.CSSProperties
}
>
<div
className="card-header"
@@ -58,8 +80,3 @@ export function NoteCardView({ card, onUpdate }: Props) {
</div>
);
}
function parseTransformScale(transform: string): number {
const m = transform.match(/scale\(([^)]+)\)/);
return m ? parseFloat(m[1]) : 1;
}

5
src/canvas/viewport.ts Normal file
View File

@@ -0,0 +1,5 @@
import { createContext, useContext } from "react";
import type { Viewport } from "./types";
export const ViewportContext = createContext<Viewport>({ x: 0, y: 0, scale: 1 });
export const useViewport = () => useContext(ViewportContext);