Skip to content

Commit acc722a

Browse files
Merge pull request #113 from gridaco/staging
Readonly Full scale canvas support like Figma & Framer
2 parents 3f1890a + 6701c0b commit acc722a

123 files changed

Lines changed: 8796 additions & 1212 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
2+
import { useGesture } from "@use-gesture/react";
3+
import type {
4+
Handler,
5+
WebKitGestureEvent,
6+
SharedGestureState,
7+
} from "@use-gesture/react";
8+
9+
export type OnPanningHandler = Handler<"wheel", WheelEvent>;
10+
11+
export type OnZoomingHandler = Handler<
12+
"pinch",
13+
WheelEvent | PointerEvent | TouchEvent | WebKitGestureEvent
14+
>;
15+
16+
export type OnPointerMoveHandler = Handler<"move">;
17+
18+
export type OnPointerDownHandler = (
19+
e: { event: React.MouseEvent<EventTarget, MouseEvent> } & SharedGestureState
20+
) => void;
21+
22+
const ZOOM_WITH_SCROLL_SENSITIVITY = 0.001;
23+
24+
export function CanvasEventTarget({
25+
onPanning,
26+
onPanningStart,
27+
onPanningEnd,
28+
onZooming,
29+
onZoomingStart,
30+
onZoomingEnd,
31+
onPointerMove,
32+
onPointerMoveStart,
33+
onPointerMoveEnd,
34+
onPointerDown,
35+
children,
36+
}: {
37+
onPanning: OnPanningHandler;
38+
onPanningStart: OnPanningHandler;
39+
onPanningEnd: OnPanningHandler;
40+
onZooming: OnZoomingHandler;
41+
onZoomingStart: OnZoomingHandler;
42+
onZoomingEnd: OnZoomingHandler;
43+
onPointerMove: OnPointerMoveHandler;
44+
onPointerMoveStart: OnPointerMoveHandler;
45+
onPointerMoveEnd: OnPointerMoveHandler;
46+
onPointerDown: OnPointerDownHandler;
47+
children?: React.ReactNode;
48+
}) {
49+
const interactionEventTargetRef = useRef();
50+
51+
const [isSpacebarPressed, setIsSpacebarPressed] = useState(false);
52+
let platform: PlatformName = "other";
53+
useEffect(() => {
54+
platform = getCurrentPlatform(window.navigator);
55+
});
56+
57+
useEffect(() => {
58+
const kd = (e) => {
59+
// if spacebar is pressed, enable panning wirt dragging.
60+
if (e.code === "Space") {
61+
setIsSpacebarPressed(true);
62+
}
63+
};
64+
const ku = (e) => {
65+
if (e.code === "Space") {
66+
setIsSpacebarPressed(false);
67+
}
68+
};
69+
70+
document.addEventListener("keydown", kd);
71+
document.addEventListener("keyup", ku);
72+
73+
return () => {
74+
document.removeEventListener("keydown", kd);
75+
document.removeEventListener("keyup", ku);
76+
};
77+
}, []);
78+
79+
const transform_wheel_to_zoom = (s) => {
80+
return {
81+
...s,
82+
origin: [s.event.clientX, s.event.clientY],
83+
delta: [-s.delta[1] * ZOOM_WITH_SCROLL_SENSITIVITY, 0],
84+
};
85+
};
86+
87+
useGesture(
88+
{
89+
onPinch: onZooming,
90+
onPinchStart: onZoomingStart,
91+
onPinchEnd: onZoomingEnd,
92+
onWheel: (s) => {
93+
if (s.altKey) {
94+
// altkey prevents panning the canvas.
95+
return;
96+
}
97+
if (s.ctrlKey) {
98+
// crtl key is also enabled on onPinch - we don't have to explicitly add linux & windows support for ctrl + scroll.
99+
return;
100+
} else {
101+
// only for mac
102+
if (s.metaKey) {
103+
onZooming(transform_wheel_to_zoom(s));
104+
// TODO: on firefox, cmd + scroll resizes the window zoom level. this should be prevented.
105+
return;
106+
}
107+
}
108+
onPanning(s);
109+
s.event.stopPropagation();
110+
s.event.preventDefault();
111+
},
112+
onWheelStart: (s) => {
113+
onPanningStart(s);
114+
s.event.stopPropagation();
115+
s.event.preventDefault();
116+
},
117+
onWheelEnd: onPanningEnd,
118+
onMove: onPointerMove,
119+
onDragStart: (s) => {
120+
if (isSpacebarPressed) {
121+
onPanningStart(s as any);
122+
}
123+
},
124+
onDrag: (s) => {
125+
if (isSpacebarPressed) {
126+
onPanning({
127+
...s,
128+
delta: [-s.delta[0], -s.delta[1]],
129+
} as any);
130+
}
131+
},
132+
onDragEnd: (s) => {
133+
if (isSpacebarPressed) {
134+
onPanningEnd(s as any);
135+
}
136+
},
137+
onMouseDown: onPointerDown,
138+
onMoveStart: onPointerMoveStart,
139+
onMoveEnd: onPointerMoveEnd,
140+
},
141+
{
142+
target: interactionEventTargetRef,
143+
eventOptions: {
144+
// passive to false to raise `e.preventDefault()` and `e.stopPropagation()`. - this will prevent the browser from scrolling the page, navigating with swipe gesture (safari, firefox).
145+
passive: false,
146+
},
147+
pinch: {},
148+
}
149+
);
150+
151+
return (
152+
<div
153+
style={{
154+
position: "absolute",
155+
inset: 0,
156+
background: "transparent",
157+
overflow: "hidden",
158+
touchAction: "none",
159+
cursor: isSpacebarPressed ? "grab" : "default",
160+
}}
161+
id="gesture-event-listener"
162+
ref={interactionEventTargetRef}
163+
>
164+
{children}
165+
</div>
166+
);
167+
}
168+
169+
type PlatformName = "mac" | "win" | "linux" | "other";
170+
171+
const getCurrentPlatform = (navigator?: { platform: string }): PlatformName =>
172+
typeof navigator === "undefined"
173+
? "other"
174+
: /Mac/.test(navigator.platform)
175+
? "mac"
176+
: /Win/.test(navigator.platform)
177+
? "win"
178+
: /Linux|X11/.test(navigator.platform)
179+
? "linux"
180+
: "other";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./canvas-event-target";

0 commit comments

Comments
 (0)