Skip to content

Commit 95522a2

Browse files
support scaling canvas by event origin point (thankyou copilot!)
1 parent c9fb6fa commit 95522a2

3 files changed

Lines changed: 71 additions & 127 deletions

File tree

editor-packages/editor-canvas/canvas/canvas.tsx

Lines changed: 71 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -59,67 +59,6 @@ interface HovringNode {
5959
reason: "frame-title" | "raycast" | "external";
6060
}
6161

62-
function auto_initial_transform(
63-
viewbound: Box,
64-
nodes: ReflectSceneNode[]
65-
): CanvasTransform {
66-
const _default = {
67-
scale: INITIAL_SCALE,
68-
xy: INITIAL_XY,
69-
};
70-
71-
if (!nodes || viewbound_not_measured(viewbound)) {
72-
return _default;
73-
}
74-
75-
const fit_single_node = (n: ReflectSceneNode) => {
76-
return centerOf(viewbound, n);
77-
};
78-
79-
if (nodes.length === 0) {
80-
return _default;
81-
} else if (nodes.length === 1) {
82-
// return center the node
83-
const c = fit_single_node(nodes[0]);
84-
return {
85-
xy: c.translate,
86-
scale: c.scale,
87-
};
88-
} else if (nodes.length < 20) {
89-
// fit bounds
90-
const c = centerOf(viewbound, ...nodes);
91-
return {
92-
xy: c.translate,
93-
scale: c.scale,
94-
};
95-
} else {
96-
// if more than 20 nodes, just center the first one. why? -> loading all frames at once will slow down the canvas, and in most cases, we don't have to show the whole content of the canvas.
97-
// fit first item
98-
const c = fit_single_node(nodes[0]);
99-
return {
100-
xy: c.translate,
101-
scale: c.scale,
102-
};
103-
}
104-
105-
return _default;
106-
}
107-
108-
/**
109-
* when viewbound is not measured, it means the canvas is not ready to render. and the value will be `[0,0,0,0]` (from react-use-measure)
110-
* @param viewbound visible canvas area bound
111-
* @returns
112-
*/
113-
const viewbound_not_measured = (viewbound: Box) => {
114-
return (
115-
!viewbound ||
116-
(viewbound[0] === 0 &&
117-
viewbound[1] === 0 &&
118-
viewbound[2] === 0 &&
119-
viewbound[3] === 0)
120-
);
121-
};
122-
12362
export function Canvas({
12463
viewbound,
12564
renderItem,
@@ -251,17 +190,24 @@ export function Canvas({
251190

252191
const onZooming: OnZoomingHandler = (state) => {
253192
const zoomdelta = state.delta[0];
254-
const zoompoint: XY = [
193+
// the origin point of the zooming point in x, y
194+
const [ox, oy]: XY = [
255195
// @ts-ignore
256196
state.event.clientX ?? 0,
257197
// @ts-ignore
258198
state.event.clientY ?? 0,
259199
];
260200

261201
const newzoom = Math.max(zoom + zoomdelta, MIN_ZOOM);
202+
203+
// calculate the offset that should be applied with scale with css transform.
204+
const [newx, newy] = [
205+
ox - (ox - offset[0]) * (newzoom / zoom),
206+
oy - (oy - offset[1]) * (newzoom / zoom),
207+
];
208+
262209
setZoom(newzoom);
263-
// TODO: transform offset
264-
// setOffset([offset[0], offset[1]]);
210+
setOffset([newx, newy]);
265211
};
266212

267213
const is_canvas_transforming = isPanning || isZooming;
@@ -388,3 +334,64 @@ function DisableBackdropFilter({ children }: { children: React.ReactNode }) {
388334
</div>
389335
);
390336
}
337+
338+
function auto_initial_transform(
339+
viewbound: Box,
340+
nodes: ReflectSceneNode[]
341+
): CanvasTransform {
342+
const _default = {
343+
scale: INITIAL_SCALE,
344+
xy: INITIAL_XY,
345+
};
346+
347+
if (!nodes || viewbound_not_measured(viewbound)) {
348+
return _default;
349+
}
350+
351+
const fit_single_node = (n: ReflectSceneNode) => {
352+
return centerOf(viewbound, n);
353+
};
354+
355+
if (nodes.length === 0) {
356+
return _default;
357+
} else if (nodes.length === 1) {
358+
// return center the node
359+
const c = fit_single_node(nodes[0]);
360+
return {
361+
xy: c.translate,
362+
scale: c.scale,
363+
};
364+
} else if (nodes.length < 20) {
365+
// fit bounds
366+
const c = centerOf(viewbound, ...nodes);
367+
return {
368+
xy: c.translate,
369+
scale: c.scale,
370+
};
371+
} else {
372+
// if more than 20 nodes, just center the first one. why? -> loading all frames at once will slow down the canvas, and in most cases, we don't have to show the whole content of the canvas.
373+
// fit first item
374+
const c = fit_single_node(nodes[0]);
375+
return {
376+
xy: c.translate,
377+
scale: c.scale,
378+
};
379+
}
380+
381+
return _default;
382+
}
383+
384+
/**
385+
* when viewbound is not measured, it means the canvas is not ready to render. and the value will be `[0,0,0,0]` (from react-use-measure)
386+
* @param viewbound visible canvas area bound
387+
* @returns
388+
*/
389+
const viewbound_not_measured = (viewbound: Box) => {
390+
return (
391+
!viewbound ||
392+
(viewbound[0] === 0 &&
393+
viewbound[1] === 0 &&
394+
viewbound[2] === 0 &&
395+
viewbound[3] === 0)
396+
);
397+
};

editor-packages/editor-canvas/math/gesture-transform.ts

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
export * from "./gesture-transform";
21
export * from "./hovering-target";
32
export * from "./center-of";

0 commit comments

Comments
 (0)