@wix/interact 2.1.4 → 2.2.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.
Files changed (83) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/cjs/react.js +1 -1
  3. package/dist/cjs/web.js +1 -1
  4. package/dist/cjs/web.js.map +1 -1
  5. package/dist/es/index.js +1 -1
  6. package/dist/es/react.js +2 -2
  7. package/dist/es/web.js +15 -15
  8. package/dist/es/web.js.map +1 -1
  9. package/dist/{index-DHqlFmW8.mjs → index-ByLXasWO.mjs} +491 -485
  10. package/dist/index-ByLXasWO.mjs.map +1 -0
  11. package/dist/index-CzRuJxn8.js +18 -0
  12. package/dist/index-CzRuJxn8.js.map +1 -0
  13. package/dist/tsconfig.build.tsbuildinfo +1 -1
  14. package/dist/types/core/Interact.d.ts +2 -2
  15. package/dist/types/core/InteractionController.d.ts +2 -2
  16. package/dist/types/core/InteractionController.d.ts.map +1 -1
  17. package/dist/types/core/add.d.ts.map +1 -1
  18. package/dist/types/core/css.d.ts.map +1 -1
  19. package/dist/types/handlers/effectHandlers.d.ts +4 -4
  20. package/dist/types/handlers/effectHandlers.d.ts.map +1 -1
  21. package/dist/types/handlers/eventTrigger.d.ts +2 -2
  22. package/dist/types/handlers/eventTrigger.d.ts.map +1 -1
  23. package/dist/types/handlers/index.d.ts.map +1 -1
  24. package/dist/types/handlers/viewEnter.d.ts.map +1 -1
  25. package/dist/types/index.d.ts +1 -1
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/react/index.d.ts +1 -1
  28. package/dist/types/react/index.d.ts.map +1 -1
  29. package/dist/types/types/config.d.ts +47 -0
  30. package/dist/types/types/config.d.ts.map +1 -0
  31. package/dist/types/types/controller.d.ts +34 -0
  32. package/dist/types/types/controller.d.ts.map +1 -0
  33. package/dist/types/types/effects.d.ts +75 -0
  34. package/dist/types/types/effects.d.ts.map +1 -0
  35. package/dist/types/types/external.d.ts +6 -0
  36. package/dist/types/types/external.d.ts.map +1 -0
  37. package/dist/types/types/global.d.ts +11 -0
  38. package/dist/types/types/global.d.ts.map +1 -0
  39. package/dist/types/types/handlers.d.ts +41 -0
  40. package/dist/types/types/handlers.d.ts.map +1 -0
  41. package/dist/types/types/index.d.ts +8 -0
  42. package/dist/types/types/index.d.ts.map +1 -0
  43. package/dist/types/types/internal.d.ts +36 -0
  44. package/dist/types/types/internal.d.ts.map +1 -0
  45. package/dist/types/types/triggers.d.ts +28 -0
  46. package/dist/types/types/triggers.d.ts.map +1 -0
  47. package/dist/types/web/InteractElement.d.ts +2 -2
  48. package/dist/types/web/InteractElement.d.ts.map +1 -1
  49. package/dist/types/web/index.d.ts +1 -1
  50. package/dist/types/web/index.d.ts.map +1 -1
  51. package/docs/api/README.md +2 -3
  52. package/docs/api/functions.md +4 -4
  53. package/docs/api/interact-class.md +2 -3
  54. package/docs/api/interact-element.md +2 -2
  55. package/docs/api/interaction-controller.md +4 -4
  56. package/docs/api/types.md +38 -69
  57. package/docs/examples/README.md +1 -1
  58. package/docs/examples/click-interactions.md +0 -7
  59. package/docs/examples/entrance-animations.md +28 -27
  60. package/docs/examples/list-patterns.md +17 -16
  61. package/docs/guides/conditions-and-media-queries.md +2 -3
  62. package/docs/guides/configuration-structure.md +5 -7
  63. package/docs/guides/effects-and-animations.md +2 -4
  64. package/docs/guides/getting-started.md +0 -1
  65. package/docs/guides/lists-and-dynamic-content.md +10 -9
  66. package/docs/guides/sequences.md +3 -4
  67. package/docs/guides/state-management.md +0 -2
  68. package/docs/guides/understanding-triggers.md +9 -13
  69. package/package.json +2 -2
  70. package/rules/click.md +96 -560
  71. package/rules/full-lean.md +536 -360
  72. package/rules/hover.md +107 -530
  73. package/rules/integration.md +212 -261
  74. package/rules/pointermove.md +154 -1407
  75. package/rules/viewenter.md +128 -863
  76. package/rules/viewprogress.md +88 -322
  77. package/dist/index-DHqlFmW8.mjs.map +0 -1
  78. package/dist/index-DYEvpIGz.js +0 -18
  79. package/dist/index-DYEvpIGz.js.map +0 -1
  80. package/dist/types/types.d.ts +0 -256
  81. package/dist/types/types.d.ts.map +0 -1
  82. package/rules/MASTER-CLEANUP-PLAN.md +0 -286
  83. package/rules/scroll-list.md +0 -748
