@wix/interact 2.1.3 → 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.
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/react.js +1 -1
- package/dist/cjs/web.js +1 -1
- package/dist/cjs/web.js.map +1 -1
- package/dist/es/index.js +1 -1
- package/dist/es/react.js +2 -2
- package/dist/es/web.js +15 -15
- package/dist/es/web.js.map +1 -1
- package/dist/index-ByLXasWO.mjs +2832 -0
- package/dist/index-ByLXasWO.mjs.map +1 -0
- package/dist/index-CzRuJxn8.js +18 -0
- package/dist/index-CzRuJxn8.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/core/Interact.d.ts +2 -2
- package/dist/types/core/InteractionController.d.ts +2 -2
- package/dist/types/core/InteractionController.d.ts.map +1 -1
- package/dist/types/core/add.d.ts.map +1 -1
- package/dist/types/core/css.d.ts.map +1 -1
- package/dist/types/handlers/effectHandlers.d.ts +4 -4
- package/dist/types/handlers/effectHandlers.d.ts.map +1 -1
- package/dist/types/handlers/eventTrigger.d.ts +2 -2
- package/dist/types/handlers/eventTrigger.d.ts.map +1 -1
- package/dist/types/handlers/index.d.ts.map +1 -1
- package/dist/types/handlers/viewEnter.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/react/index.d.ts +1 -1
- package/dist/types/react/index.d.ts.map +1 -1
- package/dist/types/types/config.d.ts +47 -0
- package/dist/types/types/config.d.ts.map +1 -0
- package/dist/types/types/controller.d.ts +34 -0
- package/dist/types/types/controller.d.ts.map +1 -0
- package/dist/types/types/effects.d.ts +75 -0
- package/dist/types/types/effects.d.ts.map +1 -0
- package/dist/types/types/external.d.ts +6 -0
- package/dist/types/types/external.d.ts.map +1 -0
- package/dist/types/types/global.d.ts +11 -0
- package/dist/types/types/global.d.ts.map +1 -0
- package/dist/types/types/handlers.d.ts +41 -0
- package/dist/types/types/handlers.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +8 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/types/internal.d.ts +36 -0
- package/dist/types/types/internal.d.ts.map +1 -0
- package/dist/types/types/triggers.d.ts +28 -0
- package/dist/types/types/triggers.d.ts.map +1 -0
- package/dist/types/web/InteractElement.d.ts +2 -2
- package/dist/types/web/InteractElement.d.ts.map +1 -1
- package/dist/types/web/index.d.ts +1 -1
- package/dist/types/web/index.d.ts.map +1 -1
- package/docs/api/README.md +2 -3
- package/docs/api/functions.md +4 -4
- package/docs/api/interact-class.md +2 -3
- package/docs/api/interact-element.md +2 -2
- package/docs/api/interaction-controller.md +4 -4
- package/docs/api/types.md +38 -69
- package/docs/examples/README.md +1 -1
- package/docs/examples/click-interactions.md +0 -7
- package/docs/examples/entrance-animations.md +28 -27
- package/docs/examples/list-patterns.md +17 -16
- package/docs/guides/conditions-and-media-queries.md +2 -3
- package/docs/guides/configuration-structure.md +5 -7
- package/docs/guides/effects-and-animations.md +2 -4
- package/docs/guides/getting-started.md +0 -1
- package/docs/guides/lists-and-dynamic-content.md +10 -9
- package/docs/guides/sequences.md +3 -4
- package/docs/guides/state-management.md +0 -2
- package/docs/guides/understanding-triggers.md +9 -13
- package/package.json +2 -2
- package/rules/click.md +96 -560
- package/rules/full-lean.md +536 -360
- package/rules/hover.md +107 -530
- package/rules/integration.md +212 -261
- package/rules/pointermove.md +154 -1407
- package/rules/viewenter.md +128 -863
- package/rules/viewprogress.md +88 -322
- package/dist/index-BtEG0cjF.mjs +0 -2791
- package/dist/index-BtEG0cjF.mjs.map +0 -1
- package/dist/index-ErMKtmX2.js +0 -18
- package/dist/index-ErMKtmX2.js.map +0 -1
- package/dist/types/types.d.ts +0 -256
- package/dist/types/types.d.ts.map +0 -1
- package/rules/MASTER-CLEANUP-PLAN.md +0 -286
- package/rules/scroll-list.md +0 -748
package/rules/scroll-list.md
DELETED
|
@@ -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']`).
|