@velumo/effects 0.1.0-beta.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/dist/effects-css.d.ts +10 -0
- package/dist/effects-css.d.ts.map +1 -0
- package/dist/effects-css.js +110 -0
- package/dist/effects-css.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +127 -0
- package/dist/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +20 -0
- package/src/effects-css.ts +109 -0
- package/src/index.ts +232 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-neutral default visuals for the seven fx:* effects, keyed off
|
|
3
|
+
* `[data-velumo-effect]` and scoped under the DOM renderer. A host on the
|
|
4
|
+
* default theme links this (served by the CLI as effects.css) to get effects
|
|
5
|
+
* out of the box; a bespoke theme (e.g. dark-carnival) ships its own richer
|
|
6
|
+
* effect CSS instead. Strength scales by the `--velumo-effect-intensity`
|
|
7
|
+
* custom property the renderer sets from the layer's intensity.
|
|
8
|
+
*/
|
|
9
|
+
export declare const effectsCss: string;
|
|
10
|
+
//# sourceMappingURL=effects-css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effects-css.d.ts","sourceRoot":"","sources":["../src/effects-css.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,QAoGf,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-neutral default visuals for the seven fx:* effects, keyed off
|
|
3
|
+
* `[data-velumo-effect]` and scoped under the DOM renderer. A host on the
|
|
4
|
+
* default theme links this (served by the CLI as effects.css) to get effects
|
|
5
|
+
* out of the box; a bespoke theme (e.g. dark-carnival) ships its own richer
|
|
6
|
+
* effect CSS instead. Strength scales by the `--velumo-effect-intensity`
|
|
7
|
+
* custom property the renderer sets from the layer's intensity.
|
|
8
|
+
*/
|
|
9
|
+
export const effectsCss = `
|
|
10
|
+
[data-velumo-renderer="dom"] [data-velumo-effect] {
|
|
11
|
+
position: absolute;
|
|
12
|
+
inset: 0;
|
|
13
|
+
pointer-events: none;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="noise"] {
|
|
17
|
+
opacity: var(--velumo-effect-intensity, 0.08);
|
|
18
|
+
background-image: repeating-radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.06) 0, rgba(255, 255, 255, 0.06) 1px, transparent 1px, transparent 2px);
|
|
19
|
+
background-size: 3px 3px;
|
|
20
|
+
mix-blend-mode: overlay;
|
|
21
|
+
animation: velumo-fx-noise 0.6s steps(3) infinite;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="scanlines"] {
|
|
25
|
+
opacity: var(--velumo-effect-intensity, 0.18);
|
|
26
|
+
background-image: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.5) 1px, transparent 1px, transparent 3px);
|
|
27
|
+
mix-blend-mode: multiply;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="vignette"] {
|
|
31
|
+
opacity: var(--velumo-effect-intensity, 0.6);
|
|
32
|
+
background: radial-gradient(circle at center, transparent 45%, rgba(0, 0, 0, 0.55) 100%);
|
|
33
|
+
mix-blend-mode: multiply;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="spotlight-beams"] {
|
|
37
|
+
opacity: var(--velumo-effect-intensity, 0.5);
|
|
38
|
+
background: linear-gradient(115deg, transparent 20%, rgba(255, 255, 255, 0.12) 40%, transparent 60%);
|
|
39
|
+
mix-blend-mode: screen;
|
|
40
|
+
animation: velumo-fx-beams 6s ease-in-out infinite alternate;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="scene-wash"] {
|
|
44
|
+
opacity: var(--velumo-effect-intensity, 0.3);
|
|
45
|
+
background: linear-gradient(135deg, rgba(120, 80, 255, 0.25), transparent 60%);
|
|
46
|
+
mix-blend-mode: screen;
|
|
47
|
+
animation: velumo-fx-wash 9s ease-in-out infinite alternate;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="particles"] {
|
|
51
|
+
opacity: var(--velumo-effect-intensity, 0.5);
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
}
|
|
54
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="particles"]::before {
|
|
55
|
+
content: "";
|
|
56
|
+
position: absolute;
|
|
57
|
+
inset: -50% 0 0 0;
|
|
58
|
+
background-image: radial-gradient(circle, rgba(255, 255, 255, 0.5) 1px, transparent 1.5px);
|
|
59
|
+
background-size: 48px 48px;
|
|
60
|
+
animation: velumo-fx-particles 7s linear infinite;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="code-rain"] {
|
|
64
|
+
opacity: var(--velumo-effect-intensity, 0.5);
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
}
|
|
67
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="code-rain"]::before {
|
|
68
|
+
content: "";
|
|
69
|
+
position: absolute;
|
|
70
|
+
inset: -100% 0 0 0;
|
|
71
|
+
background-image: repeating-linear-gradient(transparent 0 6px, rgba(80, 255, 160, 0.35) 6px 8px);
|
|
72
|
+
background-size: 100% 18px;
|
|
73
|
+
animation: velumo-fx-code-rain 1.2s linear infinite;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@keyframes velumo-fx-noise {
|
|
77
|
+
0% { transform: translate(0, 0); }
|
|
78
|
+
50% { transform: translate(-1px, 1px); }
|
|
79
|
+
100% { transform: translate(1px, -1px); }
|
|
80
|
+
}
|
|
81
|
+
@keyframes velumo-fx-beams {
|
|
82
|
+
from { transform: translateX(-6%) rotate(-1deg); }
|
|
83
|
+
to { transform: translateX(6%) rotate(1deg); }
|
|
84
|
+
}
|
|
85
|
+
@keyframes velumo-fx-wash {
|
|
86
|
+
from { transform: translateY(-2%); }
|
|
87
|
+
to { transform: translateY(2%); }
|
|
88
|
+
}
|
|
89
|
+
@keyframes velumo-fx-particles {
|
|
90
|
+
from { transform: translateY(0); }
|
|
91
|
+
to { transform: translateY(-50%); }
|
|
92
|
+
}
|
|
93
|
+
@keyframes velumo-fx-code-rain {
|
|
94
|
+
from { transform: translateY(0); }
|
|
95
|
+
to { transform: translateY(18px); }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-velumo-renderer="dom"] [data-velumo-effect-motion="reduced"],
|
|
99
|
+
[data-velumo-renderer="dom"] [data-velumo-effect-motion="reduced"]::before {
|
|
100
|
+
animation: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@media (prefers-reduced-motion: reduce) {
|
|
104
|
+
[data-velumo-renderer="dom"] [data-velumo-effect],
|
|
105
|
+
[data-velumo-renderer="dom"] [data-velumo-effect]::before {
|
|
106
|
+
animation: none;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
`.trim();
|
|
110
|
+
//# sourceMappingURL=effects-css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effects-css.js","sourceRoot":"","sources":["../src/effects-css.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoGzB,CAAC,IAAI,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Layer } from "@velumo/core";
|
|
2
|
+
import type { VelumoPlugin, LayerRenderer } from "@velumo/runtime";
|
|
3
|
+
export { effectsCss } from "./effects-css.js";
|
|
4
|
+
export interface EffectTextNode {
|
|
5
|
+
readonly textContent?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface EffectElement {
|
|
8
|
+
readonly ownerDocument: EffectDocument;
|
|
9
|
+
parentNode?: EffectElement | undefined;
|
|
10
|
+
append(...children: ReadonlyArray<EffectElement | EffectTextNode | string>): void;
|
|
11
|
+
appendChild(child: EffectElement | EffectTextNode): EffectElement | EffectTextNode;
|
|
12
|
+
removeChild(child: EffectElement | EffectTextNode): EffectElement | EffectTextNode;
|
|
13
|
+
setAttribute(name: string, value: string): void;
|
|
14
|
+
removeAttribute(name: string): void;
|
|
15
|
+
getAttribute?(name: string): string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface EffectHost extends EffectElement {
|
|
18
|
+
}
|
|
19
|
+
export interface EffectDocument {
|
|
20
|
+
createElement(tagName: string): EffectElement;
|
|
21
|
+
createTextNode?(text: string): EffectTextNode;
|
|
22
|
+
}
|
|
23
|
+
export interface EffectState {
|
|
24
|
+
readonly timeMs?: number;
|
|
25
|
+
readonly intensity?: number;
|
|
26
|
+
readonly reducedMotion?: boolean;
|
|
27
|
+
readonly [key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface EffectInstance {
|
|
30
|
+
mount(host: EffectHost): void;
|
|
31
|
+
update(state: EffectState): void;
|
|
32
|
+
unmount(): void;
|
|
33
|
+
}
|
|
34
|
+
export type EffectLayerType = "fx:noise" | "fx:scanlines" | "fx:vignette" | "fx:particles" | "fx:code-rain" | "fx:spotlight-beams" | "fx:scene-wash";
|
|
35
|
+
export declare const EFFECT_LAYER_TYPES: readonly EffectLayerType[];
|
|
36
|
+
/**
|
|
37
|
+
* The short name of an effect — the `fx:` layer type without its prefix. Derived
|
|
38
|
+
* from {@link EffectLayerType} so the two can never drift: add a new `fx:*` type
|
|
39
|
+
* and `EffectName` (and the default-intensity record below) update with it.
|
|
40
|
+
*/
|
|
41
|
+
export type EffectName = EffectLayerType extends `fx:${infer Name}` ? Name : never;
|
|
42
|
+
export interface EffectOptions {
|
|
43
|
+
readonly intensity?: number;
|
|
44
|
+
readonly id?: string;
|
|
45
|
+
readonly visible?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build an effect layer from a short name, filling a default intensity when none
|
|
49
|
+
* is given. Returns a plain {@link Layer} — the same shape the manifest expects.
|
|
50
|
+
*/
|
|
51
|
+
export declare function effect(name: EffectName, options?: EffectOptions): Layer;
|
|
52
|
+
/**
|
|
53
|
+
* Ergonomic per-effect shortcuts: `fx.noise()`, `fx.spotlightBeams()`, etc. Each
|
|
54
|
+
* is a thin wrapper over {@link effect} using the canonical effect type (not a
|
|
55
|
+
* theme-specific alias), so the namespace stays stable and theme-neutral.
|
|
56
|
+
*/
|
|
57
|
+
export declare const fx: {
|
|
58
|
+
readonly noise: (options?: EffectOptions) => Layer;
|
|
59
|
+
readonly scanlines: (options?: EffectOptions) => Layer;
|
|
60
|
+
readonly vignette: (options?: EffectOptions) => Layer;
|
|
61
|
+
readonly particles: (options?: EffectOptions) => Layer;
|
|
62
|
+
readonly codeRain: (options?: EffectOptions) => Layer;
|
|
63
|
+
readonly spotlightBeams: (options?: EffectOptions) => Layer;
|
|
64
|
+
readonly sceneWash: (options?: EffectOptions) => Layer;
|
|
65
|
+
};
|
|
66
|
+
export interface EffectsPluginOptions {
|
|
67
|
+
readonly prefersReducedMotion?: () => boolean;
|
|
68
|
+
}
|
|
69
|
+
export interface EffectLayerRendererContext {
|
|
70
|
+
readonly layer: Layer;
|
|
71
|
+
createElement(tagName: string): EffectElement;
|
|
72
|
+
}
|
|
73
|
+
export type EffectLayerRenderer = LayerRenderer<EffectLayerRendererContext, EffectElement>;
|
|
74
|
+
export declare function createStubEffect(name: string, prefersReducedMotion: () => boolean): EffectInstance;
|
|
75
|
+
export declare function createEffectsPlugin(options?: EffectsPluginOptions): VelumoPlugin<EffectLayerRendererContext, EffectElement, unknown>;
|
|
76
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export { effectsCss } from "./effects-css.js";
|
|
2
|
+
export const EFFECT_LAYER_TYPES = [
|
|
3
|
+
"fx:noise",
|
|
4
|
+
"fx:scanlines",
|
|
5
|
+
"fx:vignette",
|
|
6
|
+
"fx:particles",
|
|
7
|
+
"fx:code-rain",
|
|
8
|
+
"fx:spotlight-beams",
|
|
9
|
+
"fx:scene-wash",
|
|
10
|
+
];
|
|
11
|
+
const _effectLayerTypeCheck = "fx:noise";
|
|
12
|
+
void _effectLayerTypeCheck;
|
|
13
|
+
/**
|
|
14
|
+
* Sensible baseline intensity per effect so `effect("noise")` just works with no
|
|
15
|
+
* `?? 0.075` in author code. A theme or author can always override via options.
|
|
16
|
+
* `Record<EffectName, number>` forces every known effect to have a default.
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_EFFECT_INTENSITY = {
|
|
19
|
+
noise: 0.08,
|
|
20
|
+
scanlines: 0.18,
|
|
21
|
+
vignette: 0.6,
|
|
22
|
+
particles: 0.5,
|
|
23
|
+
"code-rain": 0.5,
|
|
24
|
+
"spotlight-beams": 0.5,
|
|
25
|
+
"scene-wash": 0.3,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Build an effect layer from a short name, filling a default intensity when none
|
|
29
|
+
* is given. Returns a plain {@link Layer} — the same shape the manifest expects.
|
|
30
|
+
*/
|
|
31
|
+
export function effect(name, options = {}) {
|
|
32
|
+
const { intensity, id, visible } = options;
|
|
33
|
+
return {
|
|
34
|
+
type: `${FX_PREFIX}${name}`,
|
|
35
|
+
props: { intensity: intensity ?? DEFAULT_EFFECT_INTENSITY[name] },
|
|
36
|
+
...(id === undefined ? {} : { id }),
|
|
37
|
+
...(visible === undefined ? {} : { visible }),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Ergonomic per-effect shortcuts: `fx.noise()`, `fx.spotlightBeams()`, etc. Each
|
|
42
|
+
* is a thin wrapper over {@link effect} using the canonical effect type (not a
|
|
43
|
+
* theme-specific alias), so the namespace stays stable and theme-neutral.
|
|
44
|
+
*/
|
|
45
|
+
export const fx = {
|
|
46
|
+
noise: (options) => effect("noise", options),
|
|
47
|
+
scanlines: (options) => effect("scanlines", options),
|
|
48
|
+
vignette: (options) => effect("vignette", options),
|
|
49
|
+
particles: (options) => effect("particles", options),
|
|
50
|
+
codeRain: (options) => effect("code-rain", options),
|
|
51
|
+
spotlightBeams: (options) => effect("spotlight-beams", options),
|
|
52
|
+
sceneWash: (options) => effect("scene-wash", options),
|
|
53
|
+
};
|
|
54
|
+
const PLUGIN_ID = "velumo/effects";
|
|
55
|
+
const FX_PREFIX = "fx:";
|
|
56
|
+
const DATA_EFFECT = "data-velumo-effect";
|
|
57
|
+
const DATA_MOTION = "data-velumo-effect-motion";
|
|
58
|
+
const DATA_TIME = "data-velumo-effect-time";
|
|
59
|
+
const DATA_INTENSITY = "data-velumo-effect-intensity";
|
|
60
|
+
export function createStubEffect(name, prefersReducedMotion) {
|
|
61
|
+
let element;
|
|
62
|
+
let host;
|
|
63
|
+
return {
|
|
64
|
+
mount(target) {
|
|
65
|
+
if (element !== undefined) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const created = target.ownerDocument.createElement("div");
|
|
69
|
+
created.setAttribute(DATA_EFFECT, name);
|
|
70
|
+
const reduced = prefersReducedMotion();
|
|
71
|
+
created.setAttribute(DATA_MOTION, reduced ? "reduced" : "normal");
|
|
72
|
+
target.append(created);
|
|
73
|
+
element = created;
|
|
74
|
+
host = target;
|
|
75
|
+
},
|
|
76
|
+
update(state) {
|
|
77
|
+
if (element === undefined) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (typeof state.timeMs === "number") {
|
|
81
|
+
element.setAttribute(DATA_TIME, String(state.timeMs));
|
|
82
|
+
}
|
|
83
|
+
if (typeof state.intensity === "number") {
|
|
84
|
+
element.setAttribute(DATA_INTENSITY, String(state.intensity));
|
|
85
|
+
}
|
|
86
|
+
if (state.reducedMotion === true) {
|
|
87
|
+
element.setAttribute(DATA_MOTION, "reduced");
|
|
88
|
+
}
|
|
89
|
+
else if (state.reducedMotion === false) {
|
|
90
|
+
element.setAttribute(DATA_MOTION, "normal");
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
unmount() {
|
|
94
|
+
if (element === undefined || host === undefined) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
host.removeChild(element);
|
|
98
|
+
element = undefined;
|
|
99
|
+
host = undefined;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function createEffectsPlugin(options = {}) {
|
|
104
|
+
const prefersReducedMotion = options.prefersReducedMotion ?? (() => false);
|
|
105
|
+
return {
|
|
106
|
+
id: PLUGIN_ID,
|
|
107
|
+
setup({ registry }) {
|
|
108
|
+
for (const layerType of EFFECT_LAYER_TYPES) {
|
|
109
|
+
const name = layerType.slice(FX_PREFIX.length);
|
|
110
|
+
const renderer = (context) => {
|
|
111
|
+
const element = context.createElement("div");
|
|
112
|
+
element.setAttribute(DATA_EFFECT, name);
|
|
113
|
+
const reduced = prefersReducedMotion();
|
|
114
|
+
element.setAttribute(DATA_MOTION, reduced ? "reduced" : "normal");
|
|
115
|
+
const intensity = context.layer.props?.intensity;
|
|
116
|
+
if (Number.isFinite(intensity)) {
|
|
117
|
+
element.setAttribute(DATA_INTENSITY, String(intensity));
|
|
118
|
+
element.setAttribute("style", `--velumo-effect-intensity: ${intensity};`);
|
|
119
|
+
}
|
|
120
|
+
return element;
|
|
121
|
+
};
|
|
122
|
+
registry.registerLayerRenderer(layerType, renderer);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"fileNames":["../../../node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/typescript/lib/lib.es2021.d.ts","../../../node_modules/typescript/lib/lib.es2022.d.ts","../../../node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../../node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../../node_modules/typescript/lib/lib.scripthost.d.ts","../../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../../node_modules/typescript/lib/lib.es2017.date.d.ts","../../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/typescript/lib/lib.es2021.promise.d.ts","../../../node_modules/typescript/lib/lib.es2021.string.d.ts","../../../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../../node_modules/typescript/lib/lib.es2021.intl.d.ts","../../../node_modules/typescript/lib/lib.es2022.array.d.ts","../../../node_modules/typescript/lib/lib.es2022.error.d.ts","../../../node_modules/typescript/lib/lib.es2022.intl.d.ts","../../../node_modules/typescript/lib/lib.es2022.object.d.ts","../../../node_modules/typescript/lib/lib.es2022.string.d.ts","../../../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../../node_modules/typescript/lib/lib.decorators.d.ts","../../../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../../node_modules/typescript/lib/lib.es2022.full.d.ts","../src/effects-css.ts","../../core/dist/types.d.ts","../../core/dist/builders.d.ts","../../core/dist/manifest-schema.d.ts","../../core/dist/layout-validation.d.ts","../../core/dist/capabilities.d.ts","../../core/dist/validation.d.ts","../../core/dist/index.d.ts","../../runtime/dist/controller.d.ts","../../runtime/dist/hash.d.ts","../../runtime/dist/notes.d.ts","../../runtime/dist/registry.d.ts","../../runtime/dist/timeline.d.ts","../../runtime/dist/action-dispatcher.d.ts","../../runtime/dist/cue-dispatcher.d.ts","../../runtime/dist/camera.d.ts","../../runtime/dist/spatial.d.ts","../../runtime/dist/scene-player.d.ts","../../runtime/dist/theme-bundle.d.ts","../../runtime/dist/index.d.ts","../src/index.ts"],"fileIdsList":[[65],[65,66,67,68,69,70],[64,71,83],[71,72,75],[76],[71],[71,75,76,77],[72],[72,73,74,75,76,77,78,79,80,81,82],[71,72],[71,80],[71,76],[71,75]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"d6b1eba8496bdd0eed6fc8a685768fe01b2da4a0388b5fe7df558290bffcf32f","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"2a2de5b9459b3fc44decd9ce6100b72f1b002ef523126c1d3d8b2a4a63d74d78","affectsGlobalScope":true,"impliedFormat":1},{"version":"f13f4b465c99041e912db5c44129a94588e1aafee35a50eab51044833f50b4ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"01a30f9e8582b369075c0808df71121e6855cb06fd8d3d39511d9ebb66405205","impliedFormat":1},{"version":"505b23263a4e9fbdf4761f56e8770c1d9e1e78cab38fe571450519fcf3faf7cb","signature":"fdf95be0e20c1f390544f4969c9befc6b99b064884b1784aaac49c64a1eab202","impliedFormat":99},{"version":"8877805003603477c63696f8af86eb14be659de3027a597617e7a95ca9250a92","impliedFormat":99},{"version":"a2c4bcfeeaf83801774f8c21daf4c18b15fd9da4469e222a6708a58d235bb286","impliedFormat":99},{"version":"163a1497b249fb8ac3eac7710af6df75222a21b20434ec35d4489c85f18a397b","impliedFormat":99},{"version":"b1d091ebe44dc4eb7ba1535c76bcbf15df97a0cde19fda1bbd34e1503df10e95","impliedFormat":99},{"version":"baeb0570658dce9269d3a5dabb44a1e6e11d8712f45750ca80885fd0e2dab38e","impliedFormat":99},{"version":"def8ed8b7980dbad419047252c19812799dbf761e797d624a9a030fb318a27ee","impliedFormat":99},{"version":"09451d1bdb65b1b10cafbccc4c762f9181609881ba078e4aabfbb7c6d4effe17","impliedFormat":99},{"version":"4c26794e2a073e20bf40b3482be4f25fa4d6b83c8d4cdb7e3744d8401eef6c38","impliedFormat":99},{"version":"92692f5dcdd41d6cebb9785ebe83f6631e448d468c7e77f56c2fe6f8f32869ba","impliedFormat":99},{"version":"1093522585079cf706e859f4323d23832ef5b62b04805190bcba06614fc8bdcb","impliedFormat":99},{"version":"165426a72c724ac8b6c5068f64aeeedc976a31cddaf7bdb30689ca0e0e885885","impliedFormat":99},{"version":"9c6738ae625093663883e4694a79bd8d9c8ff79f0d0bb60b8d6e4ab39393b77d","impliedFormat":99},{"version":"f864cf8286ef0798ae6eb2808965e2038c10ffb940b196a3a6e6364fe534115f","impliedFormat":99},{"version":"d6628b767083fceffd23177e8c53e1348d372e5755c2880f8f2e958d7a6dbafc","impliedFormat":99},{"version":"efbeb4d5e4df969ce57b6fe98d6cabe2097cae2e0695b6ef85c77f7a8045e9a3","impliedFormat":99},{"version":"4bbc05996c36885d4da5e1d3b140fd16ee7121a4184843447a3bb47b240e3386","impliedFormat":99},{"version":"0eaeeda7b43eb04e6503115b59ea0fc0dcede7d39dc6651a1ae8ba94e5974b46","impliedFormat":99},{"version":"8cad53853f3c0a8f7391721d625259ba815e33c26139f59e08751fa38c326f6d","impliedFormat":99},{"version":"7af78eb71c547cc0353d89ca54074af736128b01e231ab5662e1300adc0e17fd","impliedFormat":99},{"version":"9df88cd60ae3bfa2e5dc019937507dfaf8ff8c6733b52aaa2a4e8cef0bb65c17","signature":"783f02acb9dc4fd8b8020d3c523a124dd6324b07d9518cfd7b7707990671dfed","impliedFormat":99}],"root":[64,84],"options":{"composite":true,"declaration":true,"declarationMap":true,"module":199,"outDir":"./","rootDir":"../src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9,"tsBuildInfoFile":"./tsconfig.tsbuildinfo"},"referencedMap":[[66,1],[69,1],[71,2],[68,1],[70,1],[84,3],[77,4],[79,5],[72,6],[78,7],[73,8],[83,9],[74,10],[75,6],[81,11],[80,12],[82,13],[76,6]],"latestChangedDtsFile":"./index.d.ts","version":"6.0.3"}
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@velumo/effects",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@velumo/core": "0.0.0",
|
|
15
|
+
"@velumo/runtime": "0.0.0"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -b"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-neutral default visuals for the seven fx:* effects, keyed off
|
|
3
|
+
* `[data-velumo-effect]` and scoped under the DOM renderer. A host on the
|
|
4
|
+
* default theme links this (served by the CLI as effects.css) to get effects
|
|
5
|
+
* out of the box; a bespoke theme (e.g. dark-carnival) ships its own richer
|
|
6
|
+
* effect CSS instead. Strength scales by the `--velumo-effect-intensity`
|
|
7
|
+
* custom property the renderer sets from the layer's intensity.
|
|
8
|
+
*/
|
|
9
|
+
export const effectsCss = `
|
|
10
|
+
[data-velumo-renderer="dom"] [data-velumo-effect] {
|
|
11
|
+
position: absolute;
|
|
12
|
+
inset: 0;
|
|
13
|
+
pointer-events: none;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="noise"] {
|
|
17
|
+
opacity: var(--velumo-effect-intensity, 0.08);
|
|
18
|
+
background-image: repeating-radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.06) 0, rgba(255, 255, 255, 0.06) 1px, transparent 1px, transparent 2px);
|
|
19
|
+
background-size: 3px 3px;
|
|
20
|
+
mix-blend-mode: overlay;
|
|
21
|
+
animation: velumo-fx-noise 0.6s steps(3) infinite;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="scanlines"] {
|
|
25
|
+
opacity: var(--velumo-effect-intensity, 0.18);
|
|
26
|
+
background-image: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.5) 1px, transparent 1px, transparent 3px);
|
|
27
|
+
mix-blend-mode: multiply;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="vignette"] {
|
|
31
|
+
opacity: var(--velumo-effect-intensity, 0.6);
|
|
32
|
+
background: radial-gradient(circle at center, transparent 45%, rgba(0, 0, 0, 0.55) 100%);
|
|
33
|
+
mix-blend-mode: multiply;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="spotlight-beams"] {
|
|
37
|
+
opacity: var(--velumo-effect-intensity, 0.5);
|
|
38
|
+
background: linear-gradient(115deg, transparent 20%, rgba(255, 255, 255, 0.12) 40%, transparent 60%);
|
|
39
|
+
mix-blend-mode: screen;
|
|
40
|
+
animation: velumo-fx-beams 6s ease-in-out infinite alternate;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="scene-wash"] {
|
|
44
|
+
opacity: var(--velumo-effect-intensity, 0.3);
|
|
45
|
+
background: linear-gradient(135deg, rgba(120, 80, 255, 0.25), transparent 60%);
|
|
46
|
+
mix-blend-mode: screen;
|
|
47
|
+
animation: velumo-fx-wash 9s ease-in-out infinite alternate;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="particles"] {
|
|
51
|
+
opacity: var(--velumo-effect-intensity, 0.5);
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
}
|
|
54
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="particles"]::before {
|
|
55
|
+
content: "";
|
|
56
|
+
position: absolute;
|
|
57
|
+
inset: -50% 0 0 0;
|
|
58
|
+
background-image: radial-gradient(circle, rgba(255, 255, 255, 0.5) 1px, transparent 1.5px);
|
|
59
|
+
background-size: 48px 48px;
|
|
60
|
+
animation: velumo-fx-particles 7s linear infinite;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="code-rain"] {
|
|
64
|
+
opacity: var(--velumo-effect-intensity, 0.5);
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
}
|
|
67
|
+
[data-velumo-renderer="dom"] [data-velumo-effect="code-rain"]::before {
|
|
68
|
+
content: "";
|
|
69
|
+
position: absolute;
|
|
70
|
+
inset: -100% 0 0 0;
|
|
71
|
+
background-image: repeating-linear-gradient(transparent 0 6px, rgba(80, 255, 160, 0.35) 6px 8px);
|
|
72
|
+
background-size: 100% 18px;
|
|
73
|
+
animation: velumo-fx-code-rain 1.2s linear infinite;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@keyframes velumo-fx-noise {
|
|
77
|
+
0% { transform: translate(0, 0); }
|
|
78
|
+
50% { transform: translate(-1px, 1px); }
|
|
79
|
+
100% { transform: translate(1px, -1px); }
|
|
80
|
+
}
|
|
81
|
+
@keyframes velumo-fx-beams {
|
|
82
|
+
from { transform: translateX(-6%) rotate(-1deg); }
|
|
83
|
+
to { transform: translateX(6%) rotate(1deg); }
|
|
84
|
+
}
|
|
85
|
+
@keyframes velumo-fx-wash {
|
|
86
|
+
from { transform: translateY(-2%); }
|
|
87
|
+
to { transform: translateY(2%); }
|
|
88
|
+
}
|
|
89
|
+
@keyframes velumo-fx-particles {
|
|
90
|
+
from { transform: translateY(0); }
|
|
91
|
+
to { transform: translateY(-50%); }
|
|
92
|
+
}
|
|
93
|
+
@keyframes velumo-fx-code-rain {
|
|
94
|
+
from { transform: translateY(0); }
|
|
95
|
+
to { transform: translateY(18px); }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-velumo-renderer="dom"] [data-velumo-effect-motion="reduced"],
|
|
99
|
+
[data-velumo-renderer="dom"] [data-velumo-effect-motion="reduced"]::before {
|
|
100
|
+
animation: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@media (prefers-reduced-motion: reduce) {
|
|
104
|
+
[data-velumo-renderer="dom"] [data-velumo-effect],
|
|
105
|
+
[data-velumo-renderer="dom"] [data-velumo-effect]::before {
|
|
106
|
+
animation: none;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
`.trim();
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import type { Layer } from "@velumo/core";
|
|
2
|
+
import type { VelumoPlugin, LayerRenderer } from "@velumo/runtime";
|
|
3
|
+
|
|
4
|
+
export { effectsCss } from "./effects-css.js";
|
|
5
|
+
|
|
6
|
+
export interface EffectTextNode {
|
|
7
|
+
readonly textContent?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface EffectElement {
|
|
11
|
+
readonly ownerDocument: EffectDocument;
|
|
12
|
+
parentNode?: EffectElement | undefined;
|
|
13
|
+
append(
|
|
14
|
+
...children: ReadonlyArray<EffectElement | EffectTextNode | string>
|
|
15
|
+
): void;
|
|
16
|
+
appendChild(
|
|
17
|
+
child: EffectElement | EffectTextNode,
|
|
18
|
+
): EffectElement | EffectTextNode;
|
|
19
|
+
removeChild(
|
|
20
|
+
child: EffectElement | EffectTextNode,
|
|
21
|
+
): EffectElement | EffectTextNode;
|
|
22
|
+
setAttribute(name: string, value: string): void;
|
|
23
|
+
removeAttribute(name: string): void;
|
|
24
|
+
getAttribute?(name: string): string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface EffectHost extends EffectElement {}
|
|
28
|
+
|
|
29
|
+
export interface EffectDocument {
|
|
30
|
+
createElement(tagName: string): EffectElement;
|
|
31
|
+
createTextNode?(text: string): EffectTextNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface EffectState {
|
|
35
|
+
readonly timeMs?: number;
|
|
36
|
+
readonly intensity?: number;
|
|
37
|
+
readonly reducedMotion?: boolean;
|
|
38
|
+
readonly [key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface EffectInstance {
|
|
42
|
+
mount(host: EffectHost): void;
|
|
43
|
+
update(state: EffectState): void;
|
|
44
|
+
unmount(): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type EffectLayerType =
|
|
48
|
+
| "fx:noise"
|
|
49
|
+
| "fx:scanlines"
|
|
50
|
+
| "fx:vignette"
|
|
51
|
+
| "fx:particles"
|
|
52
|
+
| "fx:code-rain"
|
|
53
|
+
| "fx:spotlight-beams"
|
|
54
|
+
| "fx:scene-wash";
|
|
55
|
+
|
|
56
|
+
export const EFFECT_LAYER_TYPES: readonly EffectLayerType[] = [
|
|
57
|
+
"fx:noise",
|
|
58
|
+
"fx:scanlines",
|
|
59
|
+
"fx:vignette",
|
|
60
|
+
"fx:particles",
|
|
61
|
+
"fx:code-rain",
|
|
62
|
+
"fx:spotlight-beams",
|
|
63
|
+
"fx:scene-wash",
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const _effectLayerTypeCheck: EffectLayerType = "fx:noise";
|
|
67
|
+
void _effectLayerTypeCheck;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The short name of an effect — the `fx:` layer type without its prefix. Derived
|
|
71
|
+
* from {@link EffectLayerType} so the two can never drift: add a new `fx:*` type
|
|
72
|
+
* and `EffectName` (and the default-intensity record below) update with it.
|
|
73
|
+
*/
|
|
74
|
+
export type EffectName = EffectLayerType extends `fx:${infer Name}`
|
|
75
|
+
? Name
|
|
76
|
+
: never;
|
|
77
|
+
|
|
78
|
+
export interface EffectOptions {
|
|
79
|
+
readonly intensity?: number;
|
|
80
|
+
readonly id?: string;
|
|
81
|
+
readonly visible?: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Sensible baseline intensity per effect so `effect("noise")` just works with no
|
|
86
|
+
* `?? 0.075` in author code. A theme or author can always override via options.
|
|
87
|
+
* `Record<EffectName, number>` forces every known effect to have a default.
|
|
88
|
+
*/
|
|
89
|
+
const DEFAULT_EFFECT_INTENSITY: Record<EffectName, number> = {
|
|
90
|
+
noise: 0.08,
|
|
91
|
+
scanlines: 0.18,
|
|
92
|
+
vignette: 0.6,
|
|
93
|
+
particles: 0.5,
|
|
94
|
+
"code-rain": 0.5,
|
|
95
|
+
"spotlight-beams": 0.5,
|
|
96
|
+
"scene-wash": 0.3,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Build an effect layer from a short name, filling a default intensity when none
|
|
101
|
+
* is given. Returns a plain {@link Layer} — the same shape the manifest expects.
|
|
102
|
+
*/
|
|
103
|
+
export function effect(name: EffectName, options: EffectOptions = {}): Layer {
|
|
104
|
+
const { intensity, id, visible } = options;
|
|
105
|
+
return {
|
|
106
|
+
type: `${FX_PREFIX}${name}`,
|
|
107
|
+
props: { intensity: intensity ?? DEFAULT_EFFECT_INTENSITY[name] },
|
|
108
|
+
...(id === undefined ? {} : { id }),
|
|
109
|
+
...(visible === undefined ? {} : { visible }),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Ergonomic per-effect shortcuts: `fx.noise()`, `fx.spotlightBeams()`, etc. Each
|
|
115
|
+
* is a thin wrapper over {@link effect} using the canonical effect type (not a
|
|
116
|
+
* theme-specific alias), so the namespace stays stable and theme-neutral.
|
|
117
|
+
*/
|
|
118
|
+
export const fx = {
|
|
119
|
+
noise: (options?: EffectOptions): Layer => effect("noise", options),
|
|
120
|
+
scanlines: (options?: EffectOptions): Layer => effect("scanlines", options),
|
|
121
|
+
vignette: (options?: EffectOptions): Layer => effect("vignette", options),
|
|
122
|
+
particles: (options?: EffectOptions): Layer => effect("particles", options),
|
|
123
|
+
codeRain: (options?: EffectOptions): Layer => effect("code-rain", options),
|
|
124
|
+
spotlightBeams: (options?: EffectOptions): Layer =>
|
|
125
|
+
effect("spotlight-beams", options),
|
|
126
|
+
sceneWash: (options?: EffectOptions): Layer => effect("scene-wash", options),
|
|
127
|
+
} as const;
|
|
128
|
+
|
|
129
|
+
export interface EffectsPluginOptions {
|
|
130
|
+
readonly prefersReducedMotion?: () => boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface EffectLayerRendererContext {
|
|
134
|
+
readonly layer: Layer;
|
|
135
|
+
createElement(tagName: string): EffectElement;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Ward 033 contract alignment: the registered renderer returns a DOM-element-
|
|
139
|
+
// like value (EffectElement) so renderer-dom's #appendCustomLayerResult can
|
|
140
|
+
// append it directly. The Ward 029 EffectInstance / createStubEffect lifecycle
|
|
141
|
+
// API remains exported below as an advanced/lower-level surface for callers
|
|
142
|
+
// (e.g. a future timeline-driven external effect driver) that want explicit
|
|
143
|
+
// mount/update/unmount control.
|
|
144
|
+
export type EffectLayerRenderer = LayerRenderer<
|
|
145
|
+
EffectLayerRendererContext,
|
|
146
|
+
EffectElement
|
|
147
|
+
>;
|
|
148
|
+
|
|
149
|
+
const PLUGIN_ID = "velumo/effects";
|
|
150
|
+
const FX_PREFIX = "fx:";
|
|
151
|
+
|
|
152
|
+
const DATA_EFFECT = "data-velumo-effect";
|
|
153
|
+
const DATA_MOTION = "data-velumo-effect-motion";
|
|
154
|
+
const DATA_TIME = "data-velumo-effect-time";
|
|
155
|
+
const DATA_INTENSITY = "data-velumo-effect-intensity";
|
|
156
|
+
|
|
157
|
+
export function createStubEffect(
|
|
158
|
+
name: string,
|
|
159
|
+
prefersReducedMotion: () => boolean,
|
|
160
|
+
): EffectInstance {
|
|
161
|
+
let element: EffectElement | undefined;
|
|
162
|
+
let host: EffectHost | undefined;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
mount(target: EffectHost): void {
|
|
166
|
+
if (element !== undefined) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const created = target.ownerDocument.createElement("div");
|
|
170
|
+
created.setAttribute(DATA_EFFECT, name);
|
|
171
|
+
const reduced = prefersReducedMotion();
|
|
172
|
+
created.setAttribute(DATA_MOTION, reduced ? "reduced" : "normal");
|
|
173
|
+
target.append(created);
|
|
174
|
+
element = created;
|
|
175
|
+
host = target;
|
|
176
|
+
},
|
|
177
|
+
update(state: EffectState): void {
|
|
178
|
+
if (element === undefined) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (typeof state.timeMs === "number") {
|
|
182
|
+
element.setAttribute(DATA_TIME, String(state.timeMs));
|
|
183
|
+
}
|
|
184
|
+
if (typeof state.intensity === "number") {
|
|
185
|
+
element.setAttribute(DATA_INTENSITY, String(state.intensity));
|
|
186
|
+
}
|
|
187
|
+
if (state.reducedMotion === true) {
|
|
188
|
+
element.setAttribute(DATA_MOTION, "reduced");
|
|
189
|
+
} else if (state.reducedMotion === false) {
|
|
190
|
+
element.setAttribute(DATA_MOTION, "normal");
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
unmount(): void {
|
|
194
|
+
if (element === undefined || host === undefined) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
host.removeChild(element);
|
|
198
|
+
element = undefined;
|
|
199
|
+
host = undefined;
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function createEffectsPlugin(
|
|
205
|
+
options: EffectsPluginOptions = {},
|
|
206
|
+
): VelumoPlugin<EffectLayerRendererContext, EffectElement, unknown> {
|
|
207
|
+
const prefersReducedMotion = options.prefersReducedMotion ?? (() => false);
|
|
208
|
+
return {
|
|
209
|
+
id: PLUGIN_ID,
|
|
210
|
+
setup({ registry }) {
|
|
211
|
+
for (const layerType of EFFECT_LAYER_TYPES) {
|
|
212
|
+
const name = layerType.slice(FX_PREFIX.length);
|
|
213
|
+
const renderer: EffectLayerRenderer = (context) => {
|
|
214
|
+
const element = context.createElement("div");
|
|
215
|
+
element.setAttribute(DATA_EFFECT, name);
|
|
216
|
+
const reduced = prefersReducedMotion();
|
|
217
|
+
element.setAttribute(DATA_MOTION, reduced ? "reduced" : "normal");
|
|
218
|
+
const intensity = context.layer.props?.intensity;
|
|
219
|
+
if (Number.isFinite(intensity)) {
|
|
220
|
+
element.setAttribute(DATA_INTENSITY, String(intensity));
|
|
221
|
+
element.setAttribute(
|
|
222
|
+
"style",
|
|
223
|
+
`--velumo-effect-intensity: ${intensity};`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
return element;
|
|
227
|
+
};
|
|
228
|
+
registry.registerLayerRenderer(layerType, renderer);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
package/tsconfig.json
ADDED