@ev-ry/fx 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE.md +13 -0
- package/QUICKSTART.fa.md +94 -0
- package/README.md +116 -0
- package/assets/fonts/Estedad-OFL.txt +93 -0
- package/assets/vendor/bidi-LICENSE.txt +22 -0
- package/assets/vendor/bidi.min.js +1 -0
- package/assets/vendor/three-LICENSE.txt +21 -0
- package/assets/vendor/three.min.js +7 -0
- package/build-report.json +328 -0
- package/docs/GUIDE.md +125 -0
- package/docs/RELEASE-NOTES.md +40 -0
- package/examples/AnimatedTitle.jsx +21 -0
- package/examples/navigation-away.html +1 -0
- package/examples/navigation.html +38 -0
- package/examples/script.html +2 -0
- package/package.json +57 -0
- package/src/dom-attachment.d.ts +78 -0
- package/src/dom-attachment.js +187 -0
- package/src/dom-auto-reveal.d.ts +17 -0
- package/src/dom-auto-reveal.js +53 -0
- package/src/dom-auto-route.js +25 -0
- package/src/dom-free-bootstrap.js +42 -0
- package/src/dom-free-loader.d.ts +13 -0
- package/src/dom-free-loader.js +12 -0
- package/src/dom-free-script.js +12 -0
- package/src/dom-free.d.ts +16 -0
- package/src/dom-free.js +42 -0
- package/src/dom-image-raster.js +22 -0
- package/src/dom-image-surface.js +109 -0
- package/src/dom-image-swap.js +27 -0
- package/src/dom-once.js +34 -0
- package/src/dom-raster-cache.js +33 -0
- package/src/dom-reveal-boot.js +11 -0
- package/src/dom-reveal.js +75 -0
- package/src/dom-rich-text.js +119 -0
- package/src/dom-surface-font.js +50 -0
- package/src/dom-svg-surface.js +43 -0
- package/src/dom-text-fingerprint.js +27 -0
- package/src/dom-text-runs.js +45 -0
- package/src/dom-text-surface.js +254 -0
- package/src/font-mesh-engine.js +368 -0
- package/src/font-rasterizer.js +65 -0
- package/src/hybrid-text-flow.js +45 -0
- package/src/image-preparation-queue.js +16 -0
- package/src/image-source.js +61 -0
- package/src/image-surface.js +162 -0
- package/src/insertion-range.js +29 -0
- package/src/mesh-generator-core.js +149 -0
- package/src/motion-envelope.js +22 -0
- package/src/motion.js +52 -0
- package/src/native-run-shaping.js +60 -0
- package/src/particle-centers.js +50 -0
- package/src/raster-texture-material.js +79 -0
- package/src/raster-texture-mesh.js +35 -0
- package/src/render-owner.js +63 -0
- package/src/runtime-font-engine.js +242 -0
- package/src/runtime-lifecycle.js +26 -0
- package/src/text-direction.js +25 -0
- package/src/text-edit-effect.js +101 -0
- package/src/text-edit-motions.js +15 -0
- package/src/text-effect-options.js +29 -0
- package/src/text-effect-path.js +61 -0
- package/src/text-mesh-density.js +36 -0
- package/src/text-motion-character-centers.js +74 -0
- package/src/text-motion-contour.js +22 -0
- package/src/text-motion-primitives.js +91 -0
- package/src/text-motion-programs.js +1 -0
- package/src/text-motion-recipes.js +71 -0
- package/src/text-scene.js +272 -0
- package/src/triangle-coverage.js +25 -0
- package/src/triangle-effect.js +194 -0
- package/src/triangle-motion-frame.js +38 -0
- package/src/viewport-clip.js +119 -0
- package/src/viewport-render-owner.js +318 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0-rc.1",
|
|
3
|
+
"effects": [
|
|
4
|
+
"dust-wind",
|
|
5
|
+
"smoke",
|
|
6
|
+
"drifting-snow",
|
|
7
|
+
"melt"
|
|
8
|
+
],
|
|
9
|
+
"files": [
|
|
10
|
+
{
|
|
11
|
+
"file": "src/dom-reveal-boot.js",
|
|
12
|
+
"bytes": 580,
|
|
13
|
+
"gzip": 353
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"file": "src/dom-free.js",
|
|
17
|
+
"bytes": 3047,
|
|
18
|
+
"gzip": 1159
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"file": "src/dom-attachment.js",
|
|
22
|
+
"bytes": 9834,
|
|
23
|
+
"gzip": 3001
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"file": "src/dom-reveal.js",
|
|
27
|
+
"bytes": 6042,
|
|
28
|
+
"gzip": 2310
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"file": "src/dom-svg-surface.js",
|
|
32
|
+
"bytes": 4873,
|
|
33
|
+
"gzip": 1994
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"file": "src/dom-image-surface.js",
|
|
37
|
+
"bytes": 10611,
|
|
38
|
+
"gzip": 3666
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"file": "src/image-surface.js",
|
|
42
|
+
"bytes": 11834,
|
|
43
|
+
"gzip": 4227
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"file": "src/motion-envelope.js",
|
|
47
|
+
"bytes": 886,
|
|
48
|
+
"gzip": 456
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"file": "src/image-source.js",
|
|
52
|
+
"bytes": 4013,
|
|
53
|
+
"gzip": 1776
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"file": "src/raster-texture-mesh.js",
|
|
57
|
+
"bytes": 2592,
|
|
58
|
+
"gzip": 1150
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"file": "src/raster-texture-material.js",
|
|
62
|
+
"bytes": 4981,
|
|
63
|
+
"gzip": 1903
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"file": "src/text-mesh-density.js",
|
|
67
|
+
"bytes": 2147,
|
|
68
|
+
"gzip": 1007
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"file": "src/mesh-generator-core.js",
|
|
72
|
+
"bytes": 8403,
|
|
73
|
+
"gzip": 3519
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"file": "src/font-mesh-engine.js",
|
|
77
|
+
"bytes": 19194,
|
|
78
|
+
"gzip": 5583
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"file": "src/text-direction.js",
|
|
82
|
+
"bytes": 1057,
|
|
83
|
+
"gzip": 619
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"file": "assets/vendor/bidi.min.js",
|
|
87
|
+
"bytes": 11924,
|
|
88
|
+
"gzip": 5753
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"file": "src/triangle-coverage.js",
|
|
92
|
+
"bytes": 1211,
|
|
93
|
+
"gzip": 636
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"file": "src/triangle-effect.js",
|
|
97
|
+
"bytes": 13694,
|
|
98
|
+
"gzip": 4287
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"file": "src/particle-centers.js",
|
|
102
|
+
"bytes": 2936,
|
|
103
|
+
"gzip": 1069
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"file": "src/text-edit-motions.js",
|
|
107
|
+
"bytes": 2068,
|
|
108
|
+
"gzip": 1035
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"file": "src/text-motion-recipes.js",
|
|
112
|
+
"bytes": 4883,
|
|
113
|
+
"gzip": 1895
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"file": "src/text-motion-programs.js",
|
|
117
|
+
"bytes": 3367,
|
|
118
|
+
"gzip": 897
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"file": "src/text-effect-options.js",
|
|
122
|
+
"bytes": 2309,
|
|
123
|
+
"gzip": 1052
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"file": "src/text-effect-path.js",
|
|
127
|
+
"bytes": 2924,
|
|
128
|
+
"gzip": 1132
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"file": "src/text-motion-primitives.js",
|
|
132
|
+
"bytes": 4430,
|
|
133
|
+
"gzip": 1894
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"file": "src/text-motion-contour.js",
|
|
137
|
+
"bytes": 1449,
|
|
138
|
+
"gzip": 666
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"file": "src/triangle-motion-frame.js",
|
|
142
|
+
"bytes": 2124,
|
|
143
|
+
"gzip": 870
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"file": "src/dom-image-raster.js",
|
|
147
|
+
"bytes": 1916,
|
|
148
|
+
"gzip": 870
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"file": "src/image-preparation-queue.js",
|
|
152
|
+
"bytes": 1159,
|
|
153
|
+
"gzip": 573
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"file": "src/dom-image-swap.js",
|
|
157
|
+
"bytes": 3050,
|
|
158
|
+
"gzip": 1225
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"file": "src/dom-once.js",
|
|
162
|
+
"bytes": 3023,
|
|
163
|
+
"gzip": 1143
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"file": "src/render-owner.js",
|
|
167
|
+
"bytes": 3954,
|
|
168
|
+
"gzip": 1456
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"file": "src/viewport-render-owner.js",
|
|
172
|
+
"bytes": 24414,
|
|
173
|
+
"gzip": 7399
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"file": "src/viewport-clip.js",
|
|
177
|
+
"bytes": 6751,
|
|
178
|
+
"gzip": 2611
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"file": "src/dom-text-surface.js",
|
|
182
|
+
"bytes": 18914,
|
|
183
|
+
"gzip": 6011
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"file": "src/hybrid-text-flow.js",
|
|
187
|
+
"bytes": 3717,
|
|
188
|
+
"gzip": 1689
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"file": "src/runtime-font-engine.js",
|
|
192
|
+
"bytes": 12202,
|
|
193
|
+
"gzip": 3758
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"file": "src/font-rasterizer.js",
|
|
197
|
+
"bytes": 3539,
|
|
198
|
+
"gzip": 1322
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"file": "src/native-run-shaping.js",
|
|
202
|
+
"bytes": 2997,
|
|
203
|
+
"gzip": 1269
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"file": "src/text-scene.js",
|
|
207
|
+
"bytes": 13688,
|
|
208
|
+
"gzip": 3998
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"file": "src/motion.js",
|
|
212
|
+
"bytes": 1802,
|
|
213
|
+
"gzip": 691
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"file": "src/text-edit-effect.js",
|
|
217
|
+
"bytes": 7704,
|
|
218
|
+
"gzip": 2580
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"file": "src/insertion-range.js",
|
|
222
|
+
"bytes": 1858,
|
|
223
|
+
"gzip": 667
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"file": "src/text-motion-character-centers.js",
|
|
227
|
+
"bytes": 4688,
|
|
228
|
+
"gzip": 1722
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"file": "src/runtime-lifecycle.js",
|
|
232
|
+
"bytes": 848,
|
|
233
|
+
"gzip": 408
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"file": "src/dom-surface-font.js",
|
|
237
|
+
"bytes": 3246,
|
|
238
|
+
"gzip": 1251
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"file": "src/dom-raster-cache.js",
|
|
242
|
+
"bytes": 1677,
|
|
243
|
+
"gzip": 793
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"file": "src/dom-rich-text.js",
|
|
247
|
+
"bytes": 11269,
|
|
248
|
+
"gzip": 4185
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"file": "src/dom-text-runs.js",
|
|
252
|
+
"bytes": 3091,
|
|
253
|
+
"gzip": 1371
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"file": "src/dom-text-fingerprint.js",
|
|
257
|
+
"bytes": 2060,
|
|
258
|
+
"gzip": 1005
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"file": "src/dom-auto-route.js",
|
|
262
|
+
"bytes": 1884,
|
|
263
|
+
"gzip": 751
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"file": "src/dom-auto-reveal.js",
|
|
267
|
+
"bytes": 3351,
|
|
268
|
+
"gzip": 1426
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"file": "src/dom-free-script.js",
|
|
272
|
+
"bytes": 584,
|
|
273
|
+
"gzip": 357
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"file": "src/dom-free-bootstrap.js",
|
|
277
|
+
"bytes": 2095,
|
|
278
|
+
"gzip": 946
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"file": "assets/vendor/three.min.js",
|
|
282
|
+
"bytes": 651651,
|
|
283
|
+
"gzip": 162725
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"file": "src/dom-free-loader.js",
|
|
287
|
+
"bytes": 593,
|
|
288
|
+
"gzip": 361
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"file": "src/dom-free-loader.d.ts",
|
|
292
|
+
"bytes": 596,
|
|
293
|
+
"gzip": 331
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"file": "src/dom-free.d.ts",
|
|
297
|
+
"bytes": 1259,
|
|
298
|
+
"gzip": 587
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"file": "src/dom-auto-reveal.d.ts",
|
|
302
|
+
"bytes": 710,
|
|
303
|
+
"gzip": 418
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
"file": "src/dom-attachment.d.ts",
|
|
307
|
+
"bytes": 4471,
|
|
308
|
+
"gzip": 1373
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"file": "assets/vendor/three-LICENSE.txt",
|
|
312
|
+
"bytes": 1081,
|
|
313
|
+
"gzip": 666
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"file": "assets/vendor/bidi-LICENSE.txt",
|
|
317
|
+
"bytes": 1071,
|
|
318
|
+
"gzip": 650
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"file": "assets/fonts/Estedad-OFL.txt",
|
|
322
|
+
"bytes": 4388,
|
|
323
|
+
"gzip": 1971
|
|
324
|
+
}
|
|
325
|
+
],
|
|
326
|
+
"bytes": 954714,
|
|
327
|
+
"gzipSum": 274468
|
|
328
|
+
}
|
package/docs/GUIDE.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<!-- Generated by scripts/prepare-free-release.cjs from FREE-PACKAGE.md. -->
|
|
2
|
+
# EV-RY FX Free — integration guide
|
|
3
|
+
|
|
4
|
+
Four fixed behaviors: text enters with wind and exits as smoke; images enter as snow and exit with melt. Triangle particles, 2000ms, native resting. Existing native semantics remain with the host. SVG/icon attachment and simple image swaps are included with the image presets. Editable controls and other effect recipes remain outside this build. Shared text shaping and rendering utilities remain where required by display text.
|
|
5
|
+
|
|
6
|
+
## Script
|
|
7
|
+
|
|
8
|
+
Serve this directory intact at `/thd/`:
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<script src="/thd/src/dom-free-script.js" data-thd-auto defer></script>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The loader uses existing window.THREE or loads the accompanying r158 asset. After the script executes, await `THDFree.ready`; its `instance` is the automatic installation. Without data-thd-auto, call `(await THDFree.ready).create(document, options)` explicitly. This is a script entry with accompanying modules/assets, not a single-file bundle.
|
|
15
|
+
|
|
16
|
+
## Modules
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import {createFree} from '@ev-ry/fx';
|
|
20
|
+
const free = createFree(THREE, document, {auto: false});
|
|
21
|
+
const text = free.attachText(document.querySelector('.title'));
|
|
22
|
+
await text.ready;
|
|
23
|
+
text.play('exit');
|
|
24
|
+
// free.destroy() on page/component unmount.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Provide a compatible THREE namespace; r158 is the version tested here. The package import itself has no page side effects. Copy the assets directory with its relative paths if your bundler does not preserve new URL assets.
|
|
28
|
+
|
|
29
|
+
For explicit initialization from an ES module, use the separate loader entry:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import * as THREE from 'three'; // r158 is the tested version.
|
|
33
|
+
import {loadFree} from '@ev-ry/fx/script';
|
|
34
|
+
const api = await loadFree({THREE, auto: true});
|
|
35
|
+
// api.instance is the automatic installation.
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Importing this entry is inert, including during server-side rendering. Call `loadFree()` only in the browser after mounting the document. Without an injected THREE namespace it reuses `window.THREE` or loads the accompanying asset; keep that asset reachable when bundling. Repeated calls reuse the first `THDFree.ready`, so the first initialization chooses `auto`. For manual ownership, use `auto:false` then `api.create(root, options)`. Loaders do not silently start a second automatic installation.
|
|
39
|
+
|
|
40
|
+
The classic `<script src="…/src/dom-free-script.js">` URL remains unchanged. `@ev-ry/fx/classic-script` resolves that classic asset; it is not an ES-module import entry. Do not use `type="module"` with the classic asset: use `loadFree` instead.
|
|
41
|
+
|
|
42
|
+
Auto selects h1/h2/[data-thd-text] and img[data-thd-image]; data-thd-ignore and interactive/navigation/dialog regions are excluded. Options: auto, textSelector, imageSelector, presentation, threshold, once, intersectionRoot. Call refresh after route/DOM changes. Manual attachment accepts presentation and revealOnView. play/cancel/refresh/stats/destroy are exposed; arbitrary effects are not. Scroll exit hides repeated targets rather than running an exit animation.
|
|
43
|
+
|
|
44
|
+
Preparation is lazy for offscreen targets. Native content may paint before a late script initializes; use the optional early mask below for initial-page entry effects. Unsupported content falls back to native presentation. Cross-origin image canvas restrictions and unsupported rich CSS still apply. This does not claim general CSS reproduction or new physical-device validation.
|
|
45
|
+
|
|
46
|
+
For an offscreen or zero-area image, `ready` can resolve with temporary native reason `Image not visible`; it does not promise a prepared GPU mesh outside the viewport. Image preparation begins near the viewport (roughly half a viewport height ahead). The native image's network loading policy is unchanged. Resize work is coalesced: the current raster follows the new box briefly, then the final crop and rounded corners are rebuilt after about 50ms of quiet, with a roughly 150ms bound during continuous resizing. Existing motion keeps its timeline.
|
|
47
|
+
|
|
48
|
+
Unchanged text reuses validated layout and document-scoped raster pixels. The retained shared pixel cache is bounded at 16MiB; active geometry, textures and referenced pixels are separate, so this is not a total memory limit. Font loading invalidates cached pixels. Unsupported text is shown natively without repeated preparation retries; correct its layout and call `refresh()` to retry. Always destroy attachments on unmount.
|
|
49
|
+
|
|
50
|
+
## First-paint setup: avoid an initial flash
|
|
51
|
+
|
|
52
|
+
Load the small boot script synchronously in the head, before body content (no async/defer). Keep the main loader deferred:
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<head>
|
|
56
|
+
<script src="/thd/src/dom-reveal-boot.js"></script>
|
|
57
|
+
<script src="/thd/src/dom-free-script.js" data-thd-auto defer></script>
|
|
58
|
+
</head>
|
|
59
|
+
<body>
|
|
60
|
+
<h1 data-thd-pending>Hello EV-RY FX</h1>
|
|
61
|
+
<img data-thd-image data-thd-pending src="photo.jpg"
|
|
62
|
+
width="640" height="400" alt="A descriptive caption">
|
|
63
|
+
</body>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`data-thd-pending` only requests initial masking; it does not select or attach an element. Use it on targets that will actually receive entry effects. The mask preserves layout and transfers to the reveal controller during attachment. Never edit the controller-owned `data-thd-reveal` attribute. Specify image dimensions or aspect-ratio to prevent loading-related layout shifts.
|
|
67
|
+
|
|
68
|
+
Without JavaScript the boot mask is never installed. If the engine fails to load, `thd:error` releases the boot mask; an eight-second timer from boot execution also releases unclaimed masks. This timer is a failure fallback, not an animation delay. Late initialization after that timeout cannot guarantee flash-free entry. Once attached, the reveal controller owns its own preparation/error handling. A late-loaded boot script cannot undo an earlier paint. Sites with a restrictive CSP must allow the boot script and its injected style; otherwise omit pre-masking or integrate an equivalent permitted policy.
|
|
69
|
+
|
|
70
|
+
## Reveal, replay and component lifecycle
|
|
71
|
+
|
|
72
|
+
Automatic reveal defaults to `threshold:0` and `once:true`. Threshold is visible area ratio: `0` means a positive intersection, `.5` half, `1` full visibility; an element larger than the viewport may never reach `1`.
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
const free = (await THDFree.ready).create(document, {
|
|
76
|
+
once: false, threshold: 0.5
|
|
77
|
+
}); // Load the script WITHOUT data-thd-auto for this manual setup.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
With `once:false`, a full exit rearms the target and hides it immediately; it does not play the exit effect. Small scroll changes while still intersecting do not rearm. Reentry during unfinished entry resumes the timeline; reentry after completion starts a new entry. Explicit `surface.play('enter')` is a replay request. Use `surface.play('exit')` for animated disappearance.
|
|
81
|
+
|
|
82
|
+
Do not attach manually to an automatically selected element. Use `auto:false` for fully manual ownership, or exclude manual targets from the automatic selectors. Call `free.refresh()` after adding/removing targets; discovery is not a blanket DOM mutation watcher. Destroy the surface or installation on component unmount. In React, initialize after the host exists and return cleanup from the effect; see `examples/AnimatedTitle.jsx`. Its children are a string, not an arbitrary React subtree adapter.
|
|
83
|
+
|
|
84
|
+
Automatic loader installations survive a persisted `pagehide`/`pageshow` round trip (the browser's back/forward cache). Return refreshes the same owner and preserves completed once-only entries; a non-persisted page exit releases it. A manually destroyed installation is never revived. For installations created with `api.create()` or `createFree()`, lifecycle belongs to the host: avoid destroying solely for `pagehide` when `event.persisted` is true, refresh on persisted return, and still destroy on actual component unmount.
|
|
85
|
+
|
|
86
|
+
## Troubleshooting
|
|
87
|
+
|
|
88
|
+
Plain headings that wrap across lines use the existing DOM-measured multi-line text path automatically. A line break caused by available width alone does not require nested spans or a smaller font to animate. Unsupported typography can still retain native fallback.
|
|
89
|
+
|
|
90
|
+
Normal/nowrap HTML whitespace is collapsed for raster text while DOM source offsets are preserved. NBSP and preformatted spacing are not globally trimmed. An initially empty manually attached target can reveal after content arrives; a recoverable unsupported layout can reveal after correction and refresh. Automatic scanning still requires `free.refresh()` to discover a previously empty, unattached target. Recovery does not replay an already completed once-only entry.
|
|
91
|
+
|
|
92
|
+
| Symptom | Check |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| Initial flash | Boot script precedes body paint; target has `data-thd-pending`; no async/defer on boot. |
|
|
95
|
+
| Target stays hidden until fallback | Target matches an attachment selector and is not inside an excluded region. |
|
|
96
|
+
| No entry at full visibility threshold | Target fits inside the intersection root; try a smaller threshold. |
|
|
97
|
+
| Image stays native | Inspect CORS restrictions, image loading and supported CSS; native fallback is intentional. |
|
|
98
|
+
| Effect appears above an unrelated layer | Review zIndex and presentation routing; try local presentation for that target. |
|
|
99
|
+
| Duplicate work after navigation | Destroy old owners, avoid simultaneous auto/manual attachment, then refresh new targets. |
|
|
100
|
+
|
|
101
|
+
See [the Persian quickstart](../QUICKSTART.fa.md), [script example](../examples/script.html) and [phone/history check](../examples/navigation.html). Serve examples over HTTP. The boot helper applies to manual reveal attachment as well; the main loader is not required when using the module API.
|
|
102
|
+
|
|
103
|
+
Build specialization strips unused attachment entry points and effect graphs, with assertions that fail on incompatible source changes. It is not source protection: browser code is inspectable. This Free distribution is MIT licensed; see ../LICENSE and ../NOTICE.md. Third-party notices retain their original terms. The private full-product source and unpublished Pro code are outside this distribution.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## Media additions
|
|
107
|
+
|
|
108
|
+
attachSVG(svg, options) uses snow/melt and native resting, supporting the existing static path/shape SVG subset. Unsupported filters, masks, use, embedded text/images and external paint references retain native fallback. This is not arbitrary SVG support.
|
|
109
|
+
|
|
110
|
+
swapImage(previous, next, {exit:true, waitForExit:false, presentation:'global'}) returns {finished,cancel}. Both images must be unattached. Entry uses snow; optional exit uses melt. waitForExit sequences them. Arbitrary effect selection stays unavailable. The next image takes the previous image's placement on success; previous is hidden. These APIs reuse the main engine.
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
### Native image shape and layering
|
|
114
|
+
|
|
115
|
+
Free global canvas defaults to zIndex:1; createFree accepts zIndex to fit the host's layer convention. A header at z-index 20 now stays above the effect. This is an explicit shared-layer policy, not automatic reconstruction of arbitrary stacking contexts.
|
|
116
|
+
|
|
117
|
+
DOM images are rasterized from their displayed box before meshing, applying supported centered object-fit fill/contain/cover and elliptical/percentage corner radii. The alpha mask travels with particles instead of appearing only on native handoff. Changes in size/fit/radii rebuild that raster. This adds preparation work. Tainted canvas/unsupported layouts retain native fallback. Borders, shadows, arbitrary CSS clipping and complete screenshot equivalence are not claimed.
|
|
118
|
+
|
|
119
|
+
### Default shared canvas (2026-09-14)
|
|
120
|
+
|
|
121
|
+
createFree defaults to auto routing with documentCanvas:true. Ordinary page effects use the bounded document-connected shared canvas; nested scroll containers, fixed/sticky content and explicit positioned stacking contexts route conservatively to local presentation. Force local with presentation:'local'; select legacy fixed shared rendering with presentation:'global',documentCanvas:false. experimentalDocumentCanvas is retained as a compatibility alias. Dialog-root rendering is unchanged. This is not general CSS stacking equivalence.
|
|
122
|
+
|
|
123
|
+
## Package identity
|
|
124
|
+
|
|
125
|
+
EV-RY FX uses the package name `@ev-ry/fx`. `THDFree`, `data-thd-*`, the `/thd/` example deployment directory and existing module filenames remain compatible technical identifiers. The deployment directory can be renamed as long as module/asset paths stay intact.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# EV-RY FX Free 0.1.0-rc.1
|
|
2
|
+
|
|
3
|
+
Public preview release candidate from EV-RY. The Free edition uses the [MIT license](../LICENSE). Package: `@ev-ry/fx`. Try the [live demo](https://kbaghini.github.io/evry-fx/docs/) or browse the [source and examples](https://github.com/kbaghini/evry-fx).
|
|
4
|
+
|
|
5
|
+
The product is now EV-RY FX. Existing `THDFree`, `data-thd-*`, `createFree`, `loadFree` and `src/dom-*` names remain compatible. No behavior or API rename is implied by the branding change.
|
|
6
|
+
|
|
7
|
+
## Included
|
|
8
|
+
|
|
9
|
+
- Automatic intersection reveal for headings, marked display text and marked images.
|
|
10
|
+
- Manual text, image and supported static SVG attachment, plus simple image swaps.
|
|
11
|
+
- Four fixed presets: dust wind / smoke for text, drifting snow / melt for media. Textured triangular particles, a 2000ms timeline and native resting presentation.
|
|
12
|
+
- Classic script loading, explicit `loadFree()` ES-module initialization, TypeScript declarations and integration examples.
|
|
13
|
+
- Document-connected shared canvas with conservative local routing for special layouts.
|
|
14
|
+
|
|
15
|
+
## Reliability work
|
|
16
|
+
|
|
17
|
+
- Early opt-in masking prevents native content flashing before initial reveal, while preserving layout and providing failure recovery.
|
|
18
|
+
- Wrapped headings and ordinary HTML whitespace use browser-measured placement. Initially empty or recoverable unsupported text can resume after valid content/layout and refresh.
|
|
19
|
+
- Reentry during an unfinished reveal preserves its clock. Completed once-only entries are not restarted by minor scrolling or presentation updates.
|
|
20
|
+
- Unsupported content returns to native rendering instead of repeatedly retrying known failures. Repeated local refresh preserves valid attachments.
|
|
21
|
+
- Font loading invalidates shared pixels and refreshes rich-text meshes even when the CSS font name and line metrics stay unchanged.
|
|
22
|
+
- Automatic loader ownership handles browser back/forward-cache returns; manual owners retain explicit lifecycle responsibility.
|
|
23
|
+
- `FreeSurface.cancel()` is required in the declarations, matching the runtime API. The module loader has its own typed export.
|
|
24
|
+
|
|
25
|
+
## Performance work
|
|
26
|
+
|
|
27
|
+
- Validated, unchanged text reuses its layout before the expensive per-character placement pass. Character-frame data is collected only when the selected entry/exit effect needs it.
|
|
28
|
+
- Display-text raster pixels use a document-scoped cache capped at 16MiB. This is a cache limit, not total page or GPU memory.
|
|
29
|
+
- Offscreen images prepare near the viewport. Internal image preparation avoids an intermediate PNG encode/decode, and rapid resize requests are coalesced.
|
|
30
|
+
- Geometry settings, visual quality, preset choices and effect duration are preserved. These changes remove redundant work; they do not promise a universal FPS improvement or eliminate all cold WebGL startup cost.
|
|
31
|
+
|
|
32
|
+
## Validation and limits
|
|
33
|
+
|
|
34
|
+
Chrome desktop regression checks cover actual entry/exit motion, rapid reentry, plain/rich text rebuilding, whitespace and Persian mixed-text cases, fallback recovery, lifecycle cleanup and cache invalidation. Strict TypeScript consumer checks cover source and packaged declarations, cancellation, module-loader exports and rejected Free-only configuration.
|
|
35
|
+
|
|
36
|
+
Earlier document-canvas scrolling tests on physical iPhone and Android devices were reported successful by the user. That is not fresh physical-device acceptance of every optimization in this candidate, nor a guarantee for all browsers, GPUs or websites.
|
|
37
|
+
|
|
38
|
+
The Free build retains native fallback for unsupported content. Complete CSS stacking/clipping reproduction, arbitrary SVG, editable controls, custom effect recipes and dedicated framework component packs are outside this release. Cross-origin media must satisfy browser canvas restrictions. Review the [guide](GUIDE.md) before integration.
|
|
39
|
+
|
|
40
|
+
[Back to the overview](../README.md)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {useEffect, useRef} from 'react';
|
|
2
|
+
|
|
3
|
+
// Load /thd/src/dom-free-script.js before mounting React (without data-thd-auto).
|
|
4
|
+
// React owns the element/text; THD owns only its optional presentation.
|
|
5
|
+
export function AnimatedTitle({children = 'Hello EV-RY FX'}) {
|
|
6
|
+
const element = useRef(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
let cancelled = false, owner;
|
|
9
|
+
const ready = window.THDFree?.ready;
|
|
10
|
+
if (!ready) { console.error('Load the EV-RY FX Free script before mounting React'); return; }
|
|
11
|
+
ready.then(async api => {
|
|
12
|
+
if (cancelled || !element.current) return;
|
|
13
|
+
owner = api.create(element.current, {auto: false});
|
|
14
|
+
const surface = owner.attachText(element.current);
|
|
15
|
+
await surface.ready;
|
|
16
|
+
if (!cancelled) surface.play('enter');
|
|
17
|
+
}).catch(console.error);
|
|
18
|
+
return () => { cancelled = true; owner?.destroy(); };
|
|
19
|
+
}, [children]);
|
|
20
|
+
return <h2 ref={element}>{children}</h2>;
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html lang="fa" dir="rtl"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>بازگشت به آزمون THD</title><style>body{max-width:640px;margin:60px auto;padding:24px;background:#f4f3ee;color:#234a42;font:20px/2 Tahoma,Arial}button{font:inherit;padding:10px 18px;background:white;border:1px solid #8aaba0;border-radius:10px}</style><h1>حالا برگردید</h1><p>با دکمهٔ Back مرورگر، حرکت بازگشت گوشی یا دکمهٔ زیر به صفحهٔ آزمون برگردید. سپس «نمایش عنوان تازه» را امتحان کنید.</p><button onclick="history.back()">بازگشت</button></html>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="fa" dir="rtl">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
5
|
+
<script src="../src/dom-reveal-boot.js"></script>
|
|
6
|
+
<title>THD — بررسی نهایی روی گوشی</title>
|
|
7
|
+
<style>
|
|
8
|
+
*{box-sizing:border-box}body{margin:0;background:#f4f3ee;color:#234a42;font:18px/1.9 Tahoma,Arial}main{max-width:760px;margin:auto;padding:24px}h1{font-size:clamp(30px,7vw,48px);line-height:1.6}h2{font-size:28px}img{display:block;width:100%;height:230px;object-fit:cover;border-radius:18px}a,button{font:inherit;display:inline-block;color:inherit;background:white;border:1px solid #8aaba0;border-radius:10px;padding:9px 16px;text-decoration:none;cursor:pointer}section{padding:40px 0}.space{height:65vh}.note{font-size:15px;color:#526d65}#status{padding:10px 0;min-height:48px}
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body><main>
|
|
12
|
+
<div data-thd-ignore><strong>بررسی نهایی روی گوشی</strong><p class="note">ابتدا متن و تصویر را ببینید، پایین بروید و برگردید. سپس صفحهٔ دوم را باز کنید و با دکمهٔ Back مرورگر برگردید.</p></div>
|
|
13
|
+
<h1 data-thd-pending>جزئیات کوچک، حس بهتر</h1>
|
|
14
|
+
<img data-thd-image data-thd-pending width="640" height="360" alt="طرح سبز و طلایی"
|
|
15
|
+
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='640' height='360'%3E%3Crect width='640' height='360' fill='%23476f64'/%3E%3Ccircle cx='320' cy='180' r='115' fill='%23e3bd88'/%3E%3C/svg%3E">
|
|
16
|
+
<section data-thd-ignore>
|
|
17
|
+
<a href="navigation-away.html">رفتن به صفحهٔ دوم</a>
|
|
18
|
+
<button id="add" type="button">نمایش عنوان تازه</button>
|
|
19
|
+
<p id="status" role="status">در حال آمادهشدن…</p>
|
|
20
|
+
<p class="note">بعد از بازگشت، «نمایش عنوان تازه» باید همچنان افکت اجرا کند. عنوانهای قبلی فقط بار اول افکت دارند.</p>
|
|
21
|
+
</section>
|
|
22
|
+
<div id="new-content"></div>
|
|
23
|
+
<div class="space"></div>
|
|
24
|
+
<section><h2>ادامهٔ یک تجربهٔ روان</h2><p class="note">اسکرول و تغییر جهت گوشی نباید متن یا تصویر را برای همیشه مخفی کند.</p></section>
|
|
25
|
+
</main>
|
|
26
|
+
<script src="../src/dom-free-script.js" data-thd-auto defer></script>
|
|
27
|
+
<script>
|
|
28
|
+
let api,sequence=0;
|
|
29
|
+
function status(){if(api)document.querySelector('#status').textContent=api.instance.stats().disposed?'اتصال غیرفعال است؛ نتیجه را گزارش کنید.':'آمادهٔ آزمون';}
|
|
30
|
+
window.addEventListener('DOMContentLoaded',async()=>{
|
|
31
|
+
api=await THDFree.ready;status();
|
|
32
|
+
document.querySelector('#add').onclick=()=>{
|
|
33
|
+
const title=document.createElement('h2');title.textContent='عنوان تازه '+(++sequence);title.dataset.thdPending='';document.querySelector('#new-content').replaceChildren(title);
|
|
34
|
+
api.instance.refresh();title.scrollIntoView({block:'center'});status();
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
window.addEventListener('pageshow',()=>queueMicrotask(status));
|
|
38
|
+
</script></body></html>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<!doctype html><html lang="fa" dir="rtl"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><script src="../src/dom-reveal-boot.js"></script><title>EV-RY FX Free script example</title><style>body{font:24px Arial;margin:40px}section{margin-top:70vh}</style><h1 data-thd-pending>شروع ساده با THD</h1><section><h2>عنوان بعدی با ورود به دید</h2></section><script src="../src/dom-free-script.js" data-thd-auto defer></script></html>
|
|
2
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ev-ry/fx",
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./src/dom-free.d.ts",
|
|
9
|
+
"import": "./src/dom-free.js"
|
|
10
|
+
},
|
|
11
|
+
"./script": {
|
|
12
|
+
"types": "./src/dom-free-loader.d.ts",
|
|
13
|
+
"import": "./src/dom-free-loader.js"
|
|
14
|
+
},
|
|
15
|
+
"./classic-script": "./src/dom-free-script.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"assets",
|
|
20
|
+
"examples",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"NOTICE.md",
|
|
24
|
+
"QUICKSTART.fa.md",
|
|
25
|
+
"docs/GUIDE.md",
|
|
26
|
+
"docs/RELEASE-NOTES.md",
|
|
27
|
+
"build-report.json"
|
|
28
|
+
],
|
|
29
|
+
"description": "Textured particle effects for existing HTML text, images and SVG. Native when still.",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"author": {
|
|
32
|
+
"name": "Kamran Baghini",
|
|
33
|
+
"url": "https://ev-ry.com"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"animation",
|
|
37
|
+
"particles",
|
|
38
|
+
"webgl",
|
|
39
|
+
"text-effects",
|
|
40
|
+
"image-effects",
|
|
41
|
+
"scroll-reveal",
|
|
42
|
+
"svg",
|
|
43
|
+
"rtl",
|
|
44
|
+
"ev-ry"
|
|
45
|
+
],
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/kbaghini/evry-fx.git"
|
|
52
|
+
},
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/kbaghini/evry-fx/issues"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://kbaghini.github.io/evry-fx/docs/"
|
|
57
|
+
}
|