Skip to content

Commit 655a298

Browse files
boost fullscale canvas performance
1 parent 17dd398 commit 655a298

8 files changed

Lines changed: 310 additions & 133 deletions

File tree

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

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { utils } from "@design-sdk/core";
1313
import { LazyFrame } from "@code-editor/canvas/lazy-frame";
1414
import { HudCustomRenderers, HudSurface } from "./hud-surface";
1515
import type { XY, XYWH } from "../types";
16-
16+
import type { FrameOptimizationFactors } from "../frame";
1717
const designq = utils.query;
1818

1919
const INITIAL_SCALE = 0.5;
@@ -27,6 +27,7 @@ type CanvasTransform = {
2727
};
2828

2929
interface CanvasState {
30+
filekey: string;
3031
nodes: ReflectSceneNode[];
3132
highlightedLayer?: string;
3233
selectedNodes: string[];
@@ -35,7 +36,11 @@ interface CanvasState {
3536
}
3637

3738
type CanvasCustomRenderers = HudCustomRenderers & {
38-
renderItem: (node: ReflectSceneNode) => React.ReactNode;
39+
renderItem: (
40+
p: {
41+
node: ReflectSceneNode & { filekey: string };
42+
} & FrameOptimizationFactors
43+
) => React.ReactNode;
3944
};
4045

4146
interface CanvsPreferences {
@@ -55,6 +60,7 @@ export function Canvas({
5560
renderItem,
5661
onSelectNode,
5762
onClearSelection,
63+
filekey,
5864
nodes,
5965
initialTransform = {
6066
xy: INITIAL_XY,
@@ -216,15 +222,15 @@ export function Canvas({
216222
<CanvasTransformRoot scale={zoom} xy={nonscaled_offset}>
217223
<DisableBackdropFilter>
218224
{nodes?.map((node) => {
225+
node["filekey"] = filekey;
219226
return (
220-
<LazyFrame
221-
key={node.id}
222-
xy={[node.x, node.y]}
223-
size={node}
224-
zoom={zoom}
225-
placeholder={<EmptyFrame />}
226-
>
227-
{renderItem(node)}
227+
<LazyFrame key={node.id} xy={[node.x, node.y]} size={node}>
228+
{renderItem({
229+
node: node as ReflectSceneNode & { filekey: string },
230+
zoom, // ? use scaled_zoom ?
231+
inViewport: true, // TODO:
232+
focused: selectedNodes.includes(node.id),
233+
})}
228234
</LazyFrame>
229235
);
230236
})}
@@ -277,11 +283,3 @@ function DisableBackdropFilter({ children }: { children: React.ReactNode }) {
277283
</div>
278284
);
279285
}
280-
281-
const EmptyFrame = styled.div`
282-
width: 100%;
283-
height: 100%;
284-
background-color: #e09292;
285-
border-radius: 4px;
286-
box-shadow: 0px 0px 48px #00000020;
287-
`;

editor-packages/editor-canvas/canvas/hud-surface.tsx

Lines changed: 62 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from "react";
12
import { HoverOutlineHighlight, ReadonlySelectHightlight } from "../overlay";
23
import { FrameTitle, FrameTitleProps } from "../frame-title";
34
import type { XY, XYWH } from "../types";
@@ -62,65 +63,69 @@ export function HudSurface({
6263
}}
6364
id="hud-surface"
6465
>
65-
{labelDisplayNodes &&
66-
labelDisplayNodes.map((node) => {
67-
const absxy: XY = [node.absoluteX * zoom, node.absoluteY * zoom];
68-
return renderFrameTitle({
69-
id: node.id,
70-
name: node.name,
71-
xy: absxy,
72-
wh: [node.width, node.height],
73-
zoom: zoom,
74-
selected: selectedNodes.some(
75-
(selectedNode) => selectedNode.id === node.id
76-
),
77-
onSelect: () => onSelectNode(node.id),
78-
onHoverChange: (hv) => {
79-
if (hv) {
80-
onHoverNode(node.id);
66+
{!hide && (
67+
<>
68+
{labelDisplayNodes &&
69+
labelDisplayNodes.map((node) => {
70+
const absxy: XY = [node.absoluteX * zoom, node.absoluteY * zoom];
71+
return renderFrameTitle({
72+
id: node.id,
73+
name: node.name,
74+
xy: absxy,
75+
wh: [node.width, node.height],
76+
zoom: zoom,
77+
selected: selectedNodes.some(
78+
(selectedNode) => selectedNode.id === node.id
79+
),
80+
onSelect: () => onSelectNode(node.id),
81+
onHoverChange: (hv) => {
82+
if (hv) {
83+
onHoverNode(node.id);
84+
} else {
85+
onHoverNode(null);
86+
}
87+
},
88+
highlight: !![...highlights, ...selectedNodes].find(
89+
(n) => n.id === node.id
90+
),
91+
});
92+
})}
93+
{highlights &&
94+
highlights.map((h) => {
95+
return (
96+
<HoverOutlineHighlight
97+
key={h.id}
98+
type="xywhr"
99+
xywh={h.xywh}
100+
zoom={zoom}
101+
width={2}
102+
/>
103+
);
104+
})}
105+
{selectedNodes &&
106+
selectedNodes.map((s) => {
107+
const xywh: [number, number, number, number] = [
108+
s.absoluteX,
109+
s.absoluteY,
110+
s.width,
111+
s.height,
112+
];
113+
if (readonly) {
114+
return (
115+
<ReadonlySelectHightlight
116+
key={s.id}
117+
type="xywhr"
118+
xywh={xywh}
119+
zoom={zoom}
120+
width={1}
121+
/>
122+
);
81123
} else {
82-
onHoverNode(null);
124+
// TODO: support non readonly canvas
83125
}
84-
},
85-
highlight: !![...highlights, ...selectedNodes].find(
86-
(n) => n.id === node.id
87-
),
88-
});
89-
})}
90-
{highlights &&
91-
highlights.map((h) => {
92-
return (
93-
<HoverOutlineHighlight
94-
key={h.id}
95-
type="xywhr"
96-
xywh={h.xywh}
97-
zoom={zoom}
98-
width={2}
99-
/>
100-
);
101-
})}
102-
{selectedNodes &&
103-
selectedNodes.map((s) => {
104-
const xywh: [number, number, number, number] = [
105-
s.absoluteX,
106-
s.absoluteY,
107-
s.width,
108-
s.height,
109-
];
110-
if (readonly) {
111-
return (
112-
<ReadonlySelectHightlight
113-
key={s.id}
114-
type="xywhr"
115-
xywh={xywh}
116-
zoom={zoom}
117-
width={1}
118-
/>
119-
);
120-
} else {
121-
// TODO: support non readonly canvas
122-
}
123-
})}
126+
})}
127+
</>
128+
)}
124129
</div>
125130
);
126131
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export * from "./frame";
2+
3+
/**
4+
* factors ussed when optimizing root level frames in canvas. when displaying in full-scale
5+
* e.g. when zoom < 0.1, then we rather show an static image then showing it as a vanilla html iframe.
6+
*/
7+
export interface FrameOptimizationFactors {
8+
/**
9+
* zoom of the canvas
10+
*/
11+
zoom: number;
12+
/**
13+
* whether the frame is in user visible area
14+
*/
15+
inViewport: boolean;
16+
/**
17+
* whether the frame is selected / focused by user
18+
*/
19+
focused: boolean;
20+
}

editor-packages/editor-canvas/lazy-frame/lazy-frame.tsx

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@ import { useInViewport } from "react-in-viewport";
33

44
export function LazyFrame({
55
children,
6-
placeholder,
76
xy,
87
size,
9-
zoom,
108
}: { children: React.ReactNode } & {
11-
placeholder: React.ReactNode;
129
xy: [number, number];
1310
size: { width: number; height: number };
14-
zoom: number;
1511
}) {
1612
const visibilityRef = useRef();
1713
const { inViewport, enterCount } = useInViewport(visibilityRef);
@@ -34,25 +30,17 @@ export function LazyFrame({
3430
position: "fixed",
3531
width: opt_w,
3632
height: opt_h,
33+
backgroundColor: inViewport ? undefined : "grey",
34+
backdropFilter: "none!important",
3735
}}
3836
ref={visibilityRef}
3937
>
40-
<div
41-
style={{
42-
top: 0,
43-
left: 0,
44-
position: "absolute",
45-
zIndex: -1,
46-
}}
47-
>
48-
{placeholder}
49-
</div>
5038
{enterCount > 0 && (
5139
<div
5240
style={{
5341
width: "100%",
5442
height: "100%",
55-
display: inViewport ? undefined : "none",
43+
display: inViewport ? "block" : "none",
5644
}}
5745
>
5846
{children}

editor/pages/_development/editor-canvas/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export default function EditorCanvasDevPage() {
1010
}}
1111
>
1212
<Canvas
13+
filekey="unknown"
1314
selectedNodes={[]}
1415
nodes={[]}
1516
renderItem={function (node): React.ReactNode {

editor/scaffolds/canvas/canvas.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export function VisualContentArea({ fileid }: { fileid: string }) {
5050
>
5151
<Canvas
5252
key={selectedPage}
53+
filekey={state.design.key}
5354
selectedNodes={selectedNodes.filter(Boolean)}
5455
highlightedLayer={highlightedLayer}
5556
onSelectNode={(node) => {
@@ -59,8 +60,8 @@ export function VisualContentArea({ fileid }: { fileid: string }) {
5960
dispatch({ type: "select-node", node: null });
6061
}}
6162
nodes={thisPageNodes}
62-
renderItem={(node) => {
63-
return <Preview key={node.id} target={node} />;
63+
renderItem={(p) => {
64+
return <Preview key={p.node.id} target={p.node} {...p} />;
6465
}}
6566
renderFrameTitle={(p) => (
6667
<FrameTitleRenderer

editor/scaffolds/preview/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Previe Frame in canvas
2+
3+
> Optimized frame for loading all frames in single canvas
4+
5+
## Current state
6+
7+
- uses [css contain](https://developer.mozilla.org/en-US/docs/Web/CSS/contain) property
8+
- uses display hidden property
9+
- ref: https://developer.mozilla.org/en-US/docs/Learn/Performance/CSS
10+
11+
## Optimization steps (Draft)
12+
13+
1. bg (while loading)
14+
2. image (zoom < 1)
15+
2.1 small image (zoom < 0.3)
16+
3. vanilla (zoom >= 1)
17+
4. editing mode (tbd)
18+
19+
### Caching
20+
21+
- image caching
22+
- vanilla caching
23+
24+
### Params
25+
26+
- zoom: the zoom of the canvas
27+
- in-viewport: whether if the canvas is in the viewport
28+
- focused: whether if the frame is selected

0 commit comments

Comments
 (0)