Skip to content

Commit 4d216ce

Browse files
Merge pull request #147 from gridaco/staging
Apr #2
2 parents 00a47f2 + 885f65f commit 4d216ce

File tree

107 files changed

+2286
-575
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+2286
-575
lines changed
+29-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,29 @@
1-
# A Interactive canvas for runtime frames.
1+
# A Html5 backend Interactive canvas for runtime frames.
2+
3+
## The system
4+
5+
- Canvas
6+
- Hud
7+
- Event
8+
- Math
9+
- Iframe
10+
- Node
11+
- Host
12+
13+
## General architecture
14+
15+
- `ScaffoldCanvas` - A single component canvas that holds both renderer and eventsystem
16+
- `RenderOnlyCanvas + EventSystem` - A Customizable system for complex and heavy rendering. (use saperate render host with iframe)
17+
18+
## Events
19+
20+
gesture events
21+
22+
- move (pan)
23+
- zoom (pinch)
24+
25+
- create node
26+
- remove node
27+
- move node
28+
- resize node
29+
- rename node

editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx

+12-1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ export function CanvasEventTarget({
9696
const [first_wheel_event, set_first_wheel_event] =
9797
useState<FullGestureState<"wheel">>();
9898

99+
// this is a hack to prevent from onDragStart being called even when no movement is detected.
100+
const [drag_start_emitted, set_drag_start_emitted] = useState(false);
101+
99102
useGesture(
100103
{
101104
onPinch: onZooming,
@@ -150,7 +153,10 @@ export function CanvasEventTarget({
150153
return;
151154
}
152155

153-
onDragStart(s);
156+
if (s.delta[0] || s.delta[1]) {
157+
onDragStart(s);
158+
set_drag_start_emitted(true);
159+
}
154160
},
155161
onDrag: (s) => {
156162
if (isSpacebarPressed) {
@@ -161,6 +167,10 @@ export function CanvasEventTarget({
161167
return;
162168
}
163169

170+
if ((s.delta[0] || s.delta[1]) && !drag_start_emitted) {
171+
set_drag_start_emitted(true);
172+
onDragStart(s);
173+
}
164174
onDrag(s);
165175
},
166176
onDragEnd: (s) => {
@@ -169,6 +179,7 @@ export function CanvasEventTarget({
169179
return;
170180
}
171181

182+
set_drag_start_emitted(false);
172183
onDragEnd(s);
173184
},
174185
onMouseDown: onPointerDown,

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

+73-17
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ import {
99
OnPointerDownHandler,
1010
OnDragHandler,
1111
} from "../canvas-event-target";
12-
import { get_hovering_target, centerOf } from "../math";
12+
import {
13+
target_of_point,
14+
centerOf,
15+
edge_scrolling,
16+
target_of_area,
17+
} from "../math";
1318
import { utils } from "@design-sdk/core";
1419
import { LazyFrame } from "@code-editor/canvas/lazy-frame";
15-
import { HudCustomRenderers, HudSurface } from "./hud-surface";
20+
import { HudCustomRenderers, HudSurface } from "../hud";
1621
import type { Box, XY, CanvasTransform, XYWH } from "../types";
1722
import type { FrameOptimizationFactors } from "../frame";
1823
const designq = utils.query;
@@ -89,7 +94,7 @@ export function Canvas({
8994
...props
9095
}: {
9196
viewbound: Box;
92-
onSelectNode?: (node?: ReflectSceneNode) => void;
97+
onSelectNode?: (...node: ReflectSceneNode[]) => void;
9398
onClearSelection?: () => void;
9499
} & CanvasCustomRenderers &
95100
CanvasState & {
@@ -131,6 +136,7 @@ export function Canvas({
131136
? [offset[0] / zoom, offset[1] / zoom]
132137
: [0, 0];
133138
const [isPanning, setIsPanning] = useState(false);
139+
const [isDraggomg, setIsDragging] = useState(false);
134140
const [marquee, setMarquee] = useState<XYWH>(null);
135141

136142
const cvtransform: CanvasTransform = {
@@ -151,17 +157,46 @@ export function Canvas({
151157
setHoveringLayer(wshighlight);
152158
}, [highlightedLayer]);
153159

160+
// area selection hook
161+
useEffect(() => {
162+
if (marquee) {
163+
const area: XYWH = [
164+
marquee[0] / zoom,
165+
marquee[1] / zoom,
166+
marquee[2] / zoom,
167+
marquee[3] / zoom,
168+
];
169+
170+
const selections = target_of_area({
171+
area,
172+
tree: nodes,
173+
contain: false,
174+
});
175+
176+
// https://stackoverflow.com/a/19746771
177+
const same =
178+
selectedNodes.length === selections?.length &&
179+
selectedNodes.every((value, index) => value === selections[index].id);
180+
181+
if (!same) {
182+
onSelectNode(...selections);
183+
}
184+
}
185+
//
186+
}, [marquee]);
187+
154188
const onPointerMove: OnPointerMoveHandler = (state) => {
155-
if (isPanning || isZooming) {
189+
if (isPanning || isZooming || isDraggomg) {
156190
// don't perform hover calculation while transforming.
157191
return;
158192
}
159-
const hovering = get_hovering_target({
193+
const hovering = target_of_point({
160194
point: state.xy,
161195
tree: nodes,
162196
zoom: zoom,
163197
offset: nonscaled_offset,
164198
margin: LAYER_HOVER_HIT_MARGIN,
199+
reverse: true,
165200
});
166201

167202
if (!hovering) {
@@ -223,27 +258,48 @@ export function Canvas({
223258
setOffset([newx, newy]);
224259
};
225260

261+
const onDragStart: OnDragHandler = (s) => {
262+
onClearSelection();
263+
setIsDragging(true);
264+
setHoveringLayer(null);
265+
266+
// set the marquee start point
267+
const [x, y] = s.initial;
268+
const [ox, oy] = offset;
269+
const [x1, y1] = [x - ox, y - oy];
270+
setMarquee([x1, y1, 0, 0]);
271+
};
272+
226273
const onDrag: OnDragHandler = (s) => {
227-
const [x1, y1] = s.initial;
228-
const [x2, y2] = [
274+
const [ox, oy] = offset;
275+
const [x, y] = [
229276
// @ts-ignore
230277
s.event.clientX,
231278
// @ts-ignore
232279
s.event.clientY,
233280
];
234281

235-
const [ox, oy] = offset;
236-
const [x, y, w, h] = [
237-
x1 - ox,
238-
y1 - oy,
239-
x2 - x1, // w
240-
y2 - y1, // h
241-
];
242-
setMarquee([x, y, w, h]);
282+
const [x1, y1] = [x - ox, y - oy];
283+
284+
if (marquee) {
285+
const [w, h] = [
286+
x1 - marquee[0], // w
287+
y1 - marquee[1], // h
288+
];
289+
setMarquee([marquee[0], marquee[1], w, h]);
290+
}
291+
292+
// edge scrolling
293+
const [cx, cy] = [x, y];
294+
const [dx, dy] = edge_scrolling(cx, cy, viewbound);
295+
if (dx || dy) {
296+
setOffset([ox + dx, oy + dy]);
297+
}
243298
};
244299

245300
const onDragEnd: OnDragHandler = (s) => {
246301
setMarquee(null);
302+
setIsDragging(false);
247303
};
248304

249305
const is_canvas_transforming = isPanning || isZooming;
@@ -299,8 +355,8 @@ export function Canvas({
299355
onPointerMoveStart={() => {}}
300356
onPointerMoveEnd={() => {}}
301357
onPointerDown={onPointerDown}
358+
onDragStart={onDragStart}
302359
onDrag={onDrag}
303-
onDragStart={() => {}} // TODO:
304360
onDragEnd={onDragEnd}
305361
>
306362
<HudSurface
@@ -363,7 +419,7 @@ function CanvasTransformRoot({
363419
width: 0,
364420
height: 0,
365421
willChange: "transform",
366-
transform: `scale(${scale}) translateX(${xy[0]}px) translateY(${xy[1]}px)`,
422+
transform: `scale(${scale}) translate3d(${xy[0]}px, ${xy[1]}px, 0)`,
367423
isolation: "isolate",
368424
}}
369425
>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
- move back
2+
- move front
3+
4+
- copy
5+
- paste
6+
- move under parent
7+
- lock / unlock
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Area selection (Marquee selection)
2+
3+
> This feature yet does not consider vector networks. (boolean operation)
4+
5+
Keymode
6+
7+
- default
8+
- cmd
9+
10+
Parameters
11+
12+
- raycast mode:
13+
- hit - if the target has an intersection with the area.
14+
- contain - if the target is contained in the area.
15+
- type
16+
- frame
17+
- group
18+
- frame
19+
- element (others)
20+
- is root - this only effects to the frame node.
21+
22+
Final output for painting
23+
24+
- selections (each selection will get a highlight)
25+
- bounding box (abstract group)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Canvas Drag Scroll (Scroll / Pan while dragging)
2+
3+
scroll (translate) a canvas while dragging (marquee, move, resize element). if hit the edge of the canvas, the canvas will scroll.
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Hud system
2+
3+
node
4+
5+
- hover outline
6+
- resize knob
7+
- rotate knob
8+
- border radius knob
9+
- frame title
10+
- draft editor (text editing)
11+
- multiplayer cursor
12+
- resizing size indicator
13+
- rotating rotation indicator
14+
15+
ruler & guide
16+
17+
- ruler
18+
- guide (user defined guide line)
19+
- highlight guide (hovered, selected guide line)
20+
- snap line (snapping guide line)
21+
- layout grids (grid-template-columns)
22+
- spacing guide (size between 2 nodes)
23+
24+
layout
25+
26+
- layout placement guide (preview the place of an moving item after placement inside a certain lyout - row / col)
27+
28+
- interaction knob
29+
- interaction line
30+
31+
popover
32+
33+
- popovers
34+
35+
feedback (comments)
36+
37+
- pin
38+
- create pin
39+
40+
vector
41+
42+
- TODO:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Html5 Backend Canvas docs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from "react";
2+
import type { XY } from "../types";
3+
4+
export function Arrow({
5+
b,
6+
color,
7+
size,
8+
width,
9+
direction,
10+
}: {
11+
b: XY;
12+
color: React.CSSProperties["color"];
13+
size: number;
14+
width: number;
15+
direction: "n" | "s" | "e" | "w";
16+
}) {
17+
return (
18+
<path
19+
stroke={color}
20+
strokeWidth={width}
21+
d={make_arrow_svg_path_data(b, direction, {
22+
width: size,
23+
height: size / 2,
24+
})}
25+
/>
26+
);
27+
}
28+
29+
/**
30+
*
31+
* the result will have 3 modifiers,
32+
* if the arrow is facing right, the modifiers will be:
33+
* - M - starting point [edge_x - height, edge_y + width / 2]
34+
* - L - edge [edge_x, edge_y]
35+
* - L - ending point [edge_x - height, edge_y - width / 2]
36+
*
37+
* @param edge the edge of a arrow (triangle)
38+
* @param width
39+
*/
40+
function make_arrow_svg_path_data(
41+
edge: XY,
42+
direction: "n" | "s" | "e" | "w",
43+
{ width, height }: { width: number; height: number }
44+
) {
45+
const [x, y] = edge;
46+
const w = width / 2;
47+
switch (direction) {
48+
case "e": {
49+
return `M${x - height},${y + w} L${x},${y} L${x - height},${y - w}`;
50+
}
51+
case "w": {
52+
return `M${x + height},${y + w} L${x},${y} L${x + height},${y - w}`;
53+
}
54+
case "n": {
55+
return `M${x - w},${y + height} L${x},${y} L${x + w},${y + height}`;
56+
}
57+
case "s": {
58+
return `M${x - w},${y - height} L${x},${y} L${x + w},${y - height}`;
59+
}
60+
default: {
61+
throw new Error(`invalid direction: ${direction}`);
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)