@heyramzi/ui 2.22.0 → 2.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/canvas/ui/canvas-grid.svelte +72 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyramzi/ui",
|
|
3
|
-
"version": "2.22.
|
|
3
|
+
"version": "2.22.1",
|
|
4
4
|
"description": "Svelte 5 UI for apps: primitives, effects and animations, plus work (tasks and docs), charts, slides, an editor, onboarding and a canvas as subpaths.",
|
|
5
5
|
"homepage": "https://ui.heyramzi.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
color?: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** x, y, width, height in device pixels. */
|
|
20
|
+
type Rect = [number, number, number, number];
|
|
21
|
+
|
|
19
22
|
// =========================
|
|
20
23
|
// CONSTANTS
|
|
21
24
|
// =========================
|
|
@@ -58,6 +61,16 @@
|
|
|
58
61
|
// Pan redraws every frame, so a tile is built once per size and colour.
|
|
59
62
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- render cache written inside the draw effect, never rendered
|
|
60
63
|
const tiles = new Map<string, HTMLCanvasElement>();
|
|
64
|
+
// What the canvas holds now, its origin in device pixels. Plain: only the draw effect reads it.
|
|
65
|
+
let painted: {
|
|
66
|
+
zoom: number;
|
|
67
|
+
ratio: number;
|
|
68
|
+
fill: string;
|
|
69
|
+
w: number;
|
|
70
|
+
h: number;
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
} | null = null;
|
|
61
74
|
|
|
62
75
|
function dotTile(step: number, radius: number, fill: string) {
|
|
63
76
|
const key = `${step}:${radius.toFixed(2)}:${fill}`;
|
|
@@ -77,21 +90,20 @@
|
|
|
77
90
|
return tile;
|
|
78
91
|
}
|
|
79
92
|
|
|
93
|
+
/** Fills the given device-pixel rects with the dots, the grid's origin at `originX`, `originY`. */
|
|
80
94
|
// fallow-ignore-next-line complexity
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
function paint(
|
|
96
|
+
context: CanvasRenderingContext2D,
|
|
97
|
+
zoom: number,
|
|
84
98
|
fill: string,
|
|
99
|
+
originX: number,
|
|
100
|
+
originY: number,
|
|
101
|
+
rects: Rect[],
|
|
85
102
|
) {
|
|
86
|
-
const context = element.getContext("2d");
|
|
87
|
-
if (!context) return;
|
|
88
|
-
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
89
|
-
context.clearRect(0, 0, element.width, element.height);
|
|
90
|
-
|
|
91
103
|
for (let level = 0; level < LEVELS; level++) {
|
|
92
|
-
const spacing = gap * 2 ** level *
|
|
104
|
+
const spacing = gap * 2 ** level * zoom;
|
|
93
105
|
// A coarser level draws bigger dots, so zooming out reads as moving away.
|
|
94
|
-
const screenRadius = (size / 2) * 2 ** level *
|
|
106
|
+
const screenRadius = (size / 2) * 2 ** level * zoom;
|
|
95
107
|
const radius = Math.min(MAX_RADIUS, Math.max(MIN_RADIUS, screenRadius)) * ratio;
|
|
96
108
|
const last = level === LEVELS - 1;
|
|
97
109
|
const alpha = last ? 1 : smoothstep(FADE_START, FADE_END, spacing);
|
|
@@ -102,16 +114,64 @@
|
|
|
102
114
|
const pattern = context.createPattern(dotTile(tileSize, radius / scale, fill), "repeat");
|
|
103
115
|
if (!pattern) return;
|
|
104
116
|
pattern.setTransform(
|
|
105
|
-
new DOMMatrix([scale, 0, 0, scale,
|
|
117
|
+
new DOMMatrix([scale, 0, 0, scale, originX - step / 2, originY - step / 2]),
|
|
106
118
|
);
|
|
107
119
|
context.globalAlpha = alpha;
|
|
108
120
|
context.fillStyle = pattern;
|
|
109
|
-
context.fillRect(
|
|
121
|
+
for (const [x, y, w, h] of rects) context.fillRect(x, y, w, h);
|
|
110
122
|
if (alpha >= 0.999) break;
|
|
111
123
|
}
|
|
112
124
|
context.globalAlpha = 1;
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
/** The strips a shift of `dx`, `dy` uncovers: a column on one side and a row on the other. */
|
|
128
|
+
function uncovered(dx: number, dy: number, w: number, h: number): Rect[] {
|
|
129
|
+
const rects: Rect[] = [];
|
|
130
|
+
if (dx > 0) rects.push([0, 0, dx, h]);
|
|
131
|
+
if (dx < 0) rects.push([w + dx, 0, -dx, h]);
|
|
132
|
+
if (dy > 0) rects.push([0, 0, w, dy]);
|
|
133
|
+
if (dy < 0) rects.push([0, h + dy, w, -dy]);
|
|
134
|
+
return rects;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// A pan moves the pixels already drawn and paints only the strip it uncovered; anything else repaints it all.
|
|
138
|
+
// fallow-ignore-next-line complexity
|
|
139
|
+
function draw(
|
|
140
|
+
element: HTMLCanvasElement,
|
|
141
|
+
view: { x: number; y: number; zoom: number },
|
|
142
|
+
fill: string,
|
|
143
|
+
) {
|
|
144
|
+
const context = element.getContext("2d");
|
|
145
|
+
if (!context) return;
|
|
146
|
+
const { width: w, height: h } = element;
|
|
147
|
+
const x = view.x * ratio;
|
|
148
|
+
const y = view.y * ratio;
|
|
149
|
+
const last = painted;
|
|
150
|
+
const same =
|
|
151
|
+
last &&
|
|
152
|
+
last.zoom === view.zoom &&
|
|
153
|
+
last.ratio === ratio &&
|
|
154
|
+
last.fill === fill &&
|
|
155
|
+
last.w === w &&
|
|
156
|
+
last.h === h;
|
|
157
|
+
// Whole pixels only, so the shifted dots stay sharp; the rest waits for the next pan or zoom, never past half a pixel.
|
|
158
|
+
const dx = same ? Math.round(x - last.x) : 0;
|
|
159
|
+
const dy = same ? Math.round(y - last.y) : 0;
|
|
160
|
+
if (same && Math.abs(dx) < w && Math.abs(dy) < h) {
|
|
161
|
+
if (!dx && !dy) return;
|
|
162
|
+
context.globalCompositeOperation = "copy";
|
|
163
|
+
context.drawImage(element, dx, dy);
|
|
164
|
+
context.globalCompositeOperation = "source-over";
|
|
165
|
+
painted = { ...last, x: last.x + dx, y: last.y + dy };
|
|
166
|
+
paint(context, view.zoom, fill, painted.x, painted.y, uncovered(dx, dy, w, h));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
170
|
+
context.clearRect(0, 0, w, h);
|
|
171
|
+
painted = { zoom: view.zoom, ratio, fill, w, h, x, y };
|
|
172
|
+
paint(context, view.zoom, fill, x, y, [[0, 0, w, h]]);
|
|
173
|
+
}
|
|
174
|
+
|
|
115
175
|
// =========================
|
|
116
176
|
// EFFECTS
|
|
117
177
|
// =========================
|