@fusion.dev/helix 0.172.0 → 0.174.0
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/custom-elements.json +2140 -93
- package/dist/copy-button/root/copy-button-root.css.js +1 -1
- package/dist/element-events.generated.js +8 -1
- package/dist/graph/root/graph-root.css.js +2 -1
- package/dist/orb/root/orb-root.css.js +1 -1
- package/dist/orb/root/orb-root.d.ts +24 -5
- package/dist/orb/root/orb-root.js +24 -6
- package/dist/sandbox/index.d.ts +1 -0
- package/dist/sandbox/index.js +1 -0
- package/dist/sandbox/preview/sandbox-preview.d.ts +39 -2
- package/dist/sandbox/preview/sandbox-preview.js +37 -5
- package/dist/sandbox/root/sandbox-root.css.js +1 -1
- package/dist/sandbox/root/sandbox-root.d.ts +44 -4
- package/dist/sandbox/root/sandbox-root.js +92 -17
- package/dist/sandbox/source/editing.d.ts +111 -0
- package/dist/sandbox/source/editing.js +185 -0
- package/dist/sandbox/source/sandbox-source.css.js +2 -1
- package/dist/sandbox/source/sandbox-source.d.ts +131 -6
- package/dist/sandbox/source/sandbox-source.js +301 -17
- package/dist/strands/palette.d.ts +1 -1
- package/dist/strands/path.d.ts +17 -6
- package/dist/strands/path.js +149 -43
- package/dist/strands/renderer.d.ts +14 -0
- package/dist/strands/renderer.js +14 -4
- package/dist/strands/root/strands-root.css.js +1 -1
- package/dist/strands/root/strands-root.d.ts +41 -10
- package/dist/strands/root/strands-root.js +32 -5
- package/dist/strands/shader.d.ts +3 -3
- package/dist/strands/shader.js +35 -12
- package/dist/strands/stop/strands-stop.d.ts +5 -5
- package/dist/timeline/root/timeline-root.css.js +1 -1
- package/dist/virtual-list/reorder/index.d.ts +1 -0
- package/dist/virtual-list/reorder/index.js +5 -0
- package/dist/virtual-list/reorder/virtual-list-reorder.css.d.ts +1 -0
- package/dist/virtual-list/reorder/virtual-list-reorder.css.js +12 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +9 -0
- package/lint.json +1 -1
- package/package.json +6 -6
- package/types/jsx.d.ts +919 -79
- package/types/vue.d.ts +214 -15
- package/types/words.d.ts +27 -0
|
@@ -26,7 +26,7 @@ export declare const RESTING_PALETTE: StrandsPalette;
|
|
|
26
26
|
* above the element, and the token otherwise:
|
|
27
27
|
*
|
|
28
28
|
* - `color`: `--strands-glow`, or `primary`.
|
|
29
|
-
* - `text-decoration-color`: `--strands-tint`, or `
|
|
29
|
+
* - `text-decoration-color`: `--strands-tint`, or `accent`.
|
|
30
30
|
* - `outline-color`: `--strands-ground`, or the background.
|
|
31
31
|
* - `caret-color` and `column-rule-color`: `--strands-peak`, or the dark and
|
|
32
32
|
* the light page's own peak.
|
package/dist/strands/path.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* screen are all arithmetic.
|
|
8
8
|
*/
|
|
9
9
|
/** The three forms the ribbon takes. */
|
|
10
|
-
export type StrandsVariant = '
|
|
10
|
+
export type StrandsVariant = 'bend' | 'ribbon' | 'waves';
|
|
11
11
|
/**
|
|
12
12
|
* One point on the centerline: `x` as a share of the width, `y` in CSS pixels,
|
|
13
13
|
* the ribbon's width as a share of the width, and its brightness from `0` to
|
|
@@ -38,6 +38,15 @@ export interface PlacedStop {
|
|
|
38
38
|
* than forty screens long; a longer one keeps its first 96 points and fades.
|
|
39
39
|
*/
|
|
40
40
|
export declare const MAX_POINTS = 96;
|
|
41
|
+
/**
|
|
42
|
+
* The tightest any form turns, as a share of the box's height: the radius of
|
|
43
|
+
* its sharpest curve.
|
|
44
|
+
*
|
|
45
|
+
* Half the height. A bend's apex a few dozen pixels round kinked the strands
|
|
46
|
+
* that ran along its inside, and read as a fold in the light where every form
|
|
47
|
+
* is meant to be one sweep. `path.pure.spec.ts` measures it on every form.
|
|
48
|
+
*/
|
|
49
|
+
export declare const TURN = 0.5;
|
|
41
50
|
/**
|
|
42
51
|
* The ribbon drawn inside its own box, which is what an element without
|
|
43
52
|
* `travel` shows.
|
|
@@ -45,18 +54,20 @@ export declare const MAX_POINTS = 96;
|
|
|
45
54
|
* @param variant The form to draw.
|
|
46
55
|
* @param at Where it sits across the box, as a share of the width. A wave
|
|
47
56
|
* crosses the whole width and does not read it.
|
|
48
|
-
* @param height The box's height, in CSS pixels.
|
|
49
|
-
*
|
|
57
|
+
* @param height The box's height, in CSS pixels.
|
|
58
|
+
* @param width The box's width, in CSS pixels. `x` is a share of it, and a
|
|
59
|
+
* bend's apex is drawn in pixels so it keeps its radius at any shape.
|
|
50
60
|
*/
|
|
51
|
-
export declare const boxPath: (variant: StrandsVariant, at: number | undefined, height: number) => PathPoint[];
|
|
61
|
+
export declare const boxPath: (variant: StrandsVariant, at: number | undefined, height: number, width: number) => PathPoint[];
|
|
52
62
|
/**
|
|
53
63
|
* The ribbon laid down a whole page, which is what an element with `travel`
|
|
54
64
|
* shows.
|
|
55
65
|
*
|
|
56
66
|
* Each stop places its form at its target, in page order. Between two stops
|
|
57
67
|
* the ribbon fills the gap with soft S-curves in `fill`'s place, except
|
|
58
|
-
* between two waves
|
|
59
|
-
*
|
|
68
|
+
* between two waves less than a screen and a half apart, where it turns off
|
|
69
|
+
* screen so each wave reads alone.
|
|
70
|
+
* Without stops, `fill` runs the length of the page: a bend opens the page and
|
|
60
71
|
* a ribbon follows it, waves cross every screen and a third, and a ribbon
|
|
61
72
|
* meanders the whole way. After a last wave the ribbon does not come back, so
|
|
62
73
|
* the page ends on the wave.
|
package/dist/strands/path.js
CHANGED
|
@@ -1,37 +1,91 @@
|
|
|
1
1
|
export const MAX_POINTS = 96;
|
|
2
2
|
const NARROW = 760;
|
|
3
|
-
const GLOW = {
|
|
4
|
-
const WIDTH = {
|
|
5
|
-
const
|
|
6
|
-
const
|
|
3
|
+
const GLOW = { bend: 1, ribbon: 0.36, waves: 0.7 };
|
|
4
|
+
const WIDTH = { bend: 0.12, ribbon: 0.24, waves: 0.06 };
|
|
5
|
+
const BEND_AT = 0.5;
|
|
6
|
+
const BEND_EXIT = 0.3;
|
|
7
|
+
export const TURN = 0.5;
|
|
8
|
+
const bend = (top, height, at, narrow, width, reach) => {
|
|
7
9
|
const x = narrow ? Math.max(at, 0.8) : at;
|
|
8
10
|
const y = top + height * 0.58;
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const out = (down) => x + (down * down * height) / (2 * TURN) / width;
|
|
12
|
+
const points = [];
|
|
13
|
+
for (let step = -7; step * 0.15 <= reach + 1e-9; step++) {
|
|
14
|
+
const down = step * 0.15;
|
|
15
|
+
const upper = down < 0;
|
|
16
|
+
const wide = upper
|
|
17
|
+
? 0.02 + (WIDTH.bend - 0.02) * Math.min(1, -down / 0.6)
|
|
18
|
+
: 0.02 + (WIDTH.bend * 1.4 - 0.02) * (down / reach);
|
|
19
|
+
const glow = upper ? Math.max(0, 1 + down * 1.07) : 1 - down * 0.73;
|
|
20
|
+
points.push([out(down), y + down * height, wide, GLOW.bend * glow]);
|
|
21
|
+
}
|
|
22
|
+
return points;
|
|
17
23
|
};
|
|
18
|
-
const wave = (y, height, fromRight, narrow) => {
|
|
24
|
+
const wave = (y, height, fromRight, narrow, width, from) => {
|
|
19
25
|
const w = narrow ? WIDTH.waves * 2 : WIDTH.waves;
|
|
20
26
|
const glow = narrow ? GLOW.waves * 0.65 : GLOW.waves;
|
|
21
27
|
const side = (share) => (fromRight ? 1 - share : share);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
[
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
if (from !== undefined) {
|
|
29
|
+
const across = (share) => from + ((fromRight ? 0 : 1) - from) * share;
|
|
30
|
+
return [
|
|
31
|
+
[across(0.03), y - height * 0.62, WIDTH.ribbon * 0.7, GLOW.ribbon],
|
|
32
|
+
[across(0.12), y - height * 0.36, WIDTH.ribbon * 0.5, glow * 0.55],
|
|
33
|
+
[across(0.38), y - height * 0.1, w, glow],
|
|
34
|
+
[across(0.66), y + height * 0.04, w, glow],
|
|
35
|
+
[across(1), y + height * 0.14, w, glow * 0.6],
|
|
36
|
+
[across(1.5), y + height * 0.3, w, 0],
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
const beyond = height / width;
|
|
40
|
+
const rise = Math.min(18, width / 80);
|
|
41
|
+
const span = 1 + 2 * beyond;
|
|
42
|
+
const steps = Math.ceil(span / ((height * 0.45) / width));
|
|
43
|
+
const points = [];
|
|
44
|
+
for (let k = 0; k <= steps; k++) {
|
|
45
|
+
const share = -beyond + (k / steps) * span;
|
|
46
|
+
const out = share < 0 ? -share / beyond : share > 1 ? (share - 1) / beyond : 0;
|
|
47
|
+
const lift = share < 0 ? -out * out : out * out;
|
|
48
|
+
const ripple = share >= 0 && share <= 1 ? rise * Math.sin(2 * Math.PI * share) : 0;
|
|
49
|
+
points.push([
|
|
50
|
+
side(share),
|
|
51
|
+
y + lift * height * 0.35 + ripple,
|
|
52
|
+
w,
|
|
53
|
+
glow * Math.max(0, 1 - out * 1.6),
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
return points;
|
|
30
57
|
};
|
|
31
|
-
const
|
|
58
|
+
const swingOf = (height, width, narrow) => Math.min(narrow ? 0.26 : 0.3, (height * TURN) / width);
|
|
59
|
+
const RIBBON_STEP = 0.35;
|
|
60
|
+
const bridge = (from, to, first, last) => {
|
|
61
|
+
const length = to[1] - from[1];
|
|
62
|
+
const gaps = [];
|
|
63
|
+
let gap = first;
|
|
64
|
+
let sum = 0;
|
|
65
|
+
while (sum + Math.min(gap, last) * 1.5 < length) {
|
|
66
|
+
gaps.push(Math.min(gap, last));
|
|
67
|
+
sum += Math.min(gap, last);
|
|
68
|
+
gap *= 1.33;
|
|
69
|
+
}
|
|
70
|
+
const scale = length / (sum + Math.min(gap, last));
|
|
71
|
+
const out = [];
|
|
72
|
+
let at = 0;
|
|
73
|
+
for (const one of gaps) {
|
|
74
|
+
at += one * scale;
|
|
75
|
+
const f = at / length;
|
|
76
|
+
out.push([
|
|
77
|
+
from[0] + (to[0] - from[0]) * f,
|
|
78
|
+
from[1] + (to[1] - from[1]) * f,
|
|
79
|
+
from[2] + (to[2] - from[2]) * f,
|
|
80
|
+
from[3] + (to[3] - from[3]) * f,
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
};
|
|
85
|
+
const ribbon = (from, to, height, at, phase, narrow, width) => {
|
|
32
86
|
const points = [];
|
|
33
|
-
const step = height *
|
|
34
|
-
const swing =
|
|
87
|
+
const step = height * RIBBON_STEP;
|
|
88
|
+
const swing = swingOf(height, width, narrow);
|
|
35
89
|
const wide = narrow ? 0.45 : WIDTH.ribbon;
|
|
36
90
|
const glow = narrow ? GLOW.ribbon * 0.65 : GLOW.ribbon;
|
|
37
91
|
let a = phase;
|
|
@@ -41,17 +95,17 @@ const ribbon = (from, to, height, at, phase, narrow) => {
|
|
|
41
95
|
}
|
|
42
96
|
return { phase: a, points };
|
|
43
97
|
};
|
|
44
|
-
export const boxPath = (variant, at, height) => {
|
|
98
|
+
export const boxPath = (variant, at, height, width) => {
|
|
45
99
|
const narrow = false;
|
|
46
|
-
if (variant === '
|
|
47
|
-
return
|
|
100
|
+
if (variant === 'bend') {
|
|
101
|
+
return bend(0, height, at ?? BEND_AT, narrow, width, 0.6);
|
|
48
102
|
}
|
|
49
103
|
if (variant === 'waves') {
|
|
50
|
-
return wave(height * 0.5, height, false, narrow);
|
|
104
|
+
return wave(height * 0.5, height, false, narrow, width);
|
|
51
105
|
}
|
|
52
106
|
const start = -height * 0.4;
|
|
53
107
|
const lead = [at ?? 0.5, start - height * 0.4, WIDTH.ribbon, 0];
|
|
54
|
-
const body = ribbon(start, height * 1.4, height, at ?? 0.5, 0, narrow).points.map(([x, y, w]) => [x, y, w, narrow ? GLOW.ribbon * 0.65 * 1.4 : GLOW.ribbon * 1.4]);
|
|
108
|
+
const body = ribbon(start, height * 1.4, height, at ?? 0.5, 0, narrow, width).points.map(([x, y, w]) => [x, y, w, narrow ? GLOW.ribbon * 0.65 * 1.4 : GLOW.ribbon * 1.4]);
|
|
55
109
|
return [lead, ...body];
|
|
56
110
|
};
|
|
57
111
|
export const pagePath = (stops, fill, width, height, page) => {
|
|
@@ -63,42 +117,92 @@ export const pagePath = (stops, fill, width, height, page) => {
|
|
|
63
117
|
placed.push({ at: undefined, height, top: y, variant: 'waves' });
|
|
64
118
|
}
|
|
65
119
|
}
|
|
66
|
-
else if (fill === '
|
|
67
|
-
placed.push({ at: undefined, height, top: 0, variant: '
|
|
120
|
+
else if (fill === 'bend') {
|
|
121
|
+
placed.push({ at: undefined, height, top: 0, variant: 'bend' });
|
|
68
122
|
}
|
|
69
123
|
}
|
|
70
124
|
const points = [[0.6, -height * 0.9, WIDTH.ribbon, 0]];
|
|
125
|
+
const turn = (RIBBON_STEP / 1.7) * Math.PI;
|
|
71
126
|
let phase = 1.2;
|
|
72
127
|
let cursor = 0;
|
|
73
128
|
let fromRight = true;
|
|
74
129
|
let previous;
|
|
130
|
+
let joined;
|
|
75
131
|
const gap = (to, next) => {
|
|
76
132
|
if (to - cursor < height * 0.7) {
|
|
77
133
|
return;
|
|
78
134
|
}
|
|
79
|
-
if (previous === 'waves' && next === 'waves') {
|
|
135
|
+
if (previous === 'waves' && next === 'waves' && to - cursor < height * 1.5) {
|
|
80
136
|
return;
|
|
81
137
|
}
|
|
82
|
-
const
|
|
138
|
+
const end = next === 'waves' ? to - height * 0.95 : to - height * 0.45;
|
|
139
|
+
const stretch = ribbon(cursor + height * 0.2, end, height, 0.5, phase, narrow, width);
|
|
83
140
|
phase = stretch.phase;
|
|
84
|
-
|
|
85
|
-
|
|
141
|
+
let run = stretch.points;
|
|
142
|
+
let heading;
|
|
143
|
+
if (next === 'waves') {
|
|
144
|
+
for (let k = run.length - 2; k > 0; k--) {
|
|
145
|
+
const [one, two, three] = run.slice(k - 1, k + 2);
|
|
146
|
+
if ((two[0] - one[0]) * (three[0] - two[0]) <= 0) {
|
|
147
|
+
run = run.slice(0, k + 1);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
if (k === 1) {
|
|
151
|
+
heading = three[0] < two[0];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (previous === 'waves' && run.length > 1) {
|
|
156
|
+
run = run.map((point, k) => k < 3 ? [point[0], point[1], point[2], point[3] * Math.max(0, k - 1) * 0.5] : point);
|
|
157
|
+
}
|
|
158
|
+
points.push(...run);
|
|
159
|
+
const last = run.at(-1);
|
|
86
160
|
if (last) {
|
|
87
|
-
fromRight = last[0] > 0.5;
|
|
161
|
+
fromRight = heading ?? last[0] > 0.5;
|
|
162
|
+
joined = last[0];
|
|
88
163
|
}
|
|
89
164
|
};
|
|
90
165
|
for (const stop of placed) {
|
|
166
|
+
joined = undefined;
|
|
91
167
|
gap(stop.top, stop.variant);
|
|
92
|
-
if (stop.variant === '
|
|
168
|
+
if (stop.variant === 'bend') {
|
|
93
169
|
const span = Math.min(stop.height, height);
|
|
94
|
-
|
|
95
|
-
|
|
170
|
+
const at = stop.at ?? BEND_AT;
|
|
171
|
+
const slope = BEND_EXIT / TURN;
|
|
172
|
+
const swing = swingOf(height, width, narrow);
|
|
173
|
+
const steepest = (swing * width * Math.PI) / (1.7 * height);
|
|
174
|
+
const a = Math.acos(Math.min(1, slope / steepest));
|
|
175
|
+
const x = 0.5 + swing * Math.sin(a);
|
|
176
|
+
const bent = bend(stop.top, span, at, narrow, width, narrow ? 0.6 : BEND_EXIT);
|
|
177
|
+
const exit = bent.at(-1);
|
|
178
|
+
points.push(...bent);
|
|
179
|
+
if (!narrow && x > exit[0]) {
|
|
180
|
+
const drop = Math.max(height * 0.3, ((x - exit[0]) * width) / slope);
|
|
181
|
+
const start = [x, exit[1] + drop, WIDTH.ribbon, GLOW.ribbon];
|
|
182
|
+
points.push(...bridge(exit, start, span * 0.15, height * RIBBON_STEP));
|
|
183
|
+
cursor = start[1] - height * 0.2;
|
|
184
|
+
phase = a - turn;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
cursor = exit[1] + height * 0.1;
|
|
188
|
+
phase = Math.PI / 2 - turn;
|
|
189
|
+
}
|
|
96
190
|
fromRight = true;
|
|
97
191
|
}
|
|
98
192
|
else if (stop.variant === 'waves') {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
193
|
+
const crossing = wave(stop.top, height, fromRight, narrow, width, joined);
|
|
194
|
+
const end = points.at(-1);
|
|
195
|
+
const lead = crossing[0];
|
|
196
|
+
if (joined !== undefined && end && lead) {
|
|
197
|
+
points.push(...bridge(end, lead, height * RIBBON_STEP, height * 0.26));
|
|
198
|
+
}
|
|
199
|
+
points.push(...crossing);
|
|
200
|
+
phase =
|
|
201
|
+
joined === undefined
|
|
202
|
+
? (fromRight ? Math.PI * 1.5 : Math.PI / 2) - turn
|
|
203
|
+
: (fromRight ? Math.PI / 2 : Math.PI * 1.5) - turn;
|
|
204
|
+
fromRight = joined === undefined ? !fromRight : fromRight;
|
|
205
|
+
cursor = stop.top + height * (joined === undefined ? 0.35 : 0.5);
|
|
102
206
|
}
|
|
103
207
|
else {
|
|
104
208
|
const y = stop.top + Math.min(stop.height, height) / 2;
|
|
@@ -111,7 +215,9 @@ export const pagePath = (stops, fill, width, height, page) => {
|
|
|
111
215
|
gap(page + height * 0.4, undefined);
|
|
112
216
|
}
|
|
113
217
|
const last = points.at(-1) ?? [0.5, page, WIDTH.ribbon, 0];
|
|
114
|
-
points.
|
|
218
|
+
const before = points.at(-2) ?? last;
|
|
219
|
+
const on = [2 * last[0] - before[0], 2 * last[1] - before[1], last[2], 0];
|
|
220
|
+
points.push(on, [on[0], Math.max(on[1], page) + height, last[2], 0]);
|
|
115
221
|
return points.slice(0, MAX_POINTS);
|
|
116
222
|
};
|
|
117
223
|
export const catmull = (p0, p1, p2, p3, f) => {
|
|
@@ -3,10 +3,24 @@ import type { StrandsPalette } from './palette.js';
|
|
|
3
3
|
export declare const MAX_STRANDS = 340;
|
|
4
4
|
/** The fewest, at `density` 0. Fewer stops reading as a ribbon at all. */
|
|
5
5
|
export declare const MIN_STRANDS = 60;
|
|
6
|
+
/** Specks of dust around the strands at a `dust` of `1`. */
|
|
7
|
+
export declare const DUST = 600;
|
|
8
|
+
/** The most specks a frame draws, at a `dust` of `4`. */
|
|
9
|
+
export declare const MAX_DUST: number;
|
|
6
10
|
/** What one frame needs, all of it already resolved by the element. */
|
|
7
11
|
export interface StrandsFrame {
|
|
12
|
+
/** How much the glow around the strands adds, as a multiplier. */
|
|
13
|
+
bloom: number;
|
|
14
|
+
/** How far apart the brightest and dimmest strands are, from `0` to `1`. */
|
|
15
|
+
contrast: number;
|
|
8
16
|
/** How many of the path's points are in use. */
|
|
9
17
|
count: number;
|
|
18
|
+
/** How much nearness shows, from `0`, flat, to `1`. */
|
|
19
|
+
depth: number;
|
|
20
|
+
/** How many specks of dust drift around the strands. */
|
|
21
|
+
dust: number;
|
|
22
|
+
/** How differently each strand moves, from `0`, as one, to `1`. */
|
|
23
|
+
drift: number;
|
|
10
24
|
/** The device pixels per CSS pixel the buffer was sized at. */
|
|
11
25
|
dpr: number;
|
|
12
26
|
/** Overall brightness. */
|
package/dist/strands/renderer.js
CHANGED
|
@@ -2,7 +2,11 @@ import { BLUR_FRAGMENT, COMPOSITE_FRAGMENT, DOWN_FRAGMENT, DUST_FRAGMENT, DUST_V
|
|
|
2
2
|
export const MAX_STRANDS = 340;
|
|
3
3
|
export const MIN_STRANDS = 60;
|
|
4
4
|
const SEGMENTS = 320;
|
|
5
|
-
const DUST = 600;
|
|
5
|
+
export const DUST = 600;
|
|
6
|
+
export const MAX_DUST = DUST * 4;
|
|
7
|
+
const SPREAD = 1.35;
|
|
8
|
+
const BLOOM = 0.75;
|
|
9
|
+
const GAIN = 1.615;
|
|
6
10
|
export class StrandsRenderer {
|
|
7
11
|
constructor(gl, programs) {
|
|
8
12
|
this.targets = null;
|
|
@@ -82,6 +86,7 @@ export class StrandsRenderer {
|
|
|
82
86
|
this.vec3(this.line, 'uTint', palette.tint);
|
|
83
87
|
this.vec3(this.line, 'uPeak', palette.peak);
|
|
84
88
|
this.float(this.line, 'uSoftness', frame.softness);
|
|
89
|
+
this.float(this.line, 'uContrast', frame.contrast);
|
|
85
90
|
gl.bindVertexArray(this.ribbon);
|
|
86
91
|
gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, (SEGMENTS + 1) * 2, frame.strands);
|
|
87
92
|
this.use(this.dust, frame);
|
|
@@ -89,7 +94,9 @@ export class StrandsRenderer {
|
|
|
89
94
|
this.vec3(this.dust, 'uPeak', palette.peak);
|
|
90
95
|
this.float(this.dust, 'uStrands', frame.strands);
|
|
91
96
|
gl.bindVertexArray(this.empty);
|
|
92
|
-
|
|
97
|
+
if (frame.dust > 0) {
|
|
98
|
+
gl.drawArrays(gl.POINTS, 0, frame.dust);
|
|
99
|
+
}
|
|
93
100
|
gl.disable(gl.BLEND);
|
|
94
101
|
}
|
|
95
102
|
const { far, farSwap, near, nearSwap } = targets;
|
|
@@ -108,7 +115,8 @@ export class StrandsRenderer {
|
|
|
108
115
|
this.vec3(this.composite, 'uGround', palette.ground);
|
|
109
116
|
this.vec3(this.composite, 'uInk', palette.peak);
|
|
110
117
|
this.float(this.composite, 'uLight', palette.light ? 1 : 0);
|
|
111
|
-
this.float(this.composite, 'uGain', (
|
|
118
|
+
this.float(this.composite, 'uGain', (GAIN * frame.gain) / this.scale);
|
|
119
|
+
this.float(this.composite, 'uBloom', BLOOM * frame.bloom);
|
|
112
120
|
gl.bindVertexArray(this.empty);
|
|
113
121
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
114
122
|
}
|
|
@@ -169,7 +177,9 @@ export class StrandsRenderer {
|
|
|
169
177
|
gl.uniform1f(program.uniforms.get('uScroll') ?? null, frame.scroll);
|
|
170
178
|
gl.uniform2f(program.uniforms.get('uT') ?? null, frame.range[0], frame.range[1]);
|
|
171
179
|
gl.uniform1f(program.uniforms.get('uTime') ?? null, frame.time);
|
|
172
|
-
gl.uniform1f(program.uniforms.get('uSpread') ?? null, frame.spread);
|
|
180
|
+
gl.uniform1f(program.uniforms.get('uSpread') ?? null, SPREAD * frame.spread);
|
|
181
|
+
gl.uniform1f(program.uniforms.get('uDepth') ?? null, frame.depth);
|
|
182
|
+
gl.uniform1f(program.uniforms.get('uDrift') ?? null, frame.drift);
|
|
173
183
|
gl.uniform1f(program.uniforms.get('uDpr') ?? null, frame.dpr);
|
|
174
184
|
gl.uniform1f(program.uniforms.get('uScale') ?? null, this.scale);
|
|
175
185
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSheet } from '@fusion.dev/helix-core';
|
|
2
|
-
export const styles = createSheet(`:host {--_strands-glow: var(--strands-glow, var(--helix-color-primary));--_strands-ground: var(--strands-ground, var(--helix-color-background));--_strands-tint: var(--strands-tint, var(--helix-color-
|
|
2
|
+
export const styles = createSheet(`:host {--_strands-glow: var(--strands-glow, var(--helix-color-primary));--_strands-ground: var(--strands-ground, var(--helix-color-background));--_strands-tint: var(--strands-tint, var(--helix-color-accent));position: relative;display: block;inline-size: 100%;block-size: 100%;background-image: linear-gradient(
|
|
3
3
|
115deg,
|
|
4
4
|
var(--_strands-ground) 20%,
|
|
5
5
|
color-mix(in oklab, var(--_strands-glow) 22%, var(--_strands-ground)) 48%,
|
|
@@ -11,16 +11,16 @@ import { type StrandsVariant } from '../path.js';
|
|
|
11
11
|
* roll over one another, and light runs along them.
|
|
12
12
|
*
|
|
13
13
|
* Its palette comes from the tokens. The strands are `--helix-color-primary`,
|
|
14
|
-
* a few are `--helix-color-
|
|
14
|
+
* a few are `--helix-color-accent` for depth, and the page they light is
|
|
15
15
|
* `--helix-color-background`. On a light page the light becomes ink, because
|
|
16
16
|
* light added to near-white cannot be seen.
|
|
17
17
|
*
|
|
18
|
-
* **Three forms, set with `variant`.** `
|
|
18
|
+
* **Three forms, set with `variant`.** `bend` turns bright strands through one tight point, with its
|
|
19
19
|
* arms opening to the right. `ribbon`, the default, is a broad, soft ribbon in
|
|
20
20
|
* long curves. `waves` is sharp strands crossing the full width.
|
|
21
21
|
*
|
|
22
22
|
* **In its own box, by default.** The element fills the box it is given, the
|
|
23
|
-
* way an image does, and `at` places the
|
|
23
|
+
* way an image does, and `at` places the bend's apex or the ribbon's axis across
|
|
24
24
|
* it. Content placed inside sits over the ribbon, in the `content` part.
|
|
25
25
|
*
|
|
26
26
|
* **Down the whole page, with `travel`.** The element fixes itself behind the
|
|
@@ -28,8 +28,9 @@ import { type StrandsVariant } from '../path.js';
|
|
|
28
28
|
* bottom, so scrolling travels along it. Put `helix-strands-stop` elements
|
|
29
29
|
* inside to pin a form to a section: each names an element with `for` and says
|
|
30
30
|
* which form the ribbon takes there. Between stops the ribbon fills the gap in
|
|
31
|
-
* its own `variant`, and between two waves
|
|
32
|
-
* reads alone. Set `z-index` on the
|
|
31
|
+
* its own `variant`, and between two waves less than a screen and a half
|
|
32
|
+
* apart it turns off screen so each wave reads alone. Set `z-index` on the
|
|
33
|
+
* element to move it.
|
|
33
34
|
*
|
|
34
35
|
* The ribbon stops moving in these cases:
|
|
35
36
|
* - `prefers-reduced-motion` is set. The element shows one still frame, and in
|
|
@@ -61,16 +62,16 @@ import { type StrandsVariant } from '../path.js';
|
|
|
61
62
|
* @cssproperty --strands-glow - The color of most strands. Defaults to `--helix-color-primary`.
|
|
62
63
|
* @cssproperty --strands-ground - The page the light is added to. Defaults to `--helix-color-background`.
|
|
63
64
|
* @cssproperty --strands-peak - The hottest strands, and on a light page the ink the light darkens toward. Defaults to `--helix-color-primary-hover` on a dark page and `--helix-color-primary-active` on a light one.
|
|
64
|
-
* @cssproperty --strands-tint - The color of a few strands, for depth. Defaults to `--helix-color-
|
|
65
|
+
* @cssproperty --strands-tint - The color of a few strands, for depth. Defaults to `--helix-color-accent`, which is the primary itself unless the theme relates an accent to the brand, as `accent: { from: 'brand', rotate: 40 }` does.
|
|
65
66
|
*
|
|
66
67
|
* @cssstate fallback - The element shows its gradient: the renderer draws on the CPU, there is no WebGL, or the browser took the context back. Cleared once the ribbon draws again.
|
|
67
68
|
*/
|
|
68
69
|
export declare class HelixStrands extends HelixElement {
|
|
69
70
|
static styles: CSSResultGroup;
|
|
70
71
|
/**
|
|
71
|
-
* Where the form sits across the box, as a share of the width: the
|
|
72
|
-
*
|
|
73
|
-
* crosses the whole width and does not read it. Unset, a
|
|
72
|
+
* Where the form sits across the box, as a share of the width: the bend's
|
|
73
|
+
* apex or the ribbon's axis. `0` is the left edge and `1` the right. A wave
|
|
74
|
+
* crosses the whole width and does not read it. Unset, a bend's apex sits at `0.5`
|
|
74
75
|
* and a ribbon runs down the middle.
|
|
75
76
|
*
|
|
76
77
|
* **A share rather than a length**, so the ribbon keeps its place when the
|
|
@@ -78,17 +79,47 @@ export declare class HelixStrands extends HelixElement {
|
|
|
78
79
|
* own.
|
|
79
80
|
*/
|
|
80
81
|
at?: number;
|
|
82
|
+
/**
|
|
83
|
+
* How much the glow around the strands adds, as a multiplier. `0` draws the
|
|
84
|
+
* strands alone, crisp and dimmer, and `1` is the default glow.
|
|
85
|
+
*/
|
|
86
|
+
bloom: number;
|
|
87
|
+
/**
|
|
88
|
+
* How far apart the brightest and dimmest strands are, from `0`, most of
|
|
89
|
+
* them alike, to `1`, a few burning bright over many faint ones. Clamped to
|
|
90
|
+
* that range. The ribbon as a whole keeps its strength.
|
|
91
|
+
*/
|
|
92
|
+
contrast: number;
|
|
81
93
|
/**
|
|
82
94
|
* How many strands the ribbon has, from `0`, sixty, to `1`, three hundred and
|
|
83
95
|
* forty. Clamped to that range.
|
|
84
96
|
*/
|
|
85
97
|
density: number;
|
|
98
|
+
/**
|
|
99
|
+
* How much the ribbon's depth shows, from `0`, flat, to `1`. Clamped to that
|
|
100
|
+
* range. The strands at the front of the ribbon's roll are drawn thicker,
|
|
101
|
+
* brighter, and further from its axis, and those at the back thinner, dimmer,
|
|
102
|
+
* and out of focus.
|
|
103
|
+
*/
|
|
104
|
+
depth: number;
|
|
105
|
+
/**
|
|
106
|
+
* How differently each strand moves, from `0`, the whole ribbon rolling and
|
|
107
|
+
* waving as one, to `1`, each strand at its own pace and angle, crossing
|
|
108
|
+
* over the others. Clamped to that range.
|
|
109
|
+
*/
|
|
110
|
+
drift: number;
|
|
111
|
+
/**
|
|
112
|
+
* How many specks of dust drift around the strands, as a multiplier: `0`
|
|
113
|
+
* draws none, `1` six hundred, and `4`, the most, two thousand four
|
|
114
|
+
* hundred. They gather where the ribbon is brightest.
|
|
115
|
+
*/
|
|
116
|
+
dust: number;
|
|
86
117
|
/** Overall brightness, as a multiplier on the light. */
|
|
87
118
|
gain: number;
|
|
88
119
|
/**
|
|
89
120
|
* The share of strands drawn out of focus where the ribbon is wide, from `0`,
|
|
90
121
|
* every strand sharp, to `1`, every one soft. Clamped to that range. A tight
|
|
91
|
-
* form, such as a
|
|
122
|
+
* form, such as a bend's apex or a wave, stays sharp whatever this says.
|
|
92
123
|
*/
|
|
93
124
|
softness: number;
|
|
94
125
|
/** How fast the ribbon moves, as a multiplier on time. `0` holds it still. */
|
|
@@ -10,18 +10,24 @@ import { html } from 'lit';
|
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
11
11
|
import { paletteOf, RESTING_PALETTE } from '../palette.js';
|
|
12
12
|
import { boxPath, lookup, MAX_POINTS, pagePath, visibleRange, } from '../path.js';
|
|
13
|
-
import { MAX_STRANDS, MIN_STRANDS, StrandsRenderer } from '../renderer.js';
|
|
13
|
+
import { DUST, MAX_DUST, MAX_STRANDS, MIN_STRANDS, StrandsRenderer } from '../renderer.js';
|
|
14
14
|
import { styles } from './strands-root.css.js';
|
|
15
15
|
const MAX_CANVAS_EDGE = 4096;
|
|
16
16
|
const MAX_CANVAS_PIXELS = 4_194_304;
|
|
17
17
|
const OPENING_PATIENCE = 500;
|
|
18
18
|
const TRAVEL_EASE = 0.09;
|
|
19
|
+
const PACE = 0.25;
|
|
19
20
|
let HelixStrands = class HelixStrands extends HelixElement {
|
|
20
21
|
constructor() {
|
|
21
22
|
super(...arguments);
|
|
23
|
+
this.bloom = 1;
|
|
24
|
+
this.contrast = 1;
|
|
22
25
|
this.density = 0.8;
|
|
26
|
+
this.depth = 1;
|
|
27
|
+
this.drift = 1;
|
|
28
|
+
this.dust = 1;
|
|
23
29
|
this.gain = 1;
|
|
24
|
-
this.softness = 0
|
|
30
|
+
this.softness = 0;
|
|
25
31
|
this.speed = 1;
|
|
26
32
|
this.spread = 1;
|
|
27
33
|
this.travel = false;
|
|
@@ -303,7 +309,7 @@ let HelixStrands = class HelixStrands extends HelixElement {
|
|
|
303
309
|
this.table = lookup(path);
|
|
304
310
|
}
|
|
305
311
|
else {
|
|
306
|
-
path = boxPath(variant, this.at, this.box.height);
|
|
312
|
+
path = boxPath(variant, this.at, this.box.height, this.box.width);
|
|
307
313
|
}
|
|
308
314
|
this.points.fill(0);
|
|
309
315
|
path.forEach((point, i) => {
|
|
@@ -327,7 +333,7 @@ let HelixStrands = class HelixStrands extends HelixElement {
|
|
|
327
333
|
at: Number.isFinite(at) ? at : undefined,
|
|
328
334
|
height: rect.height,
|
|
329
335
|
top: rect.top + scroll,
|
|
330
|
-
variant: variant === '
|
|
336
|
+
variant: variant === 'bend' || variant === 'waves' ? variant : 'ribbon',
|
|
331
337
|
});
|
|
332
338
|
}
|
|
333
339
|
return placed;
|
|
@@ -364,9 +370,15 @@ let HelixStrands = class HelixStrands extends HelixElement {
|
|
|
364
370
|
? visibleRange(this.table, this.offset - view[1] * 0.4, this.offset + view[1] * 1.4)
|
|
365
371
|
: [0, 1];
|
|
366
372
|
const density = Math.min(1, Math.max(0, this.density));
|
|
373
|
+
const share = (value) => Math.min(1, Math.max(0, value));
|
|
367
374
|
renderer.render({
|
|
375
|
+
bloom: Math.max(0, this.bloom),
|
|
376
|
+
contrast: share(this.contrast),
|
|
368
377
|
count: this.count,
|
|
378
|
+
depth: share(this.depth),
|
|
369
379
|
dpr: this.dpr,
|
|
380
|
+
drift: share(this.drift),
|
|
381
|
+
dust: Math.round(Math.min(MAX_DUST, Math.max(0, this.dust * DUST))),
|
|
370
382
|
gain: Math.max(0, this.gain),
|
|
371
383
|
points: this.points,
|
|
372
384
|
range,
|
|
@@ -425,7 +437,7 @@ let HelixStrands = class HelixStrands extends HelixElement {
|
|
|
425
437
|
}
|
|
426
438
|
const step = Math.min(this.held, MAX_FRAME_STEP);
|
|
427
439
|
this.held = 0;
|
|
428
|
-
this.elapsed += step * Math.max(0, this.speed);
|
|
440
|
+
this.elapsed += step * PACE * Math.max(0, this.speed);
|
|
429
441
|
if (this.travel) {
|
|
430
442
|
const target = window.scrollY;
|
|
431
443
|
this.offset += (target - this.offset) * (1 - Math.exp(-step / TRAVEL_EASE));
|
|
@@ -439,9 +451,24 @@ let HelixStrands = class HelixStrands extends HelixElement {
|
|
|
439
451
|
__decorate([
|
|
440
452
|
property({ type: Number })
|
|
441
453
|
], HelixStrands.prototype, "at", void 0);
|
|
454
|
+
__decorate([
|
|
455
|
+
property({ type: Number })
|
|
456
|
+
], HelixStrands.prototype, "bloom", void 0);
|
|
457
|
+
__decorate([
|
|
458
|
+
property({ type: Number })
|
|
459
|
+
], HelixStrands.prototype, "contrast", void 0);
|
|
442
460
|
__decorate([
|
|
443
461
|
property({ type: Number })
|
|
444
462
|
], HelixStrands.prototype, "density", void 0);
|
|
463
|
+
__decorate([
|
|
464
|
+
property({ type: Number })
|
|
465
|
+
], HelixStrands.prototype, "depth", void 0);
|
|
466
|
+
__decorate([
|
|
467
|
+
property({ type: Number })
|
|
468
|
+
], HelixStrands.prototype, "drift", void 0);
|
|
469
|
+
__decorate([
|
|
470
|
+
property({ type: Number })
|
|
471
|
+
], HelixStrands.prototype, "dust", void 0);
|
|
445
472
|
__decorate([
|
|
446
473
|
property({ type: Number })
|
|
447
474
|
], HelixStrands.prototype, "gain", void 0);
|