@@ -1,748 +0,0 @@
1
- # Scroll List Animation Rules for @wix/interact
2
-
3
- Scroll-driven list animations using `@wix/interact`. Sticky hierarchy: **container** → **items** → **content**. Use `key` for container/item; use `selector` for content within an item.
4
-
5
- ## Rule 1: Sticky Container List Animations with Named Effects
6
-
7
- **Use Case**: Sticky list containers with named effects (horizontal galleries, parallax backgrounds). Use `contain` range—animations run while the element is stuck in position.
8
-
9
- **When to Apply**: Sticky container sliding, parallax, background transformations.
10
-
11
- **Pattern**:
12
-
13
- ```typescript
14
- {
15
- key: '[CONTAINER_KEY]',
16
- trigger: 'viewProgress',
17
- effects: [
18
- {
19
- key: '[CONTAINER_KEY]',
20
- namedEffect: {
21
- type: '[CONTAINER_NAMED_EFFECT]'
22
- },
23
- rangeStart: { name: 'contain', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
24
- rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
25
- easing: 'linear',
26
- effectId: '[UNIQUE_EFFECT_ID]'
27
- }
28
- ]
29
- }
30
- ```
31
-
32
- **Variables**: `[CONTAINER_KEY]`, `[CONTAINER_NAMED_EFFECT]` ('BgParallax', 'PanScroll', 'MoveScroll', 'ParallaxScroll', 'BgPan', 'BgZoom', 'BgFade', 'BgReveal'), `[START_PERCENTAGE]`/`[END_PERCENTAGE]` (typically 0/100), `[UNIQUE_EFFECT_ID]`.
33
-
34
- **Example - Horizontal Sliding Gallery Container**:
35
-
36
- ```typescript
37
- {
38
- key: 'gallery-container',
39
- trigger: 'viewProgress',
40
- effects: [
41
- {
42
- key: 'gallery-container',
43
- namedEffect: {
44
- type: 'PanScroll'
45
- },
46
- rangeStart: { name: 'contain', offset: { unit: 'percentage', value: 0 } },
47
- rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: 100 } },
48
- easing: 'linear',
49
- effectId: 'gallery-slide'
50
- }
51
- ]
52
- }
53
- ```
54
-
55
- **Example - Parallax Container Background**:
56
-
57
- ```typescript
58
- {
59
- key: 'sticky-list-wrapper',
60
- trigger: 'viewProgress',
61
- effects: [
62
- {
63
- key: 'list-background',
64
- namedEffect: {
65
- type: 'BgParallax'
66
- },
67
- rangeStart: { name: 'contain', offset: { unit: 'percentage', value: 0 } },
68
- rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: 100 } },
69
- easing: 'linear',
70
- effectId: 'bg-parallax'
71
- }
72
- ]
73
- }
74
- ```
75
-
76
- ---
77
-
78
- ## Rule 2: Sticky Item List Animations with Named Effects
79
-
80
- **Use Case**: Individual sticky list items with named effects for entrance/exit (progressive reveals, item transformations).
81
-
82
- **When to Apply**: Item entrance/exit during sticky phases, progressive item reveals.
83
-
84
- **Pattern**:
85
-
86
- ```typescript
87
- {
88
- key: '[ITEM_KEY]',
89
- trigger: 'viewProgress',
90
- effects: [
91
- {
92
- key: '[ITEM_KEY]',
93
- namedEffect: {
94
- type: '[ITEM_NAMED_EFFECT]'
95
- },
96
- rangeStart: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
97
- rangeEnd: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
98
- easing: '[EASING_FUNCTION]',
99
- effectId: '[UNIQUE_EFFECT_ID]'
100
- }
101
- ]
102
- }
103
- ```
104
-
105
- **Variables**:
106
-
107
- - `[ITEM_KEY]`: Individual list item identifier
108
- - `[ITEM_NAMED_EFFECT]`: Item-level scroll effects from @wix/motion-presets:
109
- - **Reveal/Fade**: 'FadeScroll', 'BlurScroll', 'RevealScroll', 'ShapeScroll', 'ShuttersScroll'
110
- - **Movement**: 'MoveScroll', 'SlideScroll', 'PanScroll', 'SkewPanScroll'
111
- - **Scale**: 'GrowScroll', 'ShrinkScroll', 'StretchScroll'
112
- - **Rotation**: 'SpinScroll', 'FlipScroll', 'TiltScroll', 'TurnScroll'
113
- - **3D**: 'ArcScroll', 'Spin3dScroll'
114
- - `[START_PERCENTAGE]`: Range start percentage (0-100)
115
- - `[END_PERCENTAGE]`: Range end percentage (0-100)
116
- - `[EASING_FUNCTION]`: Timing function
117
-
118
- **Example - Item Entrance Reveal**:
119
-
120
- ```typescript
121
- {
122
- key: 'list-item',
123
- trigger: 'viewProgress',
124
- effects: [
125
- {
126
- key: 'list-item',
127
- namedEffect: {
128
- type: 'RevealScroll',
129
- direction: 'bottom'
130
- },
131
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
132
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 60 } },
133
- easing: 'ease-out',
134
- effectId: 'item-reveal'
135
- }
136
- ]
137
- }
138
- ```
139
-
140
- **Example - Item Scale During Sticky**:
141
-
142
- ```typescript
143
- {
144
- key: 'sticky-list-item',
145
- trigger: 'viewProgress',
146
- effects: [
147
- {
148
- key: 'sticky-list-item',
149
- namedEffect: {
150
- type: 'GrowScroll'
151
- },
152
- rangeStart: { name: 'contain', offset: { unit: 'percentage', value: 0 } },
153
- rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: 50 } },
154
- easing: 'ease-in-out',
155
- effectId: 'item-grow'
156
- }
157
- ]
158
- }
159
- ```
160
-
161
- ---
162
-
163
- ## Rule 3: Sticky Item List Content Animations with Named Effects
164
-
165
- **Use Case**: Content within sticky items; each item is the viewProgress trigger (text reveals in cards, image animations, progressive disclosure). Use `key` for the item, `selector` for content within.
166
-
167
- **When to Apply**: Content within sticky items, staggered content reveals, text/image animations inside list items.
168
-
169
- **Pattern**:
170
-
171
- ```typescript
172
- {
173
- key: '[ITEM_CONTAINER_KEY]',
174
- trigger: 'viewProgress',
175
- effects: [
176
- {
177
- key: '[CONTENT_KEY]',
178
- namedEffect: {
179
- type: '[CONTENT_NAMED_EFFECT]'
180
- },
181
- rangeStart: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
182
- rangeEnd: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
183
- easing: '[EASING_FUNCTION]',
184
- effectId: '[UNIQUE_EFFECT_ID]'
185
- }
186
- ]
187
- }
188
- ```
189
-
190
- **Variables**:
191
-
192
- - `[ITEM_CONTAINER_KEY]` / `[CONTENT_KEY]`: Item and content identifiers. Use `selector` (e.g. `selector: '.content-text'`) for content within the item.
193
- - `[CONTENT_NAMED_EFFECT]`: Content-level scroll effects from @wix/motion-presets:
194
- - **Opacity/Visibility**: 'FadeScroll', 'BlurScroll'
195
- - **Reveal**: 'RevealScroll', 'ShapeScroll', 'ShuttersScroll'
196
- - **3D Transforms**: 'TiltScroll', 'FlipScroll', 'ArcScroll', 'TurnScroll', 'Spin3dScroll'
197
- - **Movement**: 'MoveScroll', 'SlideScroll'
198
- - **Scale**: 'GrowScroll', 'ShrinkScroll'
199
-
200
- **Example - Staggered Text Content Reveal**:
201
-
202
- ```typescript
203
- {
204
- key: 'list-item-1',
205
- trigger: 'viewProgress',
206
- effects: [
207
- {
208
- key: 'list-item-1',
209
- selector: '.content-text',
210
- namedEffect: {
211
- type: 'FadeScroll'
212
- },
213
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 20 } },
214
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 80 } },
215
- easing: 'ease-out',
216
- effectId: 'text-reveal-1'
217
- }
218
- ]
219
- },
220
- {
221
- key: 'list-item-2',
222
- trigger: 'viewProgress',
223
- effects: [
224
- {
225
- key: 'list-item-2',
226
- selector: '.content-text',
227
- namedEffect: {
228
- type: 'FadeScroll'
229
- },
230
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 20 } },
231
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 80 } },
232
- easing: 'ease-out',
233
- effectId: 'text-reveal-2'
234
- }
235
- ]
236
- }
237
- ```
238
-
239
- **Example - Image Animation Within List Item**:
240
-
241
- ```typescript
242
- {
243
- key: 'product-card',
244
- trigger: 'viewProgress',
245
- effects: [
246
- {
247
- key: 'product-card',
248
- selector: '.hero-image',
249
- namedEffect: {
250
- type: 'RevealScroll'
251
- },
252
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
253
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 50 } },
254
- easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
255
- effectId: 'product-image-reveal'
256
- }
257
- ]
258
- }
259
- ```
260
-
261
- ---
262
-
263
- ## Rule 4: List Container Keyframe Animations
264
-
265
- **Use Case**: Custom container keyframe effects for sticky containers (multi-property transforms, complex backgrounds).
266
-
267
- **When to Apply**: Custom container effects not available in named effects.
268
-
269
- **Pattern**:
270
-
271
- ```typescript
272
- {
273
- key: '[CONTAINER_KEY]',
274
- trigger: 'viewProgress',
275
- effects: [
276
- {
277
- key: '[CONTAINER_KEY]',
278
- keyframeEffect: {
279
- name: '[UNIQUE_KEYFRAME_EFFECT_NAME]',
280
- keyframes: [
281
- { [CSS_PROPERTY_1]: '[START_VALUE_1]', [CSS_PROPERTY_2]: '[START_VALUE_2]', [CSS_PROPERTY_3]: '[START_VALUE_3]' },
282
- { [CSS_PROPERTY_1]: '[MID_VALUE_1]' },
283
- { [CSS_PROPERTY_1]: '[END_VALUE_1]', [CSS_PROPERTY_2]: '[END_VALUE_2]', [CSS_PROPERTY_3]: '[END_VALUE_3]' }
284
- ]
285
- },
286
- rangeStart: { name: 'contain', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
287
- rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
288
- easing: 'linear',
289
- fill: 'both',
290
- effectId: '[UNIQUE_EFFECT_ID]'
291
- }
292
- ]
293
- }
294
- ```
295
-
296
- **Variables**: `[CONTAINER_KEY]`, `[UNIQUE_KEYFRAME_EFFECT_NAME]` (or `[UNIQUE_EFFECT_ID]`). Other variables same as Rule 1.
297
-
298
- **Example - Multi-Property Container Animation**:
299
-
300
- ```typescript
301
- {
302
- key: 'feature-list-container',
303
- trigger: 'viewProgress',
304
- effects: [
305
- {
306
- key: 'feature-list-container',
307
- keyframeEffect: {
308
- name: 'container-slide',
309
- keyframes: [
310
- { transform: 'translateX(0)', filter: 'brightness(1)', backgroundColor: 'rgb(255 255 255 / 0)' },
311
- { transform: 'translateX(-50%)', filter: 'brightness(1.2)', backgroundColor: 'rgb(255 255 255 / 0.1)' },
312
- { transform: 'translateX(-100%)', filter: 'brightness(1)', backgroundColor: 'rgb(255 255 255 / 0)' }
313
- ]
314
- },
315
- rangeStart: { name: 'contain', offset: { unit: 'percentage', value: 0 } },
316
- rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: 100 } },
317
- easing: 'linear',
318
- fill: 'both',
319
- effectId: 'container-slide'
320
- }
321
- ]
322
- }
323
- ```
324
-
325
- **Example - Container Background Transformation**:
326
-
327
- ```typescript
328
- {
329
- key: 'gallery-wrapper',
330
- trigger: 'viewProgress',
331
- effects: [
332
- {
333
- key: 'gallery-background',
334
- keyframeEffect: {
335
- name: 'bg-transform',
336
- keyframes: [
337
- { transform: 'scale(1.1) rotate(6deg)', opacity: '0.8', filter: 'hue-rotate(30deg)' },
338
- { transform: 'scale(1) rotate(0deg)', opacity: '1', filter: 'hue-rotate(0deg)' }
339
- ]
340
- },
341
- rangeStart: { name: 'contain', offset: { unit: 'percentage', value: 0 } },
342
- rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: 100 } },
343
- easing: 'linear',
344
- fill: 'both',
345
- effectId: 'bg-transform'
346
- }
347
- ]
348
- }
349
- ```
350
-
351
- ---
352
-
353
- ## Rule 5: List Item Keyframe Entrance/Exit Animations
354
-
355
- **Use Case**: Custom keyframe entrance/exit for list items (complex reveals, dismissals).
356
-
357
- **When to Apply**: Complex item entrance effects beyond named effects, coordinating item wrapper with content animations.
358
-
359
- **Pattern**:
360
-
361
- ```typescript
362
- {
363
- key: '[ITEM_KEY]',
364
- trigger: 'viewProgress',
365
- effects: [
366
- {
367
- key: '[ITEM_KEY]',
368
- keyframeEffect: {
369
- name: '[UNIQUE_KEYFRAME_EFFECT_NAME]',
370
- keyframes: [
371
- { [CSS_PROPERTY_1]: '[START_VALUE_1]', [CSS_PROPERTY_2]: '[START_VALUE_2]' },
372
- { [CSS_PROPERTY_1]: '[MID_VALUE_1]' },
373
- { [CSS_PROPERTY_1]: '[END_VALUE_1]', [CSS_PROPERTY_2]: '[END_VALUE_2]' }
374
- ]
375
- },
376
- rangeStart: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
377
- rangeEnd: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
378
- easing: '[EASING_FUNCTION]',
379
- fill: 'both',
380
- effectId: '[UNIQUE_EFFECT_ID]'
381
- }
382
- ]
383
- }
384
- ```
385
-
386
- **Variables**: `[ITEM_KEY]`, `[EASING_FUNCTION]`. Other variables same as Rule 4.
387
-
388
- **Example - Complex Item Entrance**:
389
-
390
- ```typescript
391
- {
392
- key: 'timeline-item',
393
- trigger: 'viewProgress',
394
- effects: [
395
- {
396
- key: 'timeline-item',
397
- keyframeEffect: {
398
- name: 'timeline-entrance',
399
- keyframes: [
400
- { opacity: '0', transform: 'translateY(100px) scale(0.8) rotate(5deg)', filter: 'blur(10px)', boxShadow: '0 0 0 rgb(0 0 0 / 0)' },
401
- { opacity: '0.5', transform: 'translateY(20px) scale(0.95) rotate(1deg)', filter: 'blur(2px)', boxShadow: '0 10px 20px rgb(0 0 0 / 0.1)' },
402
- { opacity: '1', transform: 'translateY(0) scale(1) rotate(0deg)', filter: 'blur(0)', boxShadow: '0 20px 40px rgb(0 0 0 / 0.15)' }
403
- ]
404
- },
405
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
406
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 80 } },
407
- easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
408
- fill: 'both',
409
- effectId: 'timeline-entrance'
410
- }
411
- ]
412
- }
413
- ```
414
-
415
- **Example - Item Exit Sequence**:
416
-
417
- ```typescript
418
- {
419
- key: 'card-item',
420
- trigger: 'viewProgress',
421
- effects: [
422
- {
423
- key: 'card-item',
424
- keyframeEffect: {
425
- name: 'card-exit-6',
426
- keyframes: [
427
- { opacity: '1', transform: 'scale(1) rotate(0deg)', filter: 'brightness(1)' },
428
- { opacity: '0.7', transform: 'scale(0.9) rotate(-2deg)', filter: 'brightness(0.8)' },
429
- { opacity: '0', transform: 'scale(0.8) rotate(-5deg)', filter: 'brightness(0.6)' }
430
- ]
431
- },
432
- rangeStart: { name: 'exit', offset: { unit: 'percentage', value: 20 } },
433
- rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } },
434
- easing: 'ease-in',
435
- fill: 'both',
436
- effectId: 'card-exit'
437
- }
438
- ]
439
- }
440
- ```
441
-
442
- ---
443
-
444
- ## Rule 6: Staggered List Animations with Custom Timing
445
-
446
- **Use Case**: Coordinated animations across list items; each item is the viewProgress trigger. Shared `effectId` in effects registry.
447
-
448
- **When to Apply**: Wave-like propagation, linear/exponential stagger, reverse-order exit effects. Uses shared `effectId` in the effects registry so each item references the same effect.
449
-
450
- **Pattern**:
451
-
452
- ```typescript
453
- {
454
- effects: {
455
- [EFFECT_ID]: {
456
- [EFFECT_TYPE]: [EFFECT_DEFINITION],
457
- rangeStart: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
458
- rangeEnd: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
459
- easing: '[EASING_FUNCTION]'
460
- }
461
- },
462
- interactions: [
463
- {
464
- key: '[ITEM_KEY_N]',
465
- trigger: 'viewProgress',
466
- effects: [
467
- {
468
- effectId: '[EFFECT_ID]'
469
- }
470
- ]
471
- },
472
- // ... repeat for each item
473
- ]
474
- }
475
- ```
476
-
477
- **Example - Linear Staggered Card Entrance**:
478
-
479
- ```typescript
480
- {
481
- effects: {
482
- 'card-entrance': {
483
- namedEffect: {
484
- type: 'SlideScroll'
485
- },
486
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
487
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 60 } },
488
- easing: 'linear'
489
- }
490
- },
491
- interactions: [
492
- {
493
- key: 'card-1',
494
- trigger: 'viewProgress',
495
- effects: [
496
- {
497
- effectId: 'card-entrance'
498
- }
499
- ]
500
- },
501
- {
502
- key: 'card-2',
503
- trigger: 'viewProgress',
504
- effects: [
505
- {
506
- effectId: 'card-entrance'
507
- }
508
- ]
509
- },
510
- {
511
- key: 'card-3',
512
- trigger: 'viewProgress',
513
- effects: [
514
- {
515
- effectId: 'card-entrance'
516
- }
517
- ]
518
- },
519
- ]
520
- }
521
- ```
522
-
523
- **Example - Exponential Stagger for Dramatic Effect**:
524
-
525
- ```typescript
526
- {
527
- effects: {
528
- 'feature-entrance': {
529
- keyframeEffect: {
530
- name: 'feature-entrance',
531
- keyframes: [
532
- { opacity: '0', transform: 'translateY(50px) scale(0.9)' },
533
- { opacity: '1', transform: 'translateY(0) scale(1)' }
534
- ]
535
- },
536
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
537
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 100 } },
538
- easing: 'expoOut',
539
- fill: 'both'
540
- }
541
- },
542
- interactions: [
543
- { key: 'feature-1', trigger: 'viewProgress', effects: [{ effectId: 'feature-entrance' }] },
544
- { key: 'feature-2', trigger: 'viewProgress', effects: [{ effectId: 'feature-entrance' }] },
545
- { key: 'feature-3', trigger: 'viewProgress', effects: [{ effectId: 'feature-entrance' }] },
546
- ]
547
- }
548
- ```
549
-
550
- ---
551
-
552
- ## Rule 7: Dynamic Content Animations with Custom Effects
553
-
554
- **Use Case**: Per-item dynamic content via `customEffect` (counters, progress tracking, data visualization, dynamic text).
555
-
556
- **When to Apply**: Scroll-driven counters, progress tracking, data visualization, dynamic text updates in list contexts.
557
-
558
- **Pattern**:
559
-
560
- ```typescript
561
- {
562
- key: '[LIST_CONTAINER_KEY]',
563
- trigger: 'viewProgress',
564
- effects: [
565
- {
566
- key: '[DYNAMIC_CONTENT_KEY]',
567
- customEffect: (element, progress) => {
568
- // progress is 0-1 representing scroll position within range
569
- [CUSTOM_CALCULATION_LOGIC]
570
- [DYNAMIC_CONTENT_UPDATE]
571
- [VISUAL_PROPERTY_UPDATES]
572
- },
573
- rangeStart: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [START_PERCENTAGE] } },
574
- rangeEnd: { name: '[RANGE_TYPE]', offset: { unit: 'percentage', value: [END_PERCENTAGE] } },
575
- fill: 'both',
576
- effectId: '[UNIQUE_EFFECT_ID]'
577
- }
578
- ]
579
- }
580
- ```
581
-
582
- **Variables**: `[LIST_CONTAINER_KEY]` / `[DYNAMIC_CONTENT_KEY]` identify the list and target elements. The `customEffect` receives `(element, progress)` where progress is 0–1.
583
-
584
- **Example - Scroll-Driven Counter in List**:
585
-
586
- ```typescript
587
- {
588
- key: 'stats-list-container',
589
- trigger: 'viewProgress',
590
- effects: [
591
- {
592
- key: 'stat-counter',
593
- customEffect: (element, progress) => {
594
- const targetValue = parseInt(element.dataset.targetValue) || 100;
595
- const currentValue = Math.floor(targetValue * progress);
596
- const percentage = Math.floor(progress * 100);
597
-
598
- // Update counter text
599
- element.textContent = currentValue.toLocaleString();
600
-
601
- // Update visual properties based on progress
602
- element.style.color = `hsl(${progress * 120}, 70%, 50%)`; // Green to red progression
603
- element.style.transform = `scale(${0.8 + progress * 0.2})`; // Subtle scale effect
604
-
605
- // Update progress bar if exists
606
- const progressBar = element.querySelector('.progress-bar');
607
- if (progressBar) {
608
- progressBar.style.width = `${percentage}%`;
609
- }
610
- },
611
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
612
- rangeEnd: { name: 'exit', offset: { unit: 'percentage', value: 100 } },
613
- fill: 'both',
614
- effectId: 'stats-counter'
615
- }
616
- ]
617
- }
618
- ```
619
-
620
- **Example - Interactive List Progress Tracking**:
621
-
622
- ```typescript
623
- {
624
- key: 'task-list',
625
- trigger: 'viewProgress',
626
- effects: [
627
- {
628
- key: 'task-item',
629
- customEffect: (element, progress) => {
630
- const items = element.closest('interact-element')?.querySelectorAll('.task-item') || [];
631
- const totalItems = items.length;
632
- const elementIndex = Array.from(items).indexOf(element);
633
- const itemStartProgress = elementIndex / totalItems;
634
- const itemEndProgress = (elementIndex + 1) / totalItems;
635
- let itemProgress = progress > itemStartProgress
636
- ? Math.min(1, (progress - itemStartProgress) / (itemEndProgress - itemStartProgress))
637
- : 0;
638
-
639
- const checkbox = element.querySelector('.task-checkbox');
640
- const taskText = element.querySelector('.task-text');
641
- if (itemProgress > 0.5) {
642
- element.classList.add('active');
643
- checkbox.style.transform = `scale(${0.8 + itemProgress * 0.4})`;
644
- checkbox.style.opacity = itemProgress;
645
- }
646
- if (itemProgress > 0.8) {
647
- element.classList.add('completed');
648
- taskText.style.textDecoration = 'line-through';
649
- taskText.style.opacity = '0.7';
650
- }
651
- },
652
- rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } },
653
- rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } },
654
- fill: 'both',
655
- effectId: 'task-progress'
656
- }
657
- ]
658
- }
659
- ```
660
-
661
- ---
662
-
663
- ## Advanced Patterns and Combinations
664
-
665
- ### Multi-Layer List Coordination
666
-
667
- Container, items, and content: use `cover` for background/foreground layers (full scroll range), `contain` for the sticky container layer (while stuck).
668
-
669
- ```typescript
670
- {
671
- key: 'complex-list-section',
672
- trigger: 'viewProgress',
673
- effects: [
674
- { key: 'list-background', keyframeEffect: { name: 'bg-parallax', keyframes: [{ transform: 'scale(1.1)' }, { transform: 'scale(1) translateY(-50px)' }] }, rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 0 } }, rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 100 } }, easing: 'linear', fill: 'both' },
675
- { key: 'list-container', keyframeEffect: { name: 'container-slide', keyframes: [{ transform: 'translateX(0)' }, { transform: 'translateX(-50%)' }] }, rangeStart: { name: 'contain', offset: { unit: 'percentage', value: 0 } }, rangeEnd: { name: 'contain', offset: { unit: 'percentage', value: 100 } }, easing: 'linear', fill: 'both' }
676
- ]
677
- }
678
- ```
679
-
680
- ### Responsive List Animations
681
-
682
- Condition IDs are user-defined strings declared in the top-level `conditions` map. Define separate interactions for the same `key` with different conditions and effects.
683
-
684
- ```typescript
685
- {
686
- conditions: {
687
- 'desktop-only': { type: 'media', predicate: '(min-width: 768px)' },
688
- 'prefers-motion': { type: 'media', predicate: '(prefers-reduced-motion: no-preference)' },
689
- 'mobile-only': { type: 'media', predicate: '(max-width: 767px)' },
690
- },
691
- interactions: [
692
- {
693
- key: 'list-item',
694
- trigger: 'viewProgress',
695
- conditions: ['desktop-only', 'prefers-motion'],
696
- effects: [
697
- {
698
- key: 'list-item',
699
- keyframeEffect: {
700
- name: 'item-complex',
701
- keyframes: [
702
- { opacity: '0', transform: 'translateY(-20px) rotateY(5deg)' },
703
- { opacity: '1', transform: 'translateY(0) rotateY(0deg)' }
704
- ]
705
- },
706
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
707
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 80 } },
708
- easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
709
- fill: 'both'
710
- }
711
- ]
712
- },
713
- // Simplified fallback for mobile or reduced-motion users
714
- {
715
- key: 'list-item',
716
- trigger: 'viewProgress',
717
- conditions: ['mobile-only'],
718
- effects: [
719
- {
720
- key: 'list-item',
721
- keyframeEffect: {
722
- name: 'item-simple',
723
- keyframes: [
724
- { opacity: '0', transform: 'translateY(30px)' },
725
- { opacity: '1', transform: 'translateY(0)' }
726
- ]
727
- },
728
- rangeStart: { name: 'entry', offset: { unit: 'percentage', value: 0 } },
729
- rangeEnd: { name: 'entry', offset: { unit: 'percentage', value: 60 } },
730
- easing: 'ease-out',
731
- fill: 'both'
732
- }
733
- ]
734
- }
735
- ]
736
- }
737
- ```
738
-
739
- ---
740
-
741
- ## Best Practices for List Scroll Animations
742
-
743
- ### List-Specific Guidelines
744
-
745
- 1. **Sticky hierarchy**: Container → items → content. Use `contain` range for sticky container effects (animations run while the element is stuck in position).
746
- 2. **Content coordination**: Use same timeline with `cover`/`contain` range and staggered offsets, or use a different timeline per item with same range and offsets.
747
- 3. **Use position:sticky**: Animate elements while they're stuck in position and not scrolling with the page.
748
- 4. **@wix/interact conditions**: Include `prefers-motion` in conditions for reduced-motion users (e.g. `conditions: ['prefers-motion']`).