@fusion.dev/helix 0.2.0 → 0.3.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 +256 -256
- package/dist/accordion/item/accordion-item.css.js +0 -1
- package/dist/accordion/item/accordion-item.js +0 -29
- package/dist/accordion/root/accordion-root.css.js +0 -1
- package/dist/accordion/root/accordion-root.js +0 -20
- package/dist/announcement/root/announcement-root.css.js +0 -1
- package/dist/announcement/root/announcement-root.js +0 -16
- package/dist/app-header/root/app-header-root.css.js +0 -1
- package/dist/app-header/root/app-header-root.js +0 -45
- package/dist/aura/root/aura-axis.js +0 -58
- package/dist/aura/root/aura-root.css.js +0 -1
- package/dist/aura/root/aura-root.js +0 -520
- package/dist/aura/root/aura-shader.js +0 -35
- package/dist/avatar/group/avatar-group.css.js +0 -1
- package/dist/avatar/group/avatar-group.js +0 -24
- package/dist/avatar/root/avatar-root.css.js +0 -1
- package/dist/avatar/root/avatar-root.js +0 -23
- package/dist/badge/root/badge-root.css.js +0 -1
- package/dist/badge/root/badge-root.d.ts +2 -6
- package/dist/badge/root/badge-root.js +0 -18
- package/dist/bar-chart/root/bar-chart-root.css.js +0 -1
- package/dist/bar-chart/root/bar-chart-root.js +0 -50
- package/dist/blockquote/root/blockquote-root.css.js +0 -1
- package/dist/blockquote/root/blockquote-root.js +0 -17
- package/dist/breadcrumbs/breadcrumb/breadcrumb.css.js +0 -1
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +0 -28
- package/dist/breadcrumbs/root/breadcrumbs-root.css.js +0 -1
- package/dist/breadcrumbs/root/breadcrumbs-root.js +0 -19
- package/dist/button/root/button-root.css.js +0 -1
- package/dist/button/root/button-root.js +0 -38
- package/dist/button-group/root/button-group-root.css.js +0 -1
- package/dist/button-group/root/button-group-root.js +0 -24
- package/dist/callout/root/callout-root.css.js +0 -1
- package/dist/callout/root/callout-root.js +0 -26
- package/dist/card/root/card-root.css.js +0 -1
- package/dist/card/root/card-root.js +0 -45
- package/dist/carousel/root/carousel-root.css.js +0 -1
- package/dist/carousel/root/carousel-root.js +0 -24
- package/dist/chat/composer/chat-composer.css.js +0 -1
- package/dist/chat/composer/chat-composer.js +0 -26
- package/dist/chat/message/chat-message.css.js +0 -1
- package/dist/chat/message/chat-message.js +0 -29
- package/dist/chat/root/chat-root.css.js +0 -1
- package/dist/chat/root/chat-root.js +0 -38
- package/dist/checkbox/root/checkbox-root.css.js +0 -1
- package/dist/checkbox/root/checkbox-root.js +0 -26
- package/dist/checkbox-group/root/checkbox-group-root.css.js +0 -1
- package/dist/checkbox-group/root/checkbox-group-root.js +0 -15
- package/dist/code/root/code-root.css.js +0 -1
- package/dist/code/root/code-root.js +0 -21
- package/dist/code-block/root/code-block-root.css.js +0 -1
- package/dist/code-block/root/code-block-root.js +0 -19
- package/dist/color-input/root/color-input-root.css.js +0 -1
- package/dist/color-input/root/color-input-root.js +0 -42
- package/dist/color-picker/root/color-picker-root.css.js +0 -1
- package/dist/color-picker/root/color-picker-root.js +0 -42
- package/dist/combobox/root/combobox-root.css.js +0 -1
- package/dist/combobox/root/combobox-root.js +0 -28
- package/dist/command/root/command-root.css.js +0 -1
- package/dist/command/root/command-root.js +0 -21
- package/dist/copy-button/root/copy-button-root.css.js +0 -1
- package/dist/copy-button/root/copy-button-root.js +0 -14
- package/dist/data-table/root/data-table-root.css.js +0 -1
- package/dist/data-table/root/data-table-root.js +0 -39
- package/dist/date-input/root/date-input-root.css.js +0 -1
- package/dist/date-input/root/date-input-root.js +0 -38
- package/dist/date-picker/root/date-picker-root.css.js +0 -1
- package/dist/date-picker/root/date-picker-root.js +0 -28
- package/dist/dialog/root/dialog-root.css.js +0 -1
- package/dist/dialog/root/dialog-root.js +0 -31
- package/dist/divider/root/divider-root.css.js +0 -1
- package/dist/divider/root/divider-root.js +0 -24
- package/dist/donut-chart/root/donut-chart-root.css.js +0 -1
- package/dist/donut-chart/root/donut-chart-root.js +0 -49
- package/dist/drawer/root/drawer-root.css.js +0 -1
- package/dist/drawer/root/drawer-root.js +0 -38
- package/dist/drilldown/item/drilldown-item.css.js +0 -1
- package/dist/drilldown/item/drilldown-item.js +0 -15
- package/dist/drilldown/root/drilldown-root.css.js +0 -1
- package/dist/drilldown/root/drilldown-root.js +0 -23
- package/dist/element-events.generated.js +0 -5
- package/dist/empty/root/empty-root.css.js +0 -1
- package/dist/empty/root/empty-root.js +0 -16
- package/dist/file-input/root/file-input-root.css.js +0 -1
- package/dist/file-input/root/file-input-root.js +0 -30
- package/dist/flex/root/flex-root.css.js +0 -1
- package/dist/flex/root/flex-root.js +0 -32
- package/dist/format-date/root/format-date-root.css.js +0 -1
- package/dist/format-date/root/format-date-root.js +0 -17
- package/dist/grid/root/grid-root.css.js +0 -1
- package/dist/grid/root/grid-root.js +0 -69
- package/dist/heatmap/root/heatmap-root.css.js +0 -1
- package/dist/heatmap/root/heatmap-root.d.ts +9 -11
- package/dist/heatmap/root/heatmap-root.js +0 -73
- package/dist/icon/glyph.css.js +0 -1
- package/dist/icon/root/icon-root.css.js +0 -1
- package/dist/icon/root/icon-root.js +0 -19
- package/dist/icon-button/root/icon-button-root.css.js +0 -1
- package/dist/icon-button/root/icon-button-root.js +0 -35
- package/dist/image/root/image-root.css.js +0 -1
- package/dist/image/root/image-root.js +0 -28
- package/dist/index.js +0 -5
- package/dist/input/root/input-root.css.js +0 -1
- package/dist/input/root/input-root.js +0 -28
- package/dist/kbd/root/kbd-root.css.js +0 -1
- package/dist/kbd/root/kbd-root.js +0 -20
- package/dist/legend/root/legend-root.css.js +0 -1
- package/dist/legend/root/legend-root.js +0 -35
- package/dist/line-chart/root/line-chart-root.css.js +0 -1
- package/dist/line-chart/root/line-chart-root.js +0 -47
- package/dist/list/item/list-item.css.js +0 -1
- package/dist/list/item/list-item.js +0 -38
- package/dist/list/root/list-root.css.js +0 -1
- package/dist/list/root/list-root.js +0 -23
- package/dist/listbox/root/listbox-root.css.js +0 -1
- package/dist/listbox/root/listbox-root.js +0 -22
- package/dist/loader/root/loader-root.css.js +0 -1
- package/dist/loader/root/loader-root.js +0 -19
- package/dist/megamenu/root/megamenu-root.css.js +0 -1
- package/dist/megamenu/root/megamenu-root.js +0 -24
- package/dist/menu/item/menu-item.css.js +0 -1
- package/dist/menu/item/menu-item.js +0 -16
- package/dist/menu/root/menu-root.css.js +0 -1
- package/dist/menu/root/menu-root.js +0 -26
- package/dist/meter/root/meter-root.css.js +0 -1
- package/dist/meter/root/meter-root.js +0 -50
- package/dist/mixins/mixin-control-surface/mixin-control-surface.js +0 -3
- package/dist/navigation/root/navigation-root.css.js +0 -1
- package/dist/navigation/root/navigation-root.js +0 -18
- package/dist/notifications/notification/notification.css.js +0 -1
- package/dist/notifications/notification/notification.js +0 -53
- package/dist/notifications/root/notifications-root.css.js +0 -1
- package/dist/notifications/root/notifications-root.js +0 -31
- package/dist/number-input/root/number-input-root.css.js +0 -1
- package/dist/number-input/root/number-input-root.js +0 -31
- package/dist/option/group/option-group.css.js +0 -1
- package/dist/option/group/option-group.js +0 -13
- package/dist/option/root/option-root.css.js +0 -1
- package/dist/option/root/option-root.js +0 -20
- package/dist/outline/root/outline-root.css.js +0 -1
- package/dist/outline/root/outline-root.js +0 -21
- package/dist/pagination/root/pagination-root.css.js +0 -1
- package/dist/pagination/root/pagination-root.js +0 -49
- package/dist/pagination/size/pagination-size.css.js +0 -1
- package/dist/pagination/size/pagination-size.js +0 -25
- package/dist/pagination/summary/pagination-summary.css.js +0 -1
- package/dist/pagination/summary/pagination-summary.js +0 -13
- package/dist/pin-input/root/pin-input-root.css.js +0 -1
- package/dist/pin-input/root/pin-input-root.js +0 -27
- package/dist/popover/root/popover-root.css.js +0 -1
- package/dist/popover/root/popover-root.js +0 -30
- package/dist/progress/root/progress-root.css.js +0 -1
- package/dist/progress/root/progress-root.js +0 -25
- package/dist/qr-code/root/qr-code-root.css.js +0 -1
- package/dist/qr-code/root/qr-code-root.js +0 -27
- package/dist/quote/root/quote-root.css.js +0 -1
- package/dist/quote/root/quote-root.js +0 -15
- package/dist/radio/group/radio-group.css.js +0 -1
- package/dist/radio/group/radio-group.js +0 -16
- package/dist/radio/root/radio-root.css.js +0 -1
- package/dist/radio/root/radio-root.js +0 -46
- package/dist/rating/root/rating-root.css.js +0 -1
- package/dist/rating/root/rating-root.js +0 -22
- package/dist/sandbox/index.js +0 -14
- package/dist/sandbox/preview/sandbox-preview.css.js +0 -1
- package/dist/sandbox/preview/sandbox-preview.js +0 -182
- package/dist/sandbox/root/sandbox-root.css.js +0 -1
- package/dist/sandbox/root/sandbox-root.js +1 -295
- package/dist/sandbox/source/sandbox-source.css.js +0 -1
- package/dist/sandbox/source/sandbox-source.js +0 -77
- package/dist/sandbox/status-bar/sandbox-status-bar.css.js +0 -1
- package/dist/sandbox/status-bar/sandbox-status-bar.js +0 -38
- package/dist/sandbox/terminal/sandbox-terminal.css.js +0 -1
- package/dist/sandbox/terminal/sandbox-terminal.js +0 -54
- package/dist/sandbox/toolbar/sandbox-toolbar.css.js +0 -1
- package/dist/sandbox/toolbar/sandbox-toolbar.js +0 -29
- package/dist/scroll-area/root/scroll-area-root.css.js +0 -1
- package/dist/scroll-area/root/scroll-area-root.js +0 -24
- package/dist/select/root/select-root.css.js +0 -1
- package/dist/select/root/select-root.js +0 -33
- package/dist/shared/chart/series.css.js +0 -1
- package/dist/shared/chart/terminus.css.js +0 -1
- package/dist/shared/field/field-inline.css.js +0 -1
- package/dist/shared/field/field-stacked.css.js +0 -1
- package/dist/shared/field/field.css.js +0 -1
- package/dist/skeleton/root/skeleton-root.css.js +0 -1
- package/dist/skeleton/root/skeleton-root.js +0 -18
- package/dist/slider/root/slider-root.css.js +0 -1
- package/dist/slider/root/slider-root.js +0 -32
- package/dist/sparkline/root/sparkline-root.css.js +0 -1
- package/dist/sparkline/root/sparkline-root.js +0 -40
- package/dist/speed-dial/item/speed-dial-item.css.js +0 -1
- package/dist/speed-dial/item/speed-dial-item.js +0 -16
- package/dist/speed-dial/root/speed-dial-root.css.js +0 -1
- package/dist/speed-dial/root/speed-dial-root.js +0 -16
- package/dist/splitter/root/splitter-root.css.js +0 -1
- package/dist/splitter/root/splitter-root.js +0 -50
- package/dist/stat/group/stat-group.css.js +0 -1
- package/dist/stat/group/stat-group.js +0 -19
- package/dist/stat/root/stat-root.css.js +0 -1
- package/dist/stat/root/stat-root.js +0 -17
- package/dist/steps/root/steps-root.css.js +0 -1
- package/dist/steps/root/steps-root.js +0 -26
- package/dist/steps/step/step.css.js +0 -1
- package/dist/steps/step/step.js +0 -12
- package/dist/switch/root/switch-root.css.js +0 -1
- package/dist/switch/root/switch-root.js +0 -32
- package/dist/table/cell/table-cell.css.js +0 -1
- package/dist/table/cell/table-cell.js +0 -14
- package/dist/table/column/table-column.css.js +0 -1
- package/dist/table/column/table-column.js +0 -13
- package/dist/table/root/table-root.css.js +0 -1
- package/dist/table/root/table-root.js +0 -19
- package/dist/table/row/table-row.css.js +0 -1
- package/dist/table/row/table-row.js +0 -14
- package/dist/tabs/panel/tab-panel.css.js +0 -1
- package/dist/tabs/panel/tab-panel.js +0 -16
- package/dist/tabs/root/tabs-root.css.js +0 -1
- package/dist/tabs/root/tabs-root.js +0 -19
- package/dist/tabs/tab/tab.css.js +0 -1
- package/dist/tabs/tab/tab.js +0 -16
- package/dist/tag/root/tag-root.css.js +0 -1
- package/dist/tag/root/tag-root.js +0 -25
- package/dist/text/root/text-root.css.js +0 -1
- package/dist/text/root/text-root.js +0 -48
- package/dist/textarea/root/textarea-root.css.js +0 -1
- package/dist/textarea/root/textarea-root.js +0 -17
- package/dist/time-input/root/time-input-root.css.js +0 -1
- package/dist/time-input/root/time-input-root.js +0 -31
- package/dist/time-picker/root/time-picker-root.css.js +0 -1
- package/dist/time-picker/root/time-picker-root.js +0 -29
- package/dist/timeline/item/timeline-item.css.js +0 -1
- package/dist/timeline/item/timeline-item.js +0 -17
- package/dist/timeline/root/timeline-root.css.js +0 -1
- package/dist/timeline/root/timeline-root.js +0 -15
- package/dist/toast/item/toast-item.css.js +0 -1
- package/dist/toast/item/toast-item.js +0 -21
- package/dist/toast/root/toast-root.css.js +0 -1
- package/dist/toast/root/toast-root.js +0 -27
- package/dist/toggle/group/toggle-group.css.js +0 -1
- package/dist/toggle/group/toggle-group.js +0 -25
- package/dist/toggle/root/toggle-root.css.js +0 -1
- package/dist/toggle/root/toggle-root.js +0 -18
- package/dist/toolbar/root/toolbar-root.css.js +0 -1
- package/dist/toolbar/root/toolbar-root.js +0 -19
- package/dist/tooltip/root/tooltip-root.css.js +0 -1
- package/dist/tooltip/root/tooltip-root.js +0 -22
- package/dist/tour/root/tour-root.css.js +0 -1
- package/dist/tour/root/tour-root.js +0 -14
- package/dist/tour/stop/tour-stop.css.js +0 -1
- package/dist/tour/stop/tour-stop.js +0 -22
- package/dist/tree/item/tree-item.css.js +0 -1
- package/dist/tree/item/tree-item.js +0 -34
- package/dist/tree/root/tree-root.css.js +0 -1
- package/dist/tree/root/tree-root.js +0 -27
- package/dist/trend/root/trend-root.css.js +0 -1
- package/dist/trend/root/trend-root.js +0 -34
- package/dist/video/root/video-root.css.js +0 -1
- package/dist/video/root/video-root.js +0 -37
- package/dist/video-playlist/item/video-playlist-item.css.js +0 -1
- package/dist/video-playlist/item/video-playlist-item.js +0 -15
- package/dist/video-playlist/menu/video-playlist-menu.css.js +0 -1
- package/dist/video-playlist/menu/video-playlist-menu.js +0 -36
- package/dist/video-playlist/root/video-playlist-root.css.js +0 -1
- package/dist/video-playlist/root/video-playlist-root.js +0 -16
- package/dist/virtual-list/root/virtual-list-root.css.js +0 -1
- package/dist/virtual-list/root/virtual-list-root.js +0 -21
- package/jsx.d.ts +1538 -2818
- package/package.json +7 -7
- package/vue.d.ts +3 -7
|
@@ -11,169 +11,25 @@ import { property } from 'lit/decorators.js';
|
|
|
11
11
|
import { tiltFor } from './aura-axis.js';
|
|
12
12
|
import { styles } from './aura-root.css.js';
|
|
13
13
|
import { FRAGMENT, INTERACTIONS, SHAPES, VERTEX } from './aura-shader.js';
|
|
14
|
-
/**
|
|
15
|
-
* The widest either edge of the drawing buffer may get, in device pixels.
|
|
16
|
-
*
|
|
17
|
-
* Past `MAX_RENDERBUFFER_SIZE` a canvas does not draw at all, and WebGL2
|
|
18
|
-
* guarantees only 2048. Desktop parts report 16384 and older integrated ones
|
|
19
|
-
* report 8192, which a page-length field can pass.
|
|
20
|
-
*/
|
|
21
14
|
const MAX_CANVAS_EDGE = 4096;
|
|
22
|
-
/**
|
|
23
|
-
* The most device pixels the drawing buffer may hold, at 2048 squared.
|
|
24
|
-
*
|
|
25
|
-
* Every one of them runs five octaves of noise through two warps on every
|
|
26
|
-
* frame, so this is the fill rate rather than the fidelity: roughly what the
|
|
27
|
-
* component already cost over the two screens it was designed against.
|
|
28
|
-
*/
|
|
29
15
|
const MAX_CANVAS_PIXELS = 4_194_304;
|
|
30
|
-
/**
|
|
31
|
-
* How long the opening frame will wait for the box to stop changing, in ms.
|
|
32
|
-
*
|
|
33
|
-
* The field is scaled by its box, so painting a box that is still settling draws
|
|
34
|
-
* the same shape at several sizes and reads as the background zooming out. The
|
|
35
|
-
* wait ends one animation frame after the last change; this is only the cap, for
|
|
36
|
-
* a page that never stops resizing it. Longer than any load takes to settle and
|
|
37
|
-
* shorter than a reader takes to conclude the background is just a gradient.
|
|
38
|
-
*/
|
|
39
16
|
const OPENING_PATIENCE = 500;
|
|
40
|
-
/**
|
|
41
|
-
* @summary A lit, drifting background, drawn from the token layer.
|
|
42
|
-
*
|
|
43
|
-
* @description
|
|
44
|
-
* One fragment shader over a full-screen triangle, with the palette read from
|
|
45
|
-
* the tokens rather than given as colors. That is the whole reason this belongs
|
|
46
|
-
* in a design system instead of in a page: a background written with hex values
|
|
47
|
-
* is an asset that has to be re-cut when the brand moves, and one written
|
|
48
|
-
* against `--helix-color-primary` is a consequence of it.
|
|
49
|
-
*
|
|
50
|
-
* **Every shape ramps through four token stops**, and the token layer inverts
|
|
51
|
-
* each of them: the page's own background, `primary-subtle`, `primary`, and
|
|
52
|
-
* then a peak. The peak is the foreground on a dark page and `primary-active`
|
|
53
|
-
* on a light one, because mixing a saturated hue toward near-black takes the
|
|
54
|
-
* chroma out of exactly the pixels carrying the most of it. Which way the page
|
|
55
|
-
* runs is not asked of the mode, which is a private token: it is read off the
|
|
56
|
-
* ground's own luminance, which the shader already has.
|
|
57
|
-
*
|
|
58
|
-
* **No dependency, and no scene graph.** The geometry is three vertices derived
|
|
59
|
-
* from `gl_VertexID`, so there is no attribute, no buffer and no upload. The
|
|
60
|
-
* common implementations of this reach for a 3D library to draw one quad, which
|
|
61
|
-
* is a camera and a material system for four vertices that never move.
|
|
62
|
-
*
|
|
63
|
-
* **It stops when nobody is looking**, off screen and in a background tab, and
|
|
64
|
-
* `prefers-reduced-motion` gets one still frame rather than an empty box: the
|
|
65
|
-
* composition is the point and the drift is a garnish.
|
|
66
|
-
*
|
|
67
|
-
* **A layer over an interactive aura has to let the pointer through.** The
|
|
68
|
-
* element is hit-testable only when `interactive` is set, because a background
|
|
69
|
-
* that swallows clicks is worse than one that does nothing, but a scrim or an
|
|
70
|
-
* overlay a consumer paints on top will absorb the move before it arrives. Any
|
|
71
|
-
* decorative layer above this wants `pointer-events: none`. It has already
|
|
72
|
-
* caught this repository's own bench once.
|
|
73
|
-
*
|
|
74
|
-
* **The fallback needs no detection.** Where WebGL is refused the canvas never
|
|
75
|
-
* paints and is transparent, and the host carries a gradient of the same tokens
|
|
76
|
-
* underneath. Nothing has to ask whether it worked.
|
|
77
|
-
*
|
|
78
|
-
* Presentation only, so there is no headless tier: the split exists to keep
|
|
79
|
-
* behavior separate from styling, and there is no structure or semantics here
|
|
80
|
-
* to separate. Decorative, so the host is hidden from assistive technology and
|
|
81
|
-
* takes no pointer events.
|
|
82
|
-
*
|
|
83
|
-
* @since 0.1.0
|
|
84
|
-
* @status experimental
|
|
85
|
-
* @category media
|
|
86
|
-
* @aliases Backdrop, Background, Atmosphere, Gradient
|
|
87
|
-
* @documentation https://helix.fusion.dev/components/aura
|
|
88
|
-
*
|
|
89
|
-
* @csspart canvas - The surface the shader draws into.
|
|
90
|
-
*/
|
|
91
17
|
let HelixAura = class HelixAura extends HelixElement {
|
|
92
18
|
constructor() {
|
|
93
19
|
super(...arguments);
|
|
94
|
-
/**
|
|
95
|
-
* How soft the falloff is, `0` tight and `1` diffuse.
|
|
96
|
-
*/
|
|
97
20
|
this.bloom = 0.5;
|
|
98
|
-
/**
|
|
99
|
-
* How far the settings wander on their own, `0` still and `1` restless.
|
|
100
|
-
*
|
|
101
|
-
* **One property rather than a range on each of the five.** A range per axis
|
|
102
|
-
* is five more things to author and four more chances to write a pair that
|
|
103
|
-
* fights another; the reason to want any of it is the same reason in every
|
|
104
|
-
* case, which is that a fixed field is legibly a loop. So this is an amount,
|
|
105
|
-
* and each axis is given its own span and its own period.
|
|
106
|
-
*
|
|
107
|
-
* The periods are irrational multiples of each other, so no two axes come
|
|
108
|
-
* back into phase and the field has no cycle a reader can count. That is the
|
|
109
|
-
* difference between a background that breathes and one that is obviously on
|
|
110
|
-
* a timer.
|
|
111
|
-
*/
|
|
112
21
|
this.drift = 0;
|
|
113
|
-
/**
|
|
114
|
-
* Overall brightness, as a multiplier on the field.
|
|
115
|
-
*/
|
|
116
22
|
this.gain = 1;
|
|
117
|
-
/**
|
|
118
|
-
* Whether the pointer shifts and lifts the field.
|
|
119
|
-
*
|
|
120
|
-
* Reflected because the stylesheet selects on it: a background is not
|
|
121
|
-
* hit-testable at rest, or it would swallow clicks meant for what is over it,
|
|
122
|
-
* and an element that is not hit-testable never receives the `pointermove`
|
|
123
|
-
* this listens for. Asking for the pointer is asking for the hit test.
|
|
124
|
-
*/
|
|
125
23
|
this.interactive = false;
|
|
126
|
-
/**
|
|
127
|
-
* How far the pointer reaches into the field, `0` upward.
|
|
128
|
-
*
|
|
129
|
-
* Multiplied into the presence the pointer already fades in and out with, so
|
|
130
|
-
* it scales the whole response rather than any one part of it, whichever
|
|
131
|
-
* interaction is in play.
|
|
132
|
-
*/
|
|
133
24
|
this.reach = 1;
|
|
134
|
-
/**
|
|
135
|
-
* Feature size, as a multiplier on the sampled coordinates.
|
|
136
|
-
*/
|
|
137
25
|
this.scale = 1;
|
|
138
|
-
/**
|
|
139
|
-
* How fast it drifts, as a multiplier on time.
|
|
140
|
-
*/
|
|
141
26
|
this.speed = 1;
|
|
142
|
-
/**
|
|
143
|
-
* The field's rotation, in degrees.
|
|
144
|
-
*/
|
|
145
27
|
this.tilt = 14;
|
|
146
|
-
/**
|
|
147
|
-
* How much the members of a population differ from one another, `0` a set
|
|
148
|
-
* that is uniform and `1` the resting amount. Clamped to that range: these
|
|
149
|
-
* are interpolations, so a larger number extrapolates rather than
|
|
150
|
-
* exaggerating, and a streak whose length has passed through zero is drawn
|
|
151
|
-
* as a bar across the whole frame.
|
|
152
|
-
*
|
|
153
|
-
* Read by the shapes made of many similar things, whether those are counted
|
|
154
|
-
* or continuous: `streaks`, whose lanes draw a speed, a length, a width, a
|
|
155
|
-
* spacing and a weight apiece; `particles`, whose motes draw a size, a
|
|
156
|
-
* brightness and a wander; and `rays` and `aurora`, whose fan, bundles and
|
|
157
|
-
* folds widen or even out. `braid`, `waves` and `flow` have a
|
|
158
|
-
* single subject and nothing to vary, and `threads` is even on purpose, so on
|
|
159
|
-
* those four this is inert.
|
|
160
|
-
*/
|
|
161
28
|
this.variance = 1;
|
|
162
|
-
/** Repaints when the palette moves, which no re-render would do on its own. */
|
|
163
29
|
this.tokens = new TokenColorController(this, () => {
|
|
164
30
|
this.readPalette();
|
|
165
31
|
this.paintOnce();
|
|
166
32
|
});
|
|
167
|
-
/**
|
|
168
|
-
* The four ramp stops, resolved.
|
|
169
|
-
*
|
|
170
|
-
* **Cached, because reading them is not free and they do not move.** Each
|
|
171
|
-
* read sets a style, calls `getComputedStyle`, fills a pixel and reads it
|
|
172
|
-
* back: a style recalc and a canvas readback apiece. Doing that inside
|
|
173
|
-
* `paint` meant four of each on every frame, forever, to fetch values that
|
|
174
|
-
* change only when the color mode does. The controller already says when
|
|
175
|
-
* that happens, so this is filled from there and from the first paint.
|
|
176
|
-
*/
|
|
177
33
|
this.palette = {
|
|
178
34
|
glow: [0.29, 0.61, 1],
|
|
179
35
|
ground: [0, 0, 0],
|
|
@@ -184,108 +40,22 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
184
40
|
this.gl = null;
|
|
185
41
|
this.program = null;
|
|
186
42
|
this.uniforms = {};
|
|
187
|
-
/** The pending context release, or `0`. Canceled by a reconnection. */
|
|
188
43
|
this.release = 0;
|
|
189
44
|
this.frame = 0;
|
|
190
45
|
this.elapsed = 0;
|
|
191
46
|
this.last = 0;
|
|
192
47
|
this.width = 0;
|
|
193
48
|
this.height = 0;
|
|
194
|
-
/**
|
|
195
|
-
* Where the pointer has been, as flat triples of x, y and age.
|
|
196
|
-
*
|
|
197
|
-
* A ring rather than a queue: the oldest slot is overwritten, so nothing is
|
|
198
|
-
* allocated and the cost is the same on the first second and the thousandth.
|
|
199
|
-
*/
|
|
200
49
|
this.trail = new Float32Array(8 * 3).fill(1);
|
|
201
50
|
this.trailNext = 0;
|
|
202
|
-
/**
|
|
203
|
-
* The same marks, oldest first, which is what the shader is given.
|
|
204
|
-
*
|
|
205
|
-
* The ring's own order is not time order, and the wake is drawn as a polyline
|
|
206
|
-
* through consecutive entries: a segment across the wrap joined the newest
|
|
207
|
-
* mark to the oldest and drew a bright bar clean through the middle of the
|
|
208
|
-
* field. Unrolled once per frame, which is eight copies.
|
|
209
|
-
*/
|
|
210
51
|
this.ordered = new Float32Array(9 * 3).fill(1);
|
|
211
|
-
/**
|
|
212
|
-
* The live end of the stroke, outside the ring.
|
|
213
|
-
*
|
|
214
|
-
* **In the ring it destroyed a mark every time it committed.** The head was
|
|
215
|
-
* the slot at `trailNext`, so advancing wrote over whatever was oldest, and
|
|
216
|
-
* the tail of the stroke lost a point instantly rather than fading. Held
|
|
217
|
-
* apart, committing appends where the head already is: the polyline goes from
|
|
218
|
-
* ending at P to ending at P, P, which is the same shape, and nothing jumps.
|
|
219
|
-
*/
|
|
220
52
|
this.head = { x: 1, y: 1 };
|
|
221
|
-
/**
|
|
222
|
-
* Where the pointer is, and where the field has got to. The gap is weight.
|
|
223
|
-
*
|
|
224
|
-
* `presence`, not `reach`: the eased value is how far the pointer has faded
|
|
225
|
-
* in, and `reach` is the consumer's amplitude. The two multiply.
|
|
226
|
-
*/
|
|
227
53
|
this.target = { presence: 0, x: 0, y: 0 };
|
|
228
54
|
this.eased = { presence: 0, x: 0, y: 0 };
|
|
229
|
-
/**
|
|
230
|
-
* The element's box, cached.
|
|
231
|
-
*
|
|
232
|
-
* **Never read per event.** `getBoundingClientRect` forces a synchronous
|
|
233
|
-
* layout, and a pointer emits events far faster than the display refreshes: a
|
|
234
|
-
* 1000Hz mouse flushed layout a thousand times a second to compute a number
|
|
235
|
-
* that only changes when the page reflows. Refreshed from the resize observer
|
|
236
|
-
* and from scroll, both of which are exactly when it can go stale, and never
|
|
237
|
-
* from the handler that reads it.
|
|
238
|
-
*/
|
|
239
55
|
this.box = { height: 1, left: 0, top: 0, width: 1 };
|
|
240
|
-
/**
|
|
241
|
-
* Whether the field has drawn its opening frame yet.
|
|
242
|
-
*
|
|
243
|
-
* **The box changes shape while a page is arriving, and this shader is scaled
|
|
244
|
-
* by the box.** `p.y` spans [-1, 1] over the element's height, so a feature's
|
|
245
|
-
* size in pixels is proportional to how tall the box happens to be when the
|
|
246
|
-
* frame is drawn. A page that upgrades its custom elements, loads its fonts
|
|
247
|
-
* and settles its images passes through several heights on the way to its
|
|
248
|
-
* final one, and painting each of them draws the same field at several
|
|
249
|
-
* scales: it opens coarse and tightens, which reads as the background zooming
|
|
250
|
-
* out on load rather than as a background.
|
|
251
|
-
*
|
|
252
|
-
* Refusing the degenerate 0 by 0 box leaves the ordinary case: a first real
|
|
253
|
-
* box that is simply not the last one.
|
|
254
|
-
*
|
|
255
|
-
* So the opening frame waits for the box to stop moving. Nothing paints until
|
|
256
|
-
* a measurement has stood unchanged for one animation frame, which is the
|
|
257
|
-
* cheapest definition of settled that is not a guess, and the host's own
|
|
258
|
-
* gradient covers the wait. After that a resize repaints immediately, because
|
|
259
|
-
* by then a change is the window moving rather than the page arriving.
|
|
260
|
-
*/
|
|
261
56
|
this.opened = false;
|
|
262
|
-
/** The pending opening frame, canceled and rescheduled by each new box. */
|
|
263
57
|
this.opening = 0;
|
|
264
|
-
/**
|
|
265
|
-
* When to stop waiting and draw anyway.
|
|
266
|
-
*
|
|
267
|
-
* A page that resizes the field every frame forever would otherwise never get
|
|
268
|
-
* an opening frame at all. Half a second is far longer than a load takes to
|
|
269
|
-
* settle and far shorter than a reader waits before deciding the background is
|
|
270
|
-
* simply a gradient.
|
|
271
|
-
*/
|
|
272
58
|
this.openBy = 0;
|
|
273
|
-
/**
|
|
274
|
-
* Gives the context back, once nothing has claimed the element again.
|
|
275
|
-
*
|
|
276
|
-
* **The context is not released by dropping the reference.** A browser caps
|
|
277
|
-
* live WebGL contexts at about sixteen and drops the oldest when a
|
|
278
|
-
* seventeenth is asked for, so a page that mounts and unmounts auras kills
|
|
279
|
-
* the ones still on screen: the symptom is an aura elsewhere going blank,
|
|
280
|
-
* which looks like a fault in that element and is this one. The context
|
|
281
|
-
* belongs to the canvas, and the canvas is held by the shadow root until the
|
|
282
|
-
* element is collected, which is a moment nothing here controls and which is
|
|
283
|
-
* far too late for a cap of sixteen. `WEBGL_lose_context` is the only way.
|
|
284
|
-
*
|
|
285
|
-
* **It spends the canvas**, which is what `build` is written around: the
|
|
286
|
-
* canvas can never serve a context again and a returning element takes a
|
|
287
|
-
* fresh one.
|
|
288
|
-
*/
|
|
289
59
|
this.drop = () => {
|
|
290
60
|
this.release = 0;
|
|
291
61
|
if (this.gl && this.program) {
|
|
@@ -299,48 +69,13 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
299
69
|
if (!this.last) {
|
|
300
70
|
this.last = now;
|
|
301
71
|
}
|
|
302
|
-
/* Skipped rather than rescheduled on a timer: `requestAnimationFrame` is
|
|
303
|
-
what pauses in a background tab and what matches the display, and a
|
|
304
|
-
`setTimeout` loop would give up both to save the same work. */
|
|
305
72
|
if (!this.interactive && now - this.last < HelixAura_1.CAPPED) {
|
|
306
73
|
this.frame = requestAnimationFrame(this.tick);
|
|
307
74
|
return;
|
|
308
75
|
}
|
|
309
|
-
/* Clamped: a tab returning from the background delivers one enormous delta,
|
|
310
|
-
and the field would teleport to wherever the pointer had gone. */
|
|
311
76
|
const delta = Math.min((now - this.last) / 1000, 0.05);
|
|
312
77
|
this.last = now;
|
|
313
|
-
/* Accumulated rather than measured from a start stamp. A stamp reset on
|
|
314
|
-
every pause means a reader who switched tabs and came back finds the
|
|
315
|
-
field snapped to its opening frame, which is the one thing a background
|
|
316
|
-
must never do. */
|
|
317
78
|
this.elapsed += delta * Math.max(0, this.drifted(this.speed, 0.4, 0.043));
|
|
318
|
-
/* Two time constants, because position and presence are two different jobs
|
|
319
|
-
and one number serves both badly.
|
|
320
|
-
|
|
321
|
-
**Position tracks.** A parallax that lags is not weighty, it is late: at
|
|
322
|
-
85 milliseconds it took about two tenths of a second to close nine tenths
|
|
323
|
-
of the gap, which is well inside what a reader perceives as the page
|
|
324
|
-
being slow rather than the field being heavy.
|
|
325
|
-
|
|
326
|
-
**Presence does not.** `reach` is the effect fading in as the pointer
|
|
327
|
-
arrives and out as it leaves, and that wants to be gradual: snapped on,
|
|
328
|
-
the whole field flinches the instant a cursor crosses the edge.
|
|
329
|
-
|
|
330
|
-
**The position constant scales with the gap, because one constant cannot
|
|
331
|
-
do both jobs.** A fixed 28 milliseconds leaves a moving pointer a
|
|
332
|
-
distance v * t behind it for as long as it keeps moving: about 56 pixels
|
|
333
|
-
at a 2000 pixel per second flick and twice that at twice the speed, which
|
|
334
|
-
is what reads as the field being unable to keep up. Lowering it for
|
|
335
|
-
everyone would give the jitter of a resting hand straight back to the
|
|
336
|
-
shader, which is the only thing the smoothing was ever there for. Jitter
|
|
337
|
-
is small and a flick is large, so the size of the gap is the signal that
|
|
338
|
-
tells them apart: at rest this eases over about 25 milliseconds, a gap of
|
|
339
|
-
one shape unit closes over nine, and two over five.
|
|
340
|
-
|
|
341
|
-
Frame-rate independent, written as a time constant rather than a
|
|
342
|
-
per-frame factor: a factor ties the feel to the refresh rate, so a 120Hz
|
|
343
|
-
display would chase twice as eagerly as a 60Hz one. */
|
|
344
79
|
const drift = Math.hypot(this.target.x - this.eased.x, this.target.y - this.eased.y);
|
|
345
80
|
const follow = 1 - Math.exp(-delta / (0.028 / (1 + drift * 2.4)));
|
|
346
81
|
if (!this.interactive) {
|
|
@@ -348,13 +83,6 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
348
83
|
}
|
|
349
84
|
this.eased.x += (this.target.x - this.eased.x) * follow;
|
|
350
85
|
this.eased.y += (this.target.y - this.eased.y) * follow;
|
|
351
|
-
/* Presence rises fast and falls slowly, which is not symmetry for its own
|
|
352
|
-
sake. A single constant meant the effect took about four tenths of a
|
|
353
|
-
second to reach full strength every time a pointer entered, and that
|
|
354
|
-
ramp is indistinguishable from lag: a reader moves in, nothing much
|
|
355
|
-
happens, and by the time it does they have decided the page is slow.
|
|
356
|
-
Rising in 115 milliseconds it answers immediately; falling over half a
|
|
357
|
-
second it lets go without the field flinching as the cursor crosses out. */
|
|
358
86
|
const rising = this.target.presence > this.eased.presence;
|
|
359
87
|
const fade = 1 - Math.exp(-delta / (rising ? 0.05 : 0.22));
|
|
360
88
|
this.eased.presence += (this.target.presence - this.eased.presence) * fade;
|
|
@@ -385,15 +113,12 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
385
113
|
}
|
|
386
114
|
};
|
|
387
115
|
this.handleScroll = () => {
|
|
388
|
-
/* Only the origin can have moved, and only what the pointer is measured
|
|
389
|
-
against needs it, so this refreshes the cache and draws nothing. */
|
|
390
116
|
const rect = this.getBoundingClientRect();
|
|
391
117
|
this.box = { ...this.box, left: rect.left, top: rect.top };
|
|
392
118
|
};
|
|
393
119
|
}
|
|
394
120
|
static { HelixAura_1 = this; }
|
|
395
121
|
static { this.styles = [styles]; }
|
|
396
|
-
/** What every context is asked for, here rather than at two call sites. */
|
|
397
122
|
static { this.CONTEXT = {
|
|
398
123
|
alpha: true,
|
|
399
124
|
antialias: false,
|
|
@@ -402,34 +127,15 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
402
127
|
stencil: false,
|
|
403
128
|
}; }
|
|
404
129
|
render() {
|
|
405
|
-
/* `aria-hidden` on the canvas rather than on the host, because a consumer
|
|
406
|
-
may want to label the region this sits behind and a hidden host would
|
|
407
|
-
take that with it. The canvas itself never has anything to announce. */
|
|
408
130
|
return html `<canvas part="canvas" aria-hidden="true"></canvas>`;
|
|
409
131
|
}
|
|
410
132
|
firstUpdated() {
|
|
411
133
|
this.start();
|
|
412
134
|
}
|
|
413
|
-
/* Everything the canvas needs, in one place both lifecycles can reach.
|
|
414
|
-
*
|
|
415
|
-
* `firstUpdated` runs once for the life of the element, while
|
|
416
|
-
* `disconnectedCallback` deletes the program, drops the context and
|
|
417
|
-
* disconnects both observers, and runs every time the element moves. Setup
|
|
418
|
-
* that lives only in `firstUpdated` therefore leaves an aura reordered by a
|
|
419
|
-
* framework with `this.gl` null and nothing to rebuild it: a live component
|
|
420
|
-
* painting nothing, for the rest of the page's life, with no error anywhere.
|
|
421
|
-
*
|
|
422
|
-
* Guarded at the call site on `hasUpdated`, because the first connection
|
|
423
|
-
* happens before the first render and there is no canvas yet. */
|
|
424
135
|
start() {
|
|
425
|
-
/* Client only, and by contract. There is no canvas on a server, so the
|
|
426
|
-
shader is an enhancement over markup that already renders a gradient. */
|
|
427
136
|
if (!this.renderRoot.querySelector('canvas')) {
|
|
428
137
|
return;
|
|
429
138
|
}
|
|
430
|
-
/* A reconnection that outran its own release has everything already: the
|
|
431
|
-
context is live and the program is linked, so the reorder costs the
|
|
432
|
-
listeners and the observers and no GL work at all. */
|
|
433
139
|
if (!this.program && !this.build()) {
|
|
434
140
|
return;
|
|
435
141
|
}
|
|
@@ -446,18 +152,8 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
446
152
|
});
|
|
447
153
|
this.visible.observe(this);
|
|
448
154
|
document.addEventListener('visibilitychange', this.handleVisibility);
|
|
449
|
-
/* Scroll moves the box without resizing it, and the cached origin is what
|
|
450
|
-
the pointer is measured against. Passive, because nothing here cancels. */
|
|
451
155
|
window.addEventListener('scroll', this.handleScroll, { capture: true, passive: true });
|
|
452
156
|
this.sized = new ResizeObserver(() => {
|
|
453
|
-
/* **A resize repaints unconditionally rather than asking.**
|
|
454
|
-
`measure` clears the drawing buffer whenever it resizes it, because
|
|
455
|
-
assigning `canvas.width` is a reset and not a hint. `paintOnce` declines
|
|
456
|
-
while the loop holds a frame handle, which is right for a property
|
|
457
|
-
change the loop is about to draw anyway and wrong here: the canvas is
|
|
458
|
-
already transparent by the time it declines, so the fallback gradient
|
|
459
|
-
underneath showed through until the next tick landed. One frame, on
|
|
460
|
-
every reflow that changed the box. */
|
|
461
157
|
if (!this.measure()) {
|
|
462
158
|
this.paintOnce();
|
|
463
159
|
return;
|
|
@@ -475,47 +171,18 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
475
171
|
this.open();
|
|
476
172
|
}
|
|
477
173
|
}
|
|
478
|
-
/**
|
|
479
|
-
* The context and the program, from whatever canvas can still give one.
|
|
480
|
-
*
|
|
481
|
-
* **A canvas whose context was force-lost never yields a working one again.**
|
|
482
|
-
* `getContext` hands back the very same dead object, which reports
|
|
483
|
-
* `isContextLost()` and answers `null` to every `getShaderParameter`, so
|
|
484
|
-
* `link` fails, `start` returns before installing a single observer, and the
|
|
485
|
-
* element sits connected and permanently blind, holding whatever buffer it
|
|
486
|
-
* last painted. Measured on the landing page, where one remove-and-reinsert
|
|
487
|
-
* was enough: the frozen frame was the 2x2 the first `measure` took before
|
|
488
|
-
* layout, stretched across two screens as a gray smear that reads as a broken
|
|
489
|
-
* gradient rather than as a dead component. The only sound in the console was
|
|
490
|
-
* `getShaderInfoLog` answering `null`, twice per element.
|
|
491
|
-
*
|
|
492
|
-
* A spent canvas is therefore replaced rather than re-asked. `restoreContext`
|
|
493
|
-
* is the other way and is not reachable from here: it needs the `webglcontextlost`
|
|
494
|
-
* event default-prevented and the extension held from before the loss, and it
|
|
495
|
-
* restores asynchronously, so `start` would have to become a state machine to
|
|
496
|
-
* use it. A fresh canvas is synchronous and cannot half-succeed.
|
|
497
|
-
*/
|
|
498
174
|
build() {
|
|
499
175
|
let canvas = this.renderRoot.querySelector('canvas');
|
|
500
176
|
if (!canvas) {
|
|
501
177
|
return false;
|
|
502
178
|
}
|
|
503
|
-
/* `antialias` off, because nothing here has a geometric edge to alias:
|
|
504
|
-
every boundary is a gradient the shader computes. Multisampling would
|
|
505
|
-
allocate a second screen-sized buffer to average pixels already smooth.
|
|
506
|
-
`preserveDrawingBuffer` off too, which costs the ability to sample the
|
|
507
|
-
canvas from a test and saves a full-screen copy on every frame. */
|
|
508
179
|
let gl = canvas.getContext('webgl2', HelixAura_1.CONTEXT);
|
|
509
180
|
if (gl?.isContextLost()) {
|
|
510
|
-
/* Swapped under Lit, which is safe because the template is static: the
|
|
511
|
-
strings never change, so no re-render ever touches this node again. */
|
|
512
181
|
const fresh = document.createElement('canvas');
|
|
513
182
|
fresh.setAttribute('part', 'canvas');
|
|
514
183
|
fresh.setAttribute('aria-hidden', 'true');
|
|
515
184
|
canvas.replaceWith(fresh);
|
|
516
185
|
canvas = fresh;
|
|
517
|
-
/* Cleared, so `measure` sizes the new buffer rather than comparing it
|
|
518
|
-
against the spent one's dimensions and returning early. */
|
|
519
186
|
this.width = 0;
|
|
520
187
|
this.height = 0;
|
|
521
188
|
gl = canvas.getContext('webgl2', HelixAura_1.CONTEXT);
|
|
@@ -555,9 +222,6 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
555
222
|
}
|
|
556
223
|
connectedCallback() {
|
|
557
224
|
super.connectedCallback();
|
|
558
|
-
/* A move is a removal followed by an insertion, so the release scheduled a
|
|
559
|
-
moment ago was never about this element going away. Canceled here, which
|
|
560
|
-
is what makes a framework reorder cost no GL work at all. */
|
|
561
225
|
if (this.release) {
|
|
562
226
|
clearTimeout(this.release);
|
|
563
227
|
this.release = 0;
|
|
@@ -567,9 +231,6 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
567
231
|
}
|
|
568
232
|
}
|
|
569
233
|
updated() {
|
|
570
|
-
/* A property change has to reach the screen even when the loop is stopped,
|
|
571
|
-
which is every reader who asked for no motion. The canvas holds its last
|
|
572
|
-
frame and no re-render repaints it. */
|
|
573
234
|
this.paintOnce();
|
|
574
235
|
}
|
|
575
236
|
disconnectedCallback() {
|
|
@@ -583,28 +244,8 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
583
244
|
window.removeEventListener('scroll', this.handleScroll, { capture: true });
|
|
584
245
|
this.sized?.disconnect();
|
|
585
246
|
this.sized = undefined;
|
|
586
|
-
/* **Deferred by a task, and canceled by a reconnection.**
|
|
587
|
-
*
|
|
588
|
-
* The DOM has no signal for a move: a framework reordering its children
|
|
589
|
-
* removes the node and inserts it again, and the element is told only that
|
|
590
|
-
* it was disconnected. Releasing the context there punished every reorder
|
|
591
|
-
* for a departure that never happened, and the landing page reorders three
|
|
592
|
-
* auras on the way in. An insertion in the same task cancels this, so a
|
|
593
|
-
* move is free and a real removal still pays a task later.
|
|
594
|
-
*
|
|
595
|
-
* A task rather than a microtask, because what has to fit inside the window
|
|
596
|
-
* is a framework's whole commit rather than one call. */
|
|
597
247
|
this.release = window.setTimeout(this.drop, 0);
|
|
598
248
|
}
|
|
599
|
-
/**
|
|
600
|
-
* Resolves the ramp. Called when the palette moves, not when a frame draws.
|
|
601
|
-
*
|
|
602
|
-
* Light pages peak on a deep brand blue and dark ones on the foreground, and
|
|
603
|
-
* the ground is what says which: mixing a saturated hue toward near-black
|
|
604
|
-
* takes the chroma out of exactly the pixels carrying the most of it. Read
|
|
605
|
-
* from the ground's own luminance rather than asked of the color mode, which
|
|
606
|
-
* is a private token this tier may not reach for.
|
|
607
|
-
*/
|
|
608
249
|
readPalette() {
|
|
609
250
|
const ground = this.tokens.read('--helix-color-background', [0, 0, 0]);
|
|
610
251
|
const light = ground[0] * 0.2126 + ground[1] * 0.7152 + ground[2] * 0.0722 > 0.5;
|
|
@@ -649,45 +290,13 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
649
290
|
gl.deleteShader(fs);
|
|
650
291
|
return program;
|
|
651
292
|
}
|
|
652
|
-
/**
|
|
653
|
-
* Re-reads the box and resizes the drawing buffer to match.
|
|
654
|
-
*
|
|
655
|
-
* **Called from the observer, never from the frame.** `clientWidth` is a
|
|
656
|
-
* layout read, and doing it in `tick` meant a forced synchronous layout on
|
|
657
|
-
* every single frame to learn a number that changes when the box changes and
|
|
658
|
-
* at no other time. A `ResizeObserver` is told when that is.
|
|
659
|
-
*
|
|
660
|
-
* Returns whether the drawing buffer was resized, which is the same question
|
|
661
|
-
* as whether it was cleared, and so whether the caller owes it a frame.
|
|
662
|
-
*/
|
|
663
293
|
measure() {
|
|
664
|
-
/* Read through `this` rather than destructured, which is not a style
|
|
665
|
-
preference: `noUnusedPrivateClassMembers` does not count a destructured
|
|
666
|
-
read, so a `const { canvas } = this` here is the field's only use and the
|
|
667
|
-
rule offers to delete the field. `TokenColorController` carries the same
|
|
668
|
-
false positive under a suppression; this one costs a word instead. */
|
|
669
294
|
const canvas = this.canvas;
|
|
670
295
|
const gl = this.gl;
|
|
671
296
|
if (!canvas || !gl) {
|
|
672
297
|
return false;
|
|
673
298
|
}
|
|
674
299
|
const rect = this.getBoundingClientRect();
|
|
675
|
-
/* **An empty rect is layout that has not happened, not a box a pixel
|
|
676
|
-
across.** These two were `Math.max(rect.width, 1)`, which never reports a
|
|
677
|
-
bad measurement and instead manufactures a plausible one. Before first
|
|
678
|
-
layout the rect is 0 by 0, so the clamp produced a 1 by 1 box, and every
|
|
679
|
-
line below faithfully computed a drawing buffer for it: two pixels by
|
|
680
|
-
two. The shader then painted four pixels and the compositor stretched
|
|
681
|
-
them over the whole page.
|
|
682
|
-
|
|
683
|
-
That is the settle. The field opened as one enormous soft blob, held it
|
|
684
|
-
until a real box arrived, and snapped to its true feature size, which
|
|
685
|
-
reads as the background scaling down. Instrumented on a reload it logged
|
|
686
|
-
`boxH: 1, bufH: 2` at 216ms with `readyState: "complete"`.
|
|
687
|
-
|
|
688
|
-
Declining is the whole fix. There is nothing worth drawing yet and the
|
|
689
|
-
observer below is already waiting to say when there is; the host's own
|
|
690
|
-
gradient covers the gap, which is what it is for. */
|
|
691
300
|
if (rect.width < 1 || rect.height < 1) {
|
|
692
301
|
return false;
|
|
693
302
|
}
|
|
@@ -697,36 +306,7 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
697
306
|
top: rect.top,
|
|
698
307
|
width: rect.width,
|
|
699
308
|
};
|
|
700
|
-
/* Capped at two, and a little under native beneath that. Every pixel runs
|
|
701
|
-
five octaves of noise through two warps, and a 6K display at its full
|
|
702
|
-
ratio is four times the work for a gradient nobody can resolve. Not as
|
|
703
|
-
far under as it was: a parallax shift resolves on the buffer's own grid,
|
|
704
|
-
so an aggressive scale makes the field step as it slides. */
|
|
705
309
|
let dpr = Math.min(window.devicePixelRatio || 1, 2) * 0.85;
|
|
706
|
-
/* **Then capped again against the box, because a ratio is not a budget.**
|
|
707
|
-
The number above answers "how fine per CSS pixel" and says nothing about
|
|
708
|
-
how many there are, which is fine while a field is a screen or two and
|
|
709
|
-
stops being fine the moment one is asked to light a whole page. Measured
|
|
710
|
-
on the landing page at 1280 by 6330: 2176 by 10761, or 23.4 million
|
|
711
|
-
pixels of five-octave noise every frame, against the 5.3 million the same
|
|
712
|
-
component costs over two screens.
|
|
713
|
-
|
|
714
|
-
Two separate ceilings, because they fail differently.
|
|
715
|
-
|
|
716
|
-
An edge past `MAX_RENDERBUFFER_SIZE` does not render slowly, it does not
|
|
717
|
-
render. WebGL2 guarantees only 2048, and while desktops report 16384 the
|
|
718
|
-
8192 that older integrated parts report is under that same 10761: the
|
|
719
|
-
aura would have been missing outright rather than expensive, and only on
|
|
720
|
-
the machines least able to say so. 4096 clears the guarantee twice over
|
|
721
|
-
and is far more than a field with no edge in it can show.
|
|
722
|
-
|
|
723
|
-
The pixel budget is the fill rate, and 2048 squared is close to what the
|
|
724
|
-
component already cost at the size it was designed for, so a page-length
|
|
725
|
-
field now costs about what a hero-sized one did rather than four of them.
|
|
726
|
-
|
|
727
|
-
Both are applied to `dpr` rather than to the dimensions, so width and
|
|
728
|
-
height come down together. The shader corrects for aspect off
|
|
729
|
-
`uResolution`, and scaling one axis alone would stretch the shape. */
|
|
730
310
|
const edge = Math.max(this.box.width, this.box.height) * dpr;
|
|
731
311
|
if (edge > MAX_CANVAS_EDGE) {
|
|
732
312
|
dpr *= MAX_CANVAS_EDGE / edge;
|
|
@@ -750,11 +330,6 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
750
330
|
}
|
|
751
331
|
paint() {
|
|
752
332
|
const { gl, uniforms } = this;
|
|
753
|
-
/* `width` is zero until a measurement succeeded, and a paint before that is
|
|
754
|
-
a paint into the degenerate buffer described on `measure`. Guarded here
|
|
755
|
-
rather than at each caller because `play` is started from the
|
|
756
|
-
intersection observer and from `visibilitychange` as well as from setup,
|
|
757
|
-
and one of those running early is the whole fault. */
|
|
758
333
|
if (!gl || !this.program || !this.width) {
|
|
759
334
|
return;
|
|
760
335
|
}
|
|
@@ -765,17 +340,11 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
765
340
|
gl.uniform3fv(uniforms.uPeak ?? null, peak);
|
|
766
341
|
gl.uniform1f(uniforms.uTime ?? null, this.elapsed);
|
|
767
342
|
gl.uniform1i(uniforms.uShape ?? null, SHAPES.indexOf(this.variant ?? 'braid'));
|
|
768
|
-
/* Each axis wanders on its own period. The seeds are deliberately not
|
|
769
|
-
simple ratios of each other: two axes on periods that divide would come
|
|
770
|
-
back into phase and give the whole field a beat. */
|
|
771
343
|
gl.uniform1f(uniforms.uTilt ?? null, this.drifted(this.bearing, 14, 0.037));
|
|
772
344
|
gl.uniform1f(uniforms.uScale ?? null, this.drifted(this.scale, 0.32, 0.061));
|
|
773
345
|
gl.uniform1f(uniforms.uBloom ?? null, this.drifted(this.bloom, 0.22, 0.029));
|
|
774
346
|
gl.uniform1f(uniforms.uGain ?? null, this.drifted(this.gain, 0.3, 0.053));
|
|
775
347
|
gl.uniform1f(uniforms.uVariance ?? null, this.variance);
|
|
776
|
-
/* Unrolled oldest first, with the live head appended last. `trailNext` is
|
|
777
|
-
the next slot to be written and so the oldest again, now that the head no
|
|
778
|
-
longer lives in the ring. */
|
|
779
348
|
for (let i = 0; i < 8; i++) {
|
|
780
349
|
const from = ((this.trailNext + i) % 8) * 3;
|
|
781
350
|
const to = i * 3;
|
|
@@ -792,13 +361,6 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
792
361
|
gl.uniform1i(uniforms.uInteraction ?? null, Math.max(0, INTERACTIONS.indexOf(this.interaction ?? 'native')));
|
|
793
362
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
794
363
|
}
|
|
795
|
-
/**
|
|
796
|
-
* The opening frame, one animation frame after the box last changed.
|
|
797
|
-
*
|
|
798
|
-
* Each new measurement cancels the pending frame and books another, so a run
|
|
799
|
-
* of resizes collapses to a single paint at the end of it rather than one per
|
|
800
|
-
* step. `openBy` stops that from being unbounded.
|
|
801
|
-
*/
|
|
802
364
|
open() {
|
|
803
365
|
if (this.opening) {
|
|
804
366
|
cancelAnimationFrame(this.opening);
|
|
@@ -819,37 +381,12 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
819
381
|
}
|
|
820
382
|
this.opening = requestAnimationFrame(draw);
|
|
821
383
|
}
|
|
822
|
-
/** One frame, now, for a change that has to show while the loop is stopped. */
|
|
823
384
|
paintOnce() {
|
|
824
385
|
if (this.frame) {
|
|
825
386
|
return;
|
|
826
387
|
}
|
|
827
388
|
this.paint();
|
|
828
389
|
}
|
|
829
|
-
/**
|
|
830
|
-
* The shortest gap between two painted frames, for a field nobody points at.
|
|
831
|
-
*
|
|
832
|
-
* **A drifting field does not need the display's rate and a pointer does.**
|
|
833
|
-
* `tick` advances on `(now - this.last) / 1000`, so motion is wall-clock: at
|
|
834
|
-
* half the frames the field moves the same distance in the same time, sampled
|
|
835
|
-
* more coarsely. `delta` is already clamped at 0.05, which is a floor of 20
|
|
836
|
-
* frames a second, and 30 sits inside that with room. A cap at 15 would land
|
|
837
|
-
* on the clamp and the field would genuinely drag, which is the failure that
|
|
838
|
-
* would be misdiagnosed as choppiness.
|
|
839
|
-
*
|
|
840
|
-
* **`interactive` is the exception and it is the whole exception.** The
|
|
841
|
-
* element eases `presence` toward the pointer on a 50ms rise constant, so at
|
|
842
|
-
* 33ms a frame the entire response is one or two samples and the follow reads
|
|
843
|
-
* as a lag rather than a lightness. Every field on the landing page leaves
|
|
844
|
-
* `interactive` unset; the only one that sets it is the lab on
|
|
845
|
-
* `/helix/aura`, where it is a toggle a reader chose. So the cap applies where
|
|
846
|
-
* nothing is being followed and lifts where something is.
|
|
847
|
-
*
|
|
848
|
-
* **The cost scales with the count.** Three fields run on the landing page,
|
|
849
|
-
* one per transparent run, and with the frame limiter off they drew 2,654
|
|
850
|
-
* times in three seconds against 1,276 for one: each instance draws once per
|
|
851
|
-
* callback. Capping the two that nobody points at is most of that back.
|
|
852
|
-
*/
|
|
853
390
|
static { this.CAPPED = 1000 / 30; }
|
|
854
391
|
play() {
|
|
855
392
|
if (this.frame ||
|
|
@@ -867,21 +404,8 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
867
404
|
cancelAnimationFrame(this.frame);
|
|
868
405
|
this.frame = 0;
|
|
869
406
|
}
|
|
870
|
-
/* `last` alone is cleared, so the next frame measures no delta against a
|
|
871
|
-
stale stamp. `elapsed` is deliberately kept. */
|
|
872
407
|
this.last = 0;
|
|
873
408
|
}
|
|
874
|
-
/**
|
|
875
|
-
* The angle actually drawn, which is `terminus` resolved against the box.
|
|
876
|
-
*
|
|
877
|
-
* A getter rather than a stored value because the box changes and this has no
|
|
878
|
-
* business knowing when: it is read once per frame from numbers already
|
|
879
|
-
* there.
|
|
880
|
-
*
|
|
881
|
-
* The solve is `tiltFor` in `aura-axis.ts` rather than an expression here, so
|
|
882
|
-
* it is callable and `aura-root.dom.spec.ts` runs it against a model of the
|
|
883
|
-
* shader in both directions.
|
|
884
|
-
*/
|
|
885
409
|
get bearing() {
|
|
886
410
|
if (this.terminus === undefined) {
|
|
887
411
|
return this.tilt;
|
|
@@ -889,13 +413,6 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
889
413
|
const { height, width } = this.box;
|
|
890
414
|
return tiltFor(this.terminus, width / Math.max(height, 1));
|
|
891
415
|
}
|
|
892
|
-
/*
|
|
893
|
-
* One axis, wandering around its set value.
|
|
894
|
-
*
|
|
895
|
-
* Two sines at an irrational ratio rather than one, so a single axis has no
|
|
896
|
-
* period of its own either. Bounded by construction: the weights sum to one,
|
|
897
|
-
* so the value never leaves `base` plus or minus `span`.
|
|
898
|
-
*/
|
|
899
416
|
drifted(base, span, seed) {
|
|
900
417
|
if (!this.drift) {
|
|
901
418
|
return base;
|
|
@@ -904,36 +421,12 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
904
421
|
const wave = Math.sin(t * seed) * 0.6 + Math.sin(t * seed * 1.618 + 2.1) * 0.4;
|
|
905
422
|
return base + span * this.drift * wave;
|
|
906
423
|
}
|
|
907
|
-
/** Ages the wake, and drops a fresh mark where the pointer has moved far. */
|
|
908
424
|
age(delta) {
|
|
909
|
-
/* Two seconds to fade. Long enough that a wake outlives the gesture that
|
|
910
|
-
made it, which is the whole effect, and short enough that a page left
|
|
911
|
-
alone settles rather than keeping a drawing nobody made. */
|
|
912
425
|
for (let i = 2; i < this.trail.length; i += 3) {
|
|
913
426
|
const next = (this.trail[i] ?? 1) + delta / 2;
|
|
914
427
|
this.trail[i] = next > 1 ? 1 : next;
|
|
915
428
|
}
|
|
916
429
|
}
|
|
917
|
-
/**
|
|
918
|
-
* Keeps the head of the wake on the cursor, and commits it as it travels.
|
|
919
|
-
*
|
|
920
|
-
* **The head is provisional, never a committed mark**, and holding it in the
|
|
921
|
-
* ring is two faults at once. Marks are spaced by distance so eight slots
|
|
922
|
-
* cover a gesture rather than the last few milliseconds of one, but if
|
|
923
|
-
* committing is also the only time the stroke grows, its leading end sits up
|
|
924
|
-
* to a full gate behind the pointer and catches up in jumps of exactly that
|
|
925
|
-
* size. Latent, and jagged, for one reason.
|
|
926
|
-
*
|
|
927
|
-
* So the slot at `trailNext` is rewritten every frame to wherever the field
|
|
928
|
-
* has actually eased to, which is where the cursor looks like it is, so the
|
|
929
|
-
* stroke always reaches the pointer. Crossing the gate does not move the head,
|
|
930
|
-
* it just stops the previous position from being overwritten, and a new
|
|
931
|
-
* provisional head starts from there. The wake grows continuously and is still
|
|
932
|
-
* only ever eight points.
|
|
933
|
-
*
|
|
934
|
-
* Measured against `eased` rather than `target`, or the stroke would reach a
|
|
935
|
-
* place the field has not arrived at yet and lead its own parallax.
|
|
936
|
-
*/
|
|
937
430
|
advanceWake() {
|
|
938
431
|
if (!this.interactive) {
|
|
939
432
|
return;
|
|
@@ -951,19 +444,6 @@ let HelixAura = class HelixAura extends HelixElement {
|
|
|
951
444
|
this.trail[at + 1] = this.head.y;
|
|
952
445
|
this.trail[at + 2] = 0;
|
|
953
446
|
this.trailNext = (this.trailNext + 1) % 8;
|
|
954
|
-
/* Every other mark ages a slot's worth on each commit, on top of aging
|
|
955
|
-
with time.
|
|
956
|
-
|
|
957
|
-
Time alone is not enough, because what has to be spent by the moment the
|
|
958
|
-
ring reuses a slot is a function of *distance traveled*, not of seconds:
|
|
959
|
-
marks commit every gate length, so a brisk gesture laps the ring long
|
|
960
|
-
before a two second fade has taken the oldest down, and overwriting a
|
|
961
|
-
mark that is still bright is a pop at the tail.
|
|
962
|
-
|
|
963
|
-
A seventh, not an eighth. A mark is written at nought and is skipped on
|
|
964
|
-
its own commit, so it collects exactly seven increments before its slot
|
|
965
|
-
comes round again. Measured at an eighth, every overwritten slot stood at
|
|
966
|
-
0.875: faint, and not gone, and a faint point vanishing is still a pop. */
|
|
967
447
|
for (let i = 2; i < this.trail.length; i += 3) {
|
|
968
448
|
if (i === at + 2) {
|
|
969
449
|
continue;
|