@wix/interact 1.92.0 → 2.0.0-rc.2
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 +2 -23
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react.js +15 -0
- package/dist/cjs/react.js.map +1 -0
- package/dist/cjs/web.js +2 -0
- package/dist/cjs/web.js.map +1 -0
- package/dist/es/index.js +8 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/react.js +650 -0
- package/dist/es/react.js.map +1 -0
- package/dist/es/web.js +56 -0
- package/dist/es/web.js.map +1 -0
- package/dist/index-C8QxOkui.mjs +7940 -0
- package/dist/index-C8QxOkui.mjs.map +1 -0
- package/dist/index-DEPRHaUt.js +18 -0
- package/dist/index-DEPRHaUt.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/types/core/Interact.d.ts +17 -7
- package/dist/types/core/Interact.d.ts.map +1 -0
- package/dist/types/core/InteractionController.d.ts +19 -0
- package/dist/types/core/InteractionController.d.ts.map +1 -0
- package/dist/types/core/add.d.ts +4 -3
- package/dist/types/core/add.d.ts.map +1 -0
- package/dist/types/core/css.d.ts +3 -0
- package/dist/types/core/css.d.ts.map +1 -0
- package/dist/types/core/remove.d.ts +3 -1
- package/dist/types/core/remove.d.ts.map +1 -0
- package/dist/types/core/utilities.d.ts +1 -0
- package/dist/types/core/utilities.d.ts.map +1 -0
- package/dist/types/dom/api.d.ts +3 -0
- package/dist/types/dom/api.d.ts.map +1 -0
- package/dist/types/handlers/animationEnd.d.ts +3 -2
- package/dist/types/handlers/animationEnd.d.ts.map +1 -0
- package/dist/types/handlers/click.d.ts +3 -2
- package/dist/types/handlers/click.d.ts.map +1 -0
- package/dist/types/handlers/hover.d.ts +3 -2
- package/dist/types/handlers/hover.d.ts.map +1 -0
- package/dist/types/handlers/index.d.ts +1 -0
- package/dist/types/handlers/index.d.ts.map +1 -0
- package/dist/types/handlers/pointerMove.d.ts +3 -2
- package/dist/types/handlers/pointerMove.d.ts.map +1 -0
- package/dist/types/handlers/utilities.d.ts +1 -0
- package/dist/types/handlers/utilities.d.ts.map +1 -0
- package/dist/types/handlers/viewEnter.d.ts +3 -2
- package/dist/types/handlers/viewEnter.d.ts.map +1 -0
- package/dist/types/handlers/viewProgress.d.ts +4 -3
- package/dist/types/handlers/viewProgress.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/react/Interaction.d.ts +10 -0
- package/dist/types/react/Interaction.d.ts.map +1 -0
- package/dist/types/react/index.d.ts +8 -0
- package/dist/types/react/index.d.ts.map +1 -0
- package/dist/types/react/interactRef.d.ts +3 -0
- package/dist/types/react/interactRef.d.ts.map +1 -0
- package/dist/types/types.d.ts +25 -10
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/utils.d.ts +4 -2
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/{InteractElement.d.ts → web/InteractElement.d.ts} +115 -77
- package/dist/types/web/InteractElement.d.ts.map +1 -0
- package/dist/types/web/defineInteractElement.d.ts +2 -0
- package/dist/types/web/defineInteractElement.d.ts.map +1 -0
- package/dist/types/web/index.d.ts +6 -0
- package/dist/types/web/index.d.ts.map +1 -0
- package/docs/README.md +211 -0
- package/docs/advanced/README.md +164 -0
- package/docs/api/README.md +157 -0
- package/docs/api/element-selection.md +607 -0
- package/docs/api/functions.md +638 -0
- package/docs/api/interact-class.md +663 -0
- package/docs/api/interact-element.md +565 -0
- package/docs/api/interaction-controller.md +450 -0
- package/docs/api/types.md +957 -0
- package/docs/examples/README.md +212 -0
- package/docs/examples/click-interactions.md +977 -0
- package/docs/examples/entrance-animations.md +935 -0
- package/docs/examples/hover-effects.md +930 -0
- package/docs/examples/list-patterns.md +737 -0
- package/docs/guides/README.md +49 -0
- package/docs/guides/conditions-and-media-queries.md +1068 -0
- package/docs/guides/configuration-structure.md +726 -0
- package/docs/guides/custom-elements.md +327 -0
- package/docs/guides/effects-and-animations.md +634 -0
- package/docs/guides/getting-started.md +379 -0
- package/docs/guides/lists-and-dynamic-content.md +713 -0
- package/docs/guides/state-management.md +747 -0
- package/docs/guides/understanding-triggers.md +690 -0
- package/docs/integration/README.md +264 -0
- package/docs/integration/react.md +605 -0
- package/package.json +73 -56
- package/rules/Integration.md +255 -0
- package/rules/click-rules.md +533 -0
- package/rules/full-lean.md +346 -0
- package/rules/hover-rules.md +593 -0
- package/rules/pointermove-rules.md +1341 -0
- package/rules/scroll-list-rules.md +900 -0
- package/rules/viewenter-rules.md +1015 -0
- package/rules/viewprogress-rules.md +1044 -0
- package/dist/cjs/InteractElement.js +0 -162
- package/dist/cjs/InteractElement.js.map +0 -1
- package/dist/cjs/__tests__/interact.spec.js +0 -1930
- package/dist/cjs/__tests__/interact.spec.js.map +0 -1
- package/dist/cjs/__tests__/viewEnter.spec.js +0 -207
- package/dist/cjs/__tests__/viewEnter.spec.js.map +0 -1
- package/dist/cjs/core/Interact.js +0 -257
- package/dist/cjs/core/Interact.js.map +0 -1
- package/dist/cjs/core/add.js +0 -246
- package/dist/cjs/core/add.js.map +0 -1
- package/dist/cjs/core/remove.js +0 -35
- package/dist/cjs/core/remove.js.map +0 -1
- package/dist/cjs/core/utilities.js +0 -16
- package/dist/cjs/core/utilities.js.map +0 -1
- package/dist/cjs/external-types.d.js +0 -2
- package/dist/cjs/external-types.d.js.map +0 -1
- package/dist/cjs/handlers/animationEnd.js +0 -33
- package/dist/cjs/handlers/animationEnd.js.map +0 -1
- package/dist/cjs/handlers/click.js +0 -116
- package/dist/cjs/handlers/click.js.map +0 -1
- package/dist/cjs/handlers/hover.js +0 -141
- package/dist/cjs/handlers/hover.js.map +0 -1
- package/dist/cjs/handlers/index.js +0 -32
- package/dist/cjs/handlers/index.js.map +0 -1
- package/dist/cjs/handlers/pointerMove.js +0 -49
- package/dist/cjs/handlers/pointerMove.js.map +0 -1
- package/dist/cjs/handlers/utilities.js +0 -49
- package/dist/cjs/handlers/utilities.js.map +0 -1
- package/dist/cjs/handlers/viewEnter.js +0 -127
- package/dist/cjs/handlers/viewEnter.js.map +0 -1
- package/dist/cjs/handlers/viewProgress.js +0 -65
- package/dist/cjs/handlers/viewProgress.js.map +0 -1
- package/dist/cjs/test-types.d.js +0 -2
- package/dist/cjs/test-types.d.js.map +0 -1
- package/dist/cjs/types.js +0 -2
- package/dist/cjs/types.js.map +0 -1
- package/dist/cjs/utils.js +0 -68
- package/dist/cjs/utils.js.map +0 -1
- package/dist/esm/InteractElement.js +0 -156
- package/dist/esm/InteractElement.js.map +0 -1
- package/dist/esm/__tests__/interact.spec.js +0 -1937
- package/dist/esm/__tests__/interact.spec.js.map +0 -1
- package/dist/esm/__tests__/viewEnter.spec.js +0 -210
- package/dist/esm/__tests__/viewEnter.spec.js.map +0 -1
- package/dist/esm/core/Interact.js +0 -251
- package/dist/esm/core/Interact.js.map +0 -1
- package/dist/esm/core/add.js +0 -241
- package/dist/esm/core/add.js.map +0 -1
- package/dist/esm/core/remove.js +0 -30
- package/dist/esm/core/remove.js.map +0 -1
- package/dist/esm/core/utilities.js +0 -14
- package/dist/esm/core/utilities.js.map +0 -1
- package/dist/esm/external-types.d.js +0 -2
- package/dist/esm/external-types.d.js.map +0 -1
- package/dist/esm/handlers/animationEnd.js +0 -29
- package/dist/esm/handlers/animationEnd.js.map +0 -1
- package/dist/esm/handlers/click.js +0 -116
- package/dist/esm/handlers/click.js.map +0 -1
- package/dist/esm/handlers/hover.js +0 -141
- package/dist/esm/handlers/hover.js.map +0 -1
- package/dist/esm/handlers/index.js +0 -27
- package/dist/esm/handlers/index.js.map +0 -1
- package/dist/esm/handlers/pointerMove.js +0 -48
- package/dist/esm/handlers/pointerMove.js.map +0 -1
- package/dist/esm/handlers/utilities.js +0 -43
- package/dist/esm/handlers/utilities.js.map +0 -1
- package/dist/esm/handlers/viewEnter.js +0 -129
- package/dist/esm/handlers/viewEnter.js.map +0 -1
- package/dist/esm/handlers/viewProgress.js +0 -61
- package/dist/esm/handlers/viewProgress.js.map +0 -1
- package/dist/esm/index.js +0 -5
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/test-types.d.js +0 -2
- package/dist/esm/test-types.d.js.map +0 -1
- package/dist/esm/types.js +0 -2
- package/dist/esm/types.js.map +0 -1
- package/dist/esm/utils.js +0 -63
- package/dist/esm/utils.js.map +0 -1
- package/dist/types/__tests__/interact.spec.d.ts +0 -1
- package/dist/types/__tests__/viewEnter.spec.d.ts +0 -0
|
@@ -0,0 +1,1044 @@
|
|
|
1
|
+
# ViewProgress Trigger Rules for @wix/interact
|
|
2
|
+
|
|
3
|
+
These rules help generate scroll-driven interactions using the `@wix/interact` library. ViewProgress triggers create scroll-based animations that update continuously as elements move through the viewport, perfect for parallax effects, progress indicators, and scroll-responsive content.
|
|
4
|
+
|
|
5
|
+
## Rule 1: Range-Based Parallax/Continuous Animation Control with Named Effects
|
|
6
|
+
|
|
7
|
+
**Use Case**: Continuous scroll-driven animations using pre-built named effects that respond to scroll position (e.g., parallax backgrounds, floating elements, scroll-responsive decorations)
|
|
8
|
+
|
|
9
|
+
**When to Apply**:
|
|
10
|
+
- For smooth parallax background movements
|
|
11
|
+
- When creating scroll-responsive floating elements
|
|
12
|
+
- For continuous scroll-driven decorative animations
|
|
13
|
+
- When using pre-built motion effects for scroll interactions
|
|
14
|
+
|
|
15
|
+
**Pattern**:
|
|
16
|
+
```typescript
|
|
17
|
+
{
|
|
18
|
+
key: '[SOURCE_SELECTOR]',
|
|
19
|
+
trigger: 'viewProgress',
|
|
20
|
+
effects: [
|
|
21
|
+
{
|
|
22
|
+
key: '[TARGET_SELECTOR]',
|
|
23
|
+
namedEffect: {
|
|
24
|
+
type: '[NAMED_EFFECT]',
|
|
25
|
+
},
|
|
26
|
+
rangeStart: { name: '[RANGE_NAME]', offset: { type: 'percentage', value: [START_PERCENTAGE] } },
|
|
27
|
+
rangeEnd: { name: '[RANGE_NAME]', offset: { type: 'percentage', value: [END_PERCENTAGE] } },
|
|
28
|
+
easing: '[EASING_FUNCTION]',
|
|
29
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Variables**:
|
|
36
|
+
- `[SOURCE_SELECTOR]`: Unique identifier for element that tracks scroll progress
|
|
37
|
+
- `[TARGET_SELECTOR]`: Unique identifier for element to animate (can be same as source or different)
|
|
38
|
+
- `[NAMED_EFFECT]`: Pre-built scroll effect name from @wix/motion (e.g., 'ParallaxScroll', 'MoveScroll', 'FadeScroll', 'RevealScroll', 'GrowScroll', 'SlideScroll', 'SpinScroll', 'PanScroll', 'BlurScroll', 'ArcScroll', 'FlipScroll', 'Spin3dScroll', 'TiltScroll', 'TurnScroll', 'ShapeScroll', 'ShuttersScroll', 'ShrinkScroll', 'SkewPanScroll', 'StretchScroll')
|
|
39
|
+
- `[RANGE_NAME]`: 'cover', 'contain', 'entry', 'exit', 'entry-crossing', or 'exit-crossing'
|
|
40
|
+
- `[START_PERCENTAGE]`: Start point as percentage (0-100)
|
|
41
|
+
- `[END_PERCENTAGE]`: End point as percentage (0-100)
|
|
42
|
+
- `[EASING_FUNCTION]`: Timing function (typically 'linear' for smooth scroll effects)
|
|
43
|
+
- `[UNIQUE_EFFECT_ID]`: Optional unique identifier
|
|
44
|
+
|
|
45
|
+
**Example - Background Parallax**:
|
|
46
|
+
```typescript
|
|
47
|
+
{
|
|
48
|
+
key: 'hero-section',
|
|
49
|
+
trigger: 'viewProgress',
|
|
50
|
+
effects: [
|
|
51
|
+
{
|
|
52
|
+
key: 'hero-background',
|
|
53
|
+
namedEffect: {
|
|
54
|
+
type: 'ParallaxScroll'
|
|
55
|
+
},
|
|
56
|
+
rangeStart: { name: 'cover', offset: { type: 'percentage', value: 0 } },
|
|
57
|
+
rangeEnd: { name: 'cover', offset: { type: 'percentage', value: 100 } },
|
|
58
|
+
easing: 'linear'
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Example - Floating Element Scroll Response**:
|
|
65
|
+
```typescript
|
|
66
|
+
{
|
|
67
|
+
key: 'content-section',
|
|
68
|
+
trigger: 'viewProgress',
|
|
69
|
+
effects: [
|
|
70
|
+
{
|
|
71
|
+
key: 'floating-decoration',
|
|
72
|
+
namedEffect: {
|
|
73
|
+
type: 'MoveScroll',
|
|
74
|
+
angle: 45 // 45-degree angle movement
|
|
75
|
+
},
|
|
76
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 0 } },
|
|
77
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 100 } },
|
|
78
|
+
easing: 'linear',
|
|
79
|
+
effectId: 'decoration-float'
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Rule 2: Range-Based Entry Animation Control with Named Effects
|
|
88
|
+
|
|
89
|
+
**Use Case**: Scroll-driven entrance animations using named effects that start when elements enter the viewport (e.g., content reveals, scroll-driven introductions)
|
|
90
|
+
|
|
91
|
+
**When to Apply**:
|
|
92
|
+
- For scroll-controlled entrance animations
|
|
93
|
+
- When elements should reveal gradually as they come into view
|
|
94
|
+
- For progressive content disclosure based on scroll
|
|
95
|
+
- When using pre-built entrance effects with scroll control
|
|
96
|
+
|
|
97
|
+
**Pattern**:
|
|
98
|
+
```typescript
|
|
99
|
+
{
|
|
100
|
+
key: '[SOURCE_SELECTOR]',
|
|
101
|
+
trigger: 'viewProgress',
|
|
102
|
+
effects: [
|
|
103
|
+
{
|
|
104
|
+
key: '[TARGET_SELECTOR]',
|
|
105
|
+
namedEffect: {
|
|
106
|
+
type: '[ENTRANCE_EFFECT]'
|
|
107
|
+
},
|
|
108
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: [ENTRY_START] } },
|
|
109
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: [ENTRY_END] } },
|
|
110
|
+
easing: '[EASING_FUNCTION]',
|
|
111
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Variables**:
|
|
118
|
+
- `[ENTRANCE_EFFECT]`: Named entrance effect from @wix/motion scroll animations (e.g., 'FadeScroll', 'SlideScroll', 'RevealScroll', 'ShapeScroll', 'GrowScroll', 'MoveScroll', 'BlurScroll')
|
|
119
|
+
- `[ENTRY_START]`: Entry animation start percentage (typically 0-30)
|
|
120
|
+
- `[ENTRY_END]`: Entry animation end percentage (typically 70-100)
|
|
121
|
+
- Other variables same as Rule 1
|
|
122
|
+
|
|
123
|
+
**Example - Content Reveal on Entry**:
|
|
124
|
+
```typescript
|
|
125
|
+
{
|
|
126
|
+
key: 'content-block',
|
|
127
|
+
trigger: 'viewProgress',
|
|
128
|
+
effects: [
|
|
129
|
+
{
|
|
130
|
+
key: 'content-block',
|
|
131
|
+
namedEffect: {
|
|
132
|
+
type: 'RevealScroll',
|
|
133
|
+
direction: 'left'
|
|
134
|
+
},
|
|
135
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 0 } },
|
|
136
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: 60 } },
|
|
137
|
+
easing: 'ease-out'
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Example - Progressive Image Reveal**:
|
|
144
|
+
```typescript
|
|
145
|
+
{
|
|
146
|
+
key: 'image-container',
|
|
147
|
+
trigger: 'viewProgress',
|
|
148
|
+
effects: [
|
|
149
|
+
{
|
|
150
|
+
key: 'feature-image',
|
|
151
|
+
namedEffect: {
|
|
152
|
+
type: 'FadeScroll'
|
|
153
|
+
},
|
|
154
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 20 } },
|
|
155
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: 80 } },
|
|
156
|
+
easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
157
|
+
effectId: 'image-reveal'
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Rule 3: Range-Based Exit Animation Control with Named Effects
|
|
166
|
+
|
|
167
|
+
**Use Case**: Scroll-driven exit animations using named effects that trigger when elements leave the viewport (e.g., content hiding, scroll-out effects, element dismissals)
|
|
168
|
+
|
|
169
|
+
**When to Apply**:
|
|
170
|
+
- For scroll-controlled exit animations
|
|
171
|
+
- When elements should hide gradually as they leave view
|
|
172
|
+
- For creating scroll-responsive content dismissals
|
|
173
|
+
- When using pre-built exit effects with scroll control
|
|
174
|
+
|
|
175
|
+
**Pattern**:
|
|
176
|
+
```typescript
|
|
177
|
+
{
|
|
178
|
+
key: '[SOURCE_SELECTOR]',
|
|
179
|
+
trigger: 'viewProgress',
|
|
180
|
+
effects: [
|
|
181
|
+
{
|
|
182
|
+
key: '[TARGET_SELECTOR]',
|
|
183
|
+
namedEffect: {
|
|
184
|
+
type: '[EXIT_EFFECT]',
|
|
185
|
+
range: 'out'
|
|
186
|
+
},
|
|
187
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: [EXIT_START] } },
|
|
188
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: [EXIT_END] } },
|
|
189
|
+
easing: '[EASING_FUNCTION]',
|
|
190
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Variables**:
|
|
197
|
+
- `[EXIT_EFFECT]`: Named exit effect (e.g., 'FadeScroll', 'SlideScroll', 'GrowScroll', 'ShrinkScroll')
|
|
198
|
+
- `[EXIT_START]`: Exit animation start percentage (typically 0-30)
|
|
199
|
+
- `[EXIT_END]`: Exit animation end percentage (typically 70-100)
|
|
200
|
+
- Other variables same as Rule 1
|
|
201
|
+
|
|
202
|
+
**Example - Content Fade Out on Exit**:
|
|
203
|
+
```typescript
|
|
204
|
+
{
|
|
205
|
+
key: 'hero-content',
|
|
206
|
+
trigger: 'viewProgress',
|
|
207
|
+
effects: [
|
|
208
|
+
{
|
|
209
|
+
key: 'hero-text',
|
|
210
|
+
namedEffect: {
|
|
211
|
+
type: 'FadeScroll',
|
|
212
|
+
range: 'out'
|
|
213
|
+
},
|
|
214
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: 0 } },
|
|
215
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 50 } },
|
|
216
|
+
easing: 'ease-in'
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Example - Navigation Hide on Scroll Out**:
|
|
223
|
+
```typescript
|
|
224
|
+
{
|
|
225
|
+
key: 'main-content',
|
|
226
|
+
trigger: 'viewProgress',
|
|
227
|
+
effects: [
|
|
228
|
+
{
|
|
229
|
+
key: 'floating-nav',
|
|
230
|
+
namedEffect: {
|
|
231
|
+
type: 'SlideScroll',
|
|
232
|
+
direction: 'top',
|
|
233
|
+
range: 'out'
|
|
234
|
+
},
|
|
235
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: 20 } },
|
|
236
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 80 } },
|
|
237
|
+
easing: 'ease-in-out',
|
|
238
|
+
effectId: 'nav-hide'
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Rule 4: Range-Based Parallax/Continuous Animation Control with Keyframe Effects
|
|
247
|
+
|
|
248
|
+
**Use Case**: Custom scroll-driven animations using keyframe effects for precise control over continuous scroll-responsive animations (e.g., custom parallax movements, complex scroll transformations, multi-property scroll effects)
|
|
249
|
+
|
|
250
|
+
**When to Apply**:
|
|
251
|
+
- For custom parallax effects not available in named effects
|
|
252
|
+
- When combining multiple CSS properties in scroll animations
|
|
253
|
+
- For precise control over scroll-driven transformations
|
|
254
|
+
- When creating unique scroll-responsive visual effects
|
|
255
|
+
|
|
256
|
+
**Pattern**:
|
|
257
|
+
```typescript
|
|
258
|
+
{
|
|
259
|
+
key: '[SOURCE_SELECTOR]',
|
|
260
|
+
trigger: 'viewProgress',
|
|
261
|
+
effects: [
|
|
262
|
+
{
|
|
263
|
+
key: '[TARGET_SELECTOR]',
|
|
264
|
+
keyframeEffect: {
|
|
265
|
+
name: '[UNIQUE_KEYFRAME_EFFECT_NAME]',
|
|
266
|
+
keyframes: [
|
|
267
|
+
{ [CSS_PROPERTY_1]: '[START_VALUE_1]', [CSS_PROPERTY_2]: '[START_VALUE_2]', [CSS_PROPERTY_3]: '[START_VALUE_3]' },
|
|
268
|
+
{ [CSS_PROPERTY_1]: '[END_VALUE_1]', [CSS_PROPERTY_2]: '[END_VALUE_2]', [CSS_PROPERTY_3]: '[END_VALUE_3]' }
|
|
269
|
+
]
|
|
270
|
+
},
|
|
271
|
+
rangeStart: { name: '[RANGE_NAME]', offset: { type: 'percentage', value: [START_PERCENTAGE] } },
|
|
272
|
+
rangeEnd: { name: '[RANGE_NAME]', offset: { type: 'percentage', value: [END_PERCENTAGE] } },
|
|
273
|
+
easing: '[EASING_FUNCTION]',
|
|
274
|
+
fill: 'both',
|
|
275
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Variables**:
|
|
282
|
+
- `[UNIQUE_KEYFRAME_EFFECT_NAME]`: unique name for the CSS keyframe effect (can equal `[UNIQUE_EFFECT_ID]` if provided)
|
|
283
|
+
- `[CSS_PROPERTY_N]`: CSS property names (e.g., 'transform', 'opacity', 'filter')
|
|
284
|
+
- `[START_VALUE_N]`: Starting value for the property
|
|
285
|
+
- `[END_VALUE_N]`: Ending value for the property
|
|
286
|
+
- Other variables same as Rule 1
|
|
287
|
+
|
|
288
|
+
**Example - Custom Background Parallax**:
|
|
289
|
+
```typescript
|
|
290
|
+
{
|
|
291
|
+
key: 'parallax-section',
|
|
292
|
+
trigger: 'viewProgress',
|
|
293
|
+
effects: [
|
|
294
|
+
{
|
|
295
|
+
key: 'parallax-bg',
|
|
296
|
+
keyframeEffect: {
|
|
297
|
+
name: 'parallax-bg',
|
|
298
|
+
keyframes: [
|
|
299
|
+
{ transform: 'translateY(0)', filter: 'brightness(1)', opacity: '0.9' },
|
|
300
|
+
{ opacity: '1' },
|
|
301
|
+
{ transform: 'translateY(-200px)', filter: 'brightness(0.8)', opacity: '0.9' }
|
|
302
|
+
]
|
|
303
|
+
},
|
|
304
|
+
rangeStart: { name: 'cover', offset: { type: 'percentage', value: 0 } },
|
|
305
|
+
rangeEnd: { name: 'cover', offset: { type: 'percentage', value: 100 } },
|
|
306
|
+
easing: 'linear',
|
|
307
|
+
fill: 'both'
|
|
308
|
+
}
|
|
309
|
+
]
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Example - Multi-Layer Scroll Effect**:
|
|
314
|
+
```typescript
|
|
315
|
+
{
|
|
316
|
+
key: 'complex-section',
|
|
317
|
+
trigger: 'viewProgress',
|
|
318
|
+
effects: [
|
|
319
|
+
{
|
|
320
|
+
key: 'background-layer',
|
|
321
|
+
keyframeEffect: {
|
|
322
|
+
name: 'bg-scroll',
|
|
323
|
+
keyframes: [
|
|
324
|
+
{ transform: 'scale(1.1) translateY(0)', filter: 'blur(0)' },
|
|
325
|
+
{ transform: 'scale(1) translateY(-100px)', filter: 'blur(2px)' }
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
rangeStart: { name: 'enter', offset: { type: 'percentage', value: 0 } },
|
|
329
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 100 } },
|
|
330
|
+
easing: 'linear',
|
|
331
|
+
fill: 'both',
|
|
332
|
+
effectId: 'bg-scroll'
|
|
333
|
+
}
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Rule 5: Range-Based Entry Animation Control with Keyframe Effects
|
|
341
|
+
|
|
342
|
+
**Use Case**: Custom scroll-driven entrance animations using keyframe effects for precise control over how elements appear as they enter the viewport (e.g., custom reveal effects, multi-property entrances, unique scroll-in animations)
|
|
343
|
+
|
|
344
|
+
**When to Apply**:
|
|
345
|
+
- For custom entrance effects not available in named effects
|
|
346
|
+
- When combining multiple properties in entrance animations
|
|
347
|
+
- For brand-specific or unique entry animations
|
|
348
|
+
- When creating complex reveal sequences
|
|
349
|
+
|
|
350
|
+
**Pattern**:
|
|
351
|
+
```typescript
|
|
352
|
+
{
|
|
353
|
+
key: '[SOURCE_SELECTOR]',
|
|
354
|
+
trigger: 'viewProgress',
|
|
355
|
+
effects: [
|
|
356
|
+
{
|
|
357
|
+
key: '[TARGET_SELECTOR]',
|
|
358
|
+
keyframeEffect: {
|
|
359
|
+
name: '[UNIQUE_KEYFRAME_EFFECT_NAME]',
|
|
360
|
+
keyframes: [
|
|
361
|
+
{ [CSS_PROPERTY_1]: '[START_VALUE_1]', [CSS_PROPERTY_2]: '[START_VALUE_2]' },
|
|
362
|
+
{ [CSS_PROPERTY_1]: '[END_VALUE_1]', [CSS_PROPERTY_2]: '[END_VALUE_2]' }
|
|
363
|
+
]
|
|
364
|
+
},
|
|
365
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: [ENTRY_START] } },
|
|
366
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: [ENTRY_END] } },
|
|
367
|
+
easing: '[EASING_FUNCTION]',
|
|
368
|
+
fill: 'both',
|
|
369
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
370
|
+
}
|
|
371
|
+
]
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Variables**:
|
|
376
|
+
Same as Rule 4, with focus on entry range
|
|
377
|
+
|
|
378
|
+
**Example - Custom Card Entrance**:
|
|
379
|
+
```typescript
|
|
380
|
+
{
|
|
381
|
+
key: 'card-section',
|
|
382
|
+
trigger: 'viewProgress',
|
|
383
|
+
effects: [
|
|
384
|
+
{
|
|
385
|
+
key: 'product-card',
|
|
386
|
+
keyframeEffect: {
|
|
387
|
+
name: 'card-entrance',
|
|
388
|
+
keyframes: [
|
|
389
|
+
{ opacity: '0', transform: 'translateY(80px) scale(0.9)', filter: 'blur(5px)' },
|
|
390
|
+
{ opacity: '1', transform: 'translateY(0) scale(1)', filter: 'blur(0)' }
|
|
391
|
+
]
|
|
392
|
+
},
|
|
393
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 0 } },
|
|
394
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: 70 } },
|
|
395
|
+
easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
396
|
+
fill: 'both'
|
|
397
|
+
}
|
|
398
|
+
]
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**Example - Text Progressive Reveal**:
|
|
403
|
+
```typescript
|
|
404
|
+
{
|
|
405
|
+
key: 'text-container',
|
|
406
|
+
trigger: 'viewProgress',
|
|
407
|
+
effects: [
|
|
408
|
+
{
|
|
409
|
+
key: 'main-heading',
|
|
410
|
+
keyframeEffect: {
|
|
411
|
+
name: 'heading-reveal',
|
|
412
|
+
keyframes: [
|
|
413
|
+
{ opacity: '0', transform: 'translateX(-50px)', color: 'rgba(0,0,0,0.3)' },
|
|
414
|
+
{ opacity: '1', transform: 'translateX(0)', color: 'rgba(0,0,0,1)' }
|
|
415
|
+
]
|
|
416
|
+
},
|
|
417
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 10 } },
|
|
418
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: 60 } },
|
|
419
|
+
easing: 'ease-out',
|
|
420
|
+
fill: 'both',
|
|
421
|
+
effectId: 'heading-reveal'
|
|
422
|
+
}
|
|
423
|
+
]
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Rule 6: Range-Based Exit Animation Control with Keyframe Effects
|
|
430
|
+
|
|
431
|
+
**Use Case**: Custom scroll-driven exit animations using keyframe effects for precise control over how elements disappear as they leave the viewport (e.g., custom hide effects, multi-property exits, unique scroll-out animations)
|
|
432
|
+
|
|
433
|
+
**When to Apply**:
|
|
434
|
+
- For custom exit effects not available in named effects
|
|
435
|
+
- When combining multiple properties in exit animations
|
|
436
|
+
- For creating smooth content transitions on scroll out
|
|
437
|
+
- When elements need complex hiding sequences
|
|
438
|
+
|
|
439
|
+
**Pattern**:
|
|
440
|
+
```typescript
|
|
441
|
+
{
|
|
442
|
+
key: '[SOURCE_SELECTOR]',
|
|
443
|
+
trigger: 'viewProgress',
|
|
444
|
+
effects: [
|
|
445
|
+
{
|
|
446
|
+
key: '[TARGET_SELECTOR]',
|
|
447
|
+
keyframeEffect: {
|
|
448
|
+
name: '[UNIQUE_KEYFRAME_EFFECT_NAME]',
|
|
449
|
+
keyframes: [
|
|
450
|
+
{ [CSS_PROPERTY_1]: '[START_VALUE_1]', [CSS_PROPERTY_2]: '[START_VALUE_2]' },
|
|
451
|
+
{ [CSS_PROPERTY_1]: '[END_VALUE_1]', [CSS_PROPERTY_2]: '[END_VALUE_2]' }
|
|
452
|
+
]
|
|
453
|
+
},
|
|
454
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: [EXIT_START] } },
|
|
455
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: [EXIT_END] } },
|
|
456
|
+
easing: '[EASING_FUNCTION]',
|
|
457
|
+
fill: 'both',
|
|
458
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
459
|
+
}
|
|
460
|
+
]
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
**Variables**:
|
|
465
|
+
Same as Rule 4, with focus on exit range
|
|
466
|
+
|
|
467
|
+
**Example - Hero Content Exit**:
|
|
468
|
+
```typescript
|
|
469
|
+
{
|
|
470
|
+
key: 'hero-section',
|
|
471
|
+
trigger: 'viewProgress',
|
|
472
|
+
effects: [
|
|
473
|
+
{
|
|
474
|
+
key: 'hero-content',
|
|
475
|
+
keyframeEffect: {
|
|
476
|
+
name: 'hero-content-animation',
|
|
477
|
+
keyframes: [
|
|
478
|
+
{ opacity: '1', transform: 'translateY(0) scale(1)', filter: 'blur(0)' },
|
|
479
|
+
{ opacity: '0', transform: 'translateY(-50px) scale(0.95)', filter: 'blur(3px)' }
|
|
480
|
+
]
|
|
481
|
+
},
|
|
482
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: 0 } },
|
|
483
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 60 } },
|
|
484
|
+
easing: 'ease-in',
|
|
485
|
+
fill: 'both'
|
|
486
|
+
}
|
|
487
|
+
]
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**Example - Navigation Scroll Hide**:
|
|
492
|
+
```typescript
|
|
493
|
+
{
|
|
494
|
+
key: 'main-header',
|
|
495
|
+
trigger: 'viewProgress',
|
|
496
|
+
effects: [
|
|
497
|
+
{
|
|
498
|
+
key: 'sticky-nav',
|
|
499
|
+
keyframeEffect: {
|
|
500
|
+
name: 'nav-hide',
|
|
501
|
+
keyframes: [
|
|
502
|
+
{ transform: 'translateY(0)', opacity: '1', backdropFilter: 'blur(10px)' },
|
|
503
|
+
{ transform: 'translateY(-100%)', opacity: '0.7', backdropFilter: 'blur(0)' }
|
|
504
|
+
]
|
|
505
|
+
},
|
|
506
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: 20 } },
|
|
507
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 80 } },
|
|
508
|
+
easing: 'ease-in-out',
|
|
509
|
+
fill: 'both',
|
|
510
|
+
effectId: 'nav-hide'
|
|
511
|
+
}
|
|
512
|
+
]
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## Rule 7: Range-Based Parallax/Continuous Animation Control with Custom Effects
|
|
519
|
+
|
|
520
|
+
**Use Case**: JavaScript-powered scroll-driven animations with full programmatic control for complex interactions (e.g., canvas animations, complex calculations, dynamic content updates, interactive scroll effects)
|
|
521
|
+
|
|
522
|
+
**When to Apply**:
|
|
523
|
+
- For animations requiring complex calculations
|
|
524
|
+
- When integrating with canvas or WebGL
|
|
525
|
+
- For dynamic content updates based on scroll
|
|
526
|
+
- When CSS keyframes are insufficient
|
|
527
|
+
|
|
528
|
+
**Pattern**:
|
|
529
|
+
```typescript
|
|
530
|
+
{
|
|
531
|
+
key: '[SOURCE_SELECTOR]',
|
|
532
|
+
trigger: 'viewProgress',
|
|
533
|
+
effects: [
|
|
534
|
+
{
|
|
535
|
+
key: '[TARGET_SELECTOR]',
|
|
536
|
+
customEffect: (element, progress, params) => {
|
|
537
|
+
// progress is 0-1 representing scroll position within range
|
|
538
|
+
[CUSTOM_ANIMATION_LOGIC]
|
|
539
|
+
},
|
|
540
|
+
rangeStart: { name: '[RANGE_NAME]', offset: { type: 'percentage', value: [START_PERCENTAGE] } },
|
|
541
|
+
rangeEnd: { name: '[RANGE_NAME]', offset: { type: 'percentage', value: [END_PERCENTAGE] } },
|
|
542
|
+
fill: 'both',
|
|
543
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
544
|
+
}
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Variables**:
|
|
550
|
+
- `[CUSTOM_ANIMATION_LOGIC]`: JavaScript code for custom animation
|
|
551
|
+
- Other variables same as Rule 1
|
|
552
|
+
|
|
553
|
+
**Example - Scroll Counter Update**:
|
|
554
|
+
```typescript
|
|
555
|
+
{
|
|
556
|
+
key: 'stats-section',
|
|
557
|
+
trigger: 'viewProgress',
|
|
558
|
+
effects: [
|
|
559
|
+
{
|
|
560
|
+
key: 'progress-counter',
|
|
561
|
+
customEffect: (element, progress) => {
|
|
562
|
+
const currentValue = Math.floor(progress * 100);
|
|
563
|
+
element.textContent = `${currentValue}%`;
|
|
564
|
+
element.style.color = `hsl(${progress * 120}, 70%, 50%)`;
|
|
565
|
+
},
|
|
566
|
+
rangeStart: { name: 'cover', offset: { type: 'percentage', value: 0 } },
|
|
567
|
+
rangeEnd: { name: 'cover', offset: { type: 'percentage', value: 100 } },
|
|
568
|
+
fill: 'both',
|
|
569
|
+
effectId: 'progress-counter'
|
|
570
|
+
}
|
|
571
|
+
]
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
**Example - Complex Particle Animation**:
|
|
576
|
+
```typescript
|
|
577
|
+
{
|
|
578
|
+
key: 'particle-container',
|
|
579
|
+
trigger: 'viewProgress',
|
|
580
|
+
effects: [
|
|
581
|
+
{
|
|
582
|
+
key: 'particle-canvas',
|
|
583
|
+
customEffect: (element, progress) => {
|
|
584
|
+
const particles = element.querySelectorAll('.particle');
|
|
585
|
+
particles.forEach((particle, index) => {
|
|
586
|
+
const delay = index * 0.1;
|
|
587
|
+
const adjustedProgress = Math.max(0, Math.min(1, (progress - delay) / (1 - delay)));
|
|
588
|
+
const rotation = adjustedProgress * 360;
|
|
589
|
+
const scale = 0.5 + (adjustedProgress * 0.5);
|
|
590
|
+
const translateY = (1 - adjustedProgress) * 200;
|
|
591
|
+
|
|
592
|
+
particle.style.transform = `
|
|
593
|
+
translateY(${translateY}px)
|
|
594
|
+
rotate(${rotation}deg)
|
|
595
|
+
scale(${scale})
|
|
596
|
+
`;
|
|
597
|
+
particle.style.opacity = adjustedProgress;
|
|
598
|
+
});
|
|
599
|
+
},
|
|
600
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 0 } },
|
|
601
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 100 } },
|
|
602
|
+
fill: 'both',
|
|
603
|
+
effectId: 'particle-scroll'
|
|
604
|
+
}
|
|
605
|
+
]
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Rule 8: Range-Based Entry Animation Control with Custom Effects
|
|
612
|
+
|
|
613
|
+
**Use Case**: JavaScript-powered entrance animations with programmatic control for complex entry sequences (e.g., dynamic counters, interactive reveals, calculated animations, progressive loading effects)
|
|
614
|
+
|
|
615
|
+
**When to Apply**:
|
|
616
|
+
- For entrance animations requiring calculations
|
|
617
|
+
- When creating dynamic content reveals
|
|
618
|
+
- For interactive entrance sequences
|
|
619
|
+
- When standard keyframes cannot achieve the desired effect
|
|
620
|
+
|
|
621
|
+
**Pattern**:
|
|
622
|
+
```typescript
|
|
623
|
+
{
|
|
624
|
+
key: '[SOURCE_SELECTOR]',
|
|
625
|
+
trigger: 'viewProgress',
|
|
626
|
+
effects: [
|
|
627
|
+
{
|
|
628
|
+
key: '[TARGET_SELECTOR]',
|
|
629
|
+
customEffect: (element, progress, params) => {
|
|
630
|
+
// progress is 0-1 representing entry progress
|
|
631
|
+
[ENTRY_ANIMATION_LOGIC]
|
|
632
|
+
},
|
|
633
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: [ENTRY_START] } },
|
|
634
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: [ENTRY_END] } },
|
|
635
|
+
fill: 'both',
|
|
636
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
637
|
+
}
|
|
638
|
+
]
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
**Variables**:
|
|
643
|
+
- `[ENTRY_ANIMATION_LOGIC]`: JavaScript code for custom entry animation
|
|
644
|
+
- Other variables same as previous rules
|
|
645
|
+
|
|
646
|
+
**Example - Dynamic Text Reveal**:
|
|
647
|
+
```typescript
|
|
648
|
+
{
|
|
649
|
+
key: 'text-section',
|
|
650
|
+
trigger: 'viewProgress',
|
|
651
|
+
effects: [
|
|
652
|
+
{
|
|
653
|
+
key: 'animated-text',
|
|
654
|
+
customEffect: (element, progress) => {
|
|
655
|
+
const text = element.dataset.fullText || element.textContent;
|
|
656
|
+
const visibleLength = Math.floor(text.length * progress);
|
|
657
|
+
const visibleText = text.substring(0, visibleLength);
|
|
658
|
+
element.textContent = visibleText + (progress < 1 ? '|' : '');
|
|
659
|
+
|
|
660
|
+
element.style.opacity = Math.min(1, progress * 2);
|
|
661
|
+
element.style.transform = `translateY(${(1 - progress) * 30}px)`;
|
|
662
|
+
},
|
|
663
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 0 } },
|
|
664
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: 80 } },
|
|
665
|
+
fill: 'both',
|
|
666
|
+
effectId: 'text-reveal'
|
|
667
|
+
}
|
|
668
|
+
]
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
**Example - Progressive Chart Fill**:
|
|
673
|
+
```typescript
|
|
674
|
+
{
|
|
675
|
+
key: 'chart-container',
|
|
676
|
+
trigger: 'viewProgress',
|
|
677
|
+
effects: [
|
|
678
|
+
{
|
|
679
|
+
key: 'chart-bar',
|
|
680
|
+
customEffect: (element, progress) => {
|
|
681
|
+
const targetHeight = element.dataset.targetHeight || 100;
|
|
682
|
+
const currentHeight = targetHeight * progress;
|
|
683
|
+
const colorIntensity = Math.floor(255 * progress);
|
|
684
|
+
|
|
685
|
+
element.style.height = `${currentHeight}px`;
|
|
686
|
+
element.style.backgroundColor = `rgb(${255 - colorIntensity}, ${colorIntensity}, 100)`;
|
|
687
|
+
element.style.boxShadow = `0 0 ${progress * 20}px rgba(0, ${colorIntensity}, 255, 0.5)`;
|
|
688
|
+
},
|
|
689
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 20 } },
|
|
690
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: 90 } },
|
|
691
|
+
fill: 'both',
|
|
692
|
+
effectId: 'chart-fill'
|
|
693
|
+
}
|
|
694
|
+
]
|
|
695
|
+
}
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## Rule 9: Range-Based Exit Animation Control with Custom Effects
|
|
701
|
+
|
|
702
|
+
**Use Case**: JavaScript-powered exit animations with programmatic control for complex exit sequences (e.g., dynamic hiding effects, calculated dismissals, interactive fade-outs, progressive unloading effects)
|
|
703
|
+
|
|
704
|
+
**When to Apply**:
|
|
705
|
+
- For exit animations requiring calculations
|
|
706
|
+
- When creating dynamic content hiding
|
|
707
|
+
- For interactive exit sequences
|
|
708
|
+
- When standard keyframes cannot achieve the desired exit effect
|
|
709
|
+
|
|
710
|
+
**Pattern**:
|
|
711
|
+
```typescript
|
|
712
|
+
{
|
|
713
|
+
key: '[SOURCE_SELECTOR]',
|
|
714
|
+
trigger: 'viewProgress',
|
|
715
|
+
effects: [
|
|
716
|
+
{
|
|
717
|
+
key: '[TARGET_SELECTOR]',
|
|
718
|
+
customEffect: (element, progress, params) => {
|
|
719
|
+
// progress is 0-1 representing exit progress
|
|
720
|
+
[EXIT_ANIMATION_LOGIC]
|
|
721
|
+
},
|
|
722
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: [EXIT_START] } },
|
|
723
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: [EXIT_END] } },
|
|
724
|
+
fill: 'both',
|
|
725
|
+
effectId: '[UNIQUE_EFFECT_ID]'
|
|
726
|
+
}
|
|
727
|
+
]
|
|
728
|
+
}
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**Variables**:
|
|
732
|
+
- `[EXIT_ANIMATION_LOGIC]`: JavaScript code for custom exit animation
|
|
733
|
+
- Other variables same as previous rules
|
|
734
|
+
|
|
735
|
+
**Example - Dissolve Effect Exit**:
|
|
736
|
+
```typescript
|
|
737
|
+
{
|
|
738
|
+
key: 'content-section',
|
|
739
|
+
trigger: 'viewProgress',
|
|
740
|
+
effects: [
|
|
741
|
+
{
|
|
742
|
+
key: 'dissolving-content',
|
|
743
|
+
customEffect: (element, progress) => {
|
|
744
|
+
const particles = element.querySelectorAll('.content-particle');
|
|
745
|
+
const dissolveProgress = progress;
|
|
746
|
+
|
|
747
|
+
particles.forEach((particle, index) => {
|
|
748
|
+
const delay = (index / particles.length) * 0.5;
|
|
749
|
+
const particleProgress = Math.max(0, (dissolveProgress - delay) / (1 - delay));
|
|
750
|
+
|
|
751
|
+
particle.style.opacity = 1 - particleProgress;
|
|
752
|
+
particle.style.transform = `
|
|
753
|
+
translateY(${particleProgress * -100}px)
|
|
754
|
+
rotate(${particleProgress * 180}deg)
|
|
755
|
+
scale(${1 - particleProgress * 0.5})
|
|
756
|
+
`;
|
|
757
|
+
});
|
|
758
|
+
},
|
|
759
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: 10 } },
|
|
760
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 90 } },
|
|
761
|
+
fill: 'both',
|
|
762
|
+
effectId: 'dissolve-exit'
|
|
763
|
+
}
|
|
764
|
+
]
|
|
765
|
+
}
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
**Example - Data Visualization Exit**:
|
|
769
|
+
```typescript
|
|
770
|
+
{
|
|
771
|
+
key: 'data-visualization',
|
|
772
|
+
trigger: 'viewProgress',
|
|
773
|
+
effects: [
|
|
774
|
+
{
|
|
775
|
+
key: 'data-point',
|
|
776
|
+
customEffect: (element, progress) => {
|
|
777
|
+
const dataPoints = element.closest('interact-element')?.querySelectorAll('.data-point') || [];
|
|
778
|
+
const totalPoints = dataPoints.length;
|
|
779
|
+
const elementIndex = Array.from(dataPoints).indexOf(element);
|
|
780
|
+
|
|
781
|
+
// Staggered exit based on data point position
|
|
782
|
+
const staggerDelay = (elementIndex / totalPoints) * 0.3;
|
|
783
|
+
const adjustedProgress = Math.max(0, (progress - staggerDelay) / (1 - staggerDelay));
|
|
784
|
+
|
|
785
|
+
const scale = 1 - (adjustedProgress * 0.8);
|
|
786
|
+
const rotation = adjustedProgress * 720; // Two full rotations
|
|
787
|
+
const opacity = 1 - adjustedProgress;
|
|
788
|
+
|
|
789
|
+
element.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
790
|
+
element.style.opacity = opacity;
|
|
791
|
+
element.style.filter = `blur(${adjustedProgress * 10}px)`;
|
|
792
|
+
},
|
|
793
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: 0 } },
|
|
794
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 80 } },
|
|
795
|
+
fill: 'both',
|
|
796
|
+
effectId: 'data-exit'
|
|
797
|
+
}
|
|
798
|
+
]
|
|
799
|
+
}
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
---
|
|
803
|
+
|
|
804
|
+
## Advanced Patterns and Combinations
|
|
805
|
+
|
|
806
|
+
### Multi-Range ViewProgress Effects
|
|
807
|
+
Combining different ranges for complex scroll animations:
|
|
808
|
+
|
|
809
|
+
```typescript
|
|
810
|
+
{
|
|
811
|
+
key: 'complex-section',
|
|
812
|
+
trigger: 'viewProgress',
|
|
813
|
+
effects: [
|
|
814
|
+
// Entry phase
|
|
815
|
+
{
|
|
816
|
+
key: 'section-content',
|
|
817
|
+
keyframeEffect: {
|
|
818
|
+
name: 'content-entrance',
|
|
819
|
+
keyframes: [
|
|
820
|
+
{ opacity: '0', transform: 'translateY(50px)' },
|
|
821
|
+
{ opacity: '1', transform: 'translateY(0)' }
|
|
822
|
+
]
|
|
823
|
+
},
|
|
824
|
+
rangeStart: { name: 'entry', offset: { type: 'percentage', value: 0 } },
|
|
825
|
+
rangeEnd: { name: 'entry', offset: { type: 'percentage', value: 50 } },
|
|
826
|
+
easing: 'ease-out',
|
|
827
|
+
fill: 'backwards'
|
|
828
|
+
},
|
|
829
|
+
// Cover phase
|
|
830
|
+
{
|
|
831
|
+
key: 'background-element',
|
|
832
|
+
keyframeEffect: {
|
|
833
|
+
name: 'background-parallax-hue',
|
|
834
|
+
keyframes: [
|
|
835
|
+
{ transform: 'translateY(0)', filter: 'hue-rotate(0deg)' },
|
|
836
|
+
{ transform: 'translateY(-100px)', filter: 'hue-rotate(180deg)' }
|
|
837
|
+
]
|
|
838
|
+
},
|
|
839
|
+
rangeStart: { name: 'cover', offset: { type: 'percentage', value: 0 } },
|
|
840
|
+
rangeEnd: { name: 'cover', offset: { type: 'percentage', value: 100 } },
|
|
841
|
+
easing: 'linear',
|
|
842
|
+
fill: 'both'
|
|
843
|
+
},
|
|
844
|
+
// Exit phase
|
|
845
|
+
{
|
|
846
|
+
key: 'section-content',
|
|
847
|
+
keyframeEffect: {
|
|
848
|
+
name: 'content-exit',
|
|
849
|
+
keyframes: [
|
|
850
|
+
{ opacity: '1', transform: 'scale(1)' },
|
|
851
|
+
{ opacity: '0', transform: 'scale(0.8)' }
|
|
852
|
+
]
|
|
853
|
+
},
|
|
854
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: 50 } },
|
|
855
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 100 } },
|
|
856
|
+
easing: 'ease-in',
|
|
857
|
+
fill: 'forwards'
|
|
858
|
+
}
|
|
859
|
+
]
|
|
860
|
+
}
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
### ViewProgress with Conditional Behavior
|
|
864
|
+
Responsive scroll animations:
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
{
|
|
868
|
+
key: 'responsive-parallax',
|
|
869
|
+
trigger: 'viewProgress',
|
|
870
|
+
conditions: ['desktop-only', 'prefers-motion'],
|
|
871
|
+
effects: [
|
|
872
|
+
{
|
|
873
|
+
key: 'parallax-bg',
|
|
874
|
+
keyframeEffect: {
|
|
875
|
+
name: 'parallax-bg',
|
|
876
|
+
keyframes: [
|
|
877
|
+
{ transform: 'translateY(0)' },
|
|
878
|
+
{ transform: 'translateY(-300px)' }
|
|
879
|
+
]
|
|
880
|
+
},
|
|
881
|
+
rangeStart: { name: 'cover', offset: { type: 'percentage', value: 0 } },
|
|
882
|
+
rangeEnd: { name: 'cover', offset: { type: 'percentage', value: 100 } },
|
|
883
|
+
easing: 'linear',
|
|
884
|
+
fill: 'both'
|
|
885
|
+
}
|
|
886
|
+
]
|
|
887
|
+
},
|
|
888
|
+
// Simplified version for mobile
|
|
889
|
+
{
|
|
890
|
+
key: 'responsive-parallax',
|
|
891
|
+
trigger: 'viewProgress',
|
|
892
|
+
conditions: ['mobile-only'],
|
|
893
|
+
effects: [
|
|
894
|
+
{
|
|
895
|
+
key: 'parallax-bg',
|
|
896
|
+
keyframeEffect: {
|
|
897
|
+
name: 'fade-out-bg',
|
|
898
|
+
keyframes: [
|
|
899
|
+
{ opacity: '1' },
|
|
900
|
+
{ opacity: '0.7' }
|
|
901
|
+
]
|
|
902
|
+
},
|
|
903
|
+
rangeStart: { name: 'exit', offset: { type: 'percentage', value: 0 } },
|
|
904
|
+
rangeEnd: { name: 'exit', offset: { type: 'percentage', value: 100 } },
|
|
905
|
+
easing: 'linear',
|
|
906
|
+
fill: 'both'
|
|
907
|
+
}
|
|
908
|
+
]
|
|
909
|
+
}
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
### Multiple Element Coordination
|
|
913
|
+
Orchestrating multiple elements with viewProgress:
|
|
914
|
+
|
|
915
|
+
```typescript
|
|
916
|
+
{
|
|
917
|
+
key: 'orchestrated-section',
|
|
918
|
+
trigger: 'viewProgress',
|
|
919
|
+
effects: [
|
|
920
|
+
{
|
|
921
|
+
key: 'bg-layer-1',
|
|
922
|
+
keyframeEffect: {
|
|
923
|
+
name: 'layer-1-parallax',
|
|
924
|
+
keyframes: [
|
|
925
|
+
{ transform: 'translateY(0)' },
|
|
926
|
+
{ transform: 'translateY(-50px)' }
|
|
927
|
+
]
|
|
928
|
+
},
|
|
929
|
+
rangeStart: { name: 'cover', offset: { type: 'percentage', value: 0 } },
|
|
930
|
+
rangeEnd: { name: 'cover', offset: { type: 'percentage', value: 100 } },
|
|
931
|
+
easing: 'linear',
|
|
932
|
+
fill: 'both'
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
key: 'bg-layer-2',
|
|
936
|
+
keyframeEffect: {
|
|
937
|
+
name: 'layer-2-parallax',
|
|
938
|
+
keyframes: [
|
|
939
|
+
{ transform: 'translateY(0)' },
|
|
940
|
+
{ transform: 'translateY(-100px)' }
|
|
941
|
+
]
|
|
942
|
+
},
|
|
943
|
+
rangeStart: { name: 'cover', offset: { type: 'percentage', value: 0 } },
|
|
944
|
+
rangeEnd: { name: 'cover', offset: { type: 'percentage', value: 100 } },
|
|
945
|
+
easing: 'linear',
|
|
946
|
+
fill: 'both'
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
key: 'fg-content',
|
|
950
|
+
keyframeEffect: {
|
|
951
|
+
name: 'layer-3-parallax',
|
|
952
|
+
keyframes: [
|
|
953
|
+
{ transform: 'translateY(0)' },
|
|
954
|
+
{ transform: 'translateY(-150px)' }
|
|
955
|
+
]
|
|
956
|
+
},
|
|
957
|
+
rangeStart: { name: 'cover', offset: { type: 'percentage', value: 0 } },
|
|
958
|
+
rangeEnd: { name: 'cover', offset: { type: 'percentage', value: 100 } },
|
|
959
|
+
easing: 'linear',
|
|
960
|
+
fill: 'both'
|
|
961
|
+
}
|
|
962
|
+
]
|
|
963
|
+
}
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
---
|
|
967
|
+
|
|
968
|
+
## Best Practices for ViewProgress Interactions
|
|
969
|
+
|
|
970
|
+
### Performance Guidelines
|
|
971
|
+
1. **Use `linear` easing** for most scroll effects to avoid jarring transitions
|
|
972
|
+
2. **Prefer `transform`, `filter`, and `opacity`** properties for hardware acceleration
|
|
973
|
+
|
|
974
|
+
### Range Configuration Guidelines
|
|
975
|
+
1. **Use appropriate range names**:
|
|
976
|
+
- `entry`: For animations that happen as element enters viewport
|
|
977
|
+
- `cover`: For animations while element is intersecting viewport
|
|
978
|
+
- `exit`: For animations as element leaves viewport
|
|
979
|
+
- `contain`: For animations while element is contained within viewport
|
|
980
|
+
|
|
981
|
+
2. **Offset Guidelines**:
|
|
982
|
+
- **0-100 values**: Represent percentage of the range
|
|
983
|
+
- **Start with broad ranges** (0-100) then refine
|
|
984
|
+
- **Use smaller ranges** (20-80) for more controlled animations
|
|
985
|
+
- **Avoid overlapping ranges** to prevent conflicting animations
|
|
986
|
+
- **Use 0-50% cover range or 0-100% entry range** for entry animations
|
|
987
|
+
- **Use 50-100% cover range or 0-100% exit range** for exit animations
|
|
988
|
+
|
|
989
|
+
### User Experience Guidelines
|
|
990
|
+
1. **Keep scroll animations subtle** to avoid motion sickness
|
|
991
|
+
2. **Ensure content remains readable** during animations
|
|
992
|
+
3. **Use progressive enhancement** - ensure content works without animations
|
|
993
|
+
4. **Test on various devices** for performance and smoothness
|
|
994
|
+
|
|
995
|
+
### Accessibility Considerations
|
|
996
|
+
1. **Respect `prefers-reduced-motion`** for all scroll animations
|
|
997
|
+
2. **Provide alternatives** for motion-sensitive users
|
|
998
|
+
3. **Don't rely solely on scroll animations** for important content
|
|
999
|
+
4. **Ensure keyboard navigation** still works with scroll effects
|
|
1000
|
+
|
|
1001
|
+
### Common Use Cases by Pattern
|
|
1002
|
+
|
|
1003
|
+
**Parallax/Continuous (Rules 1, 4, 7)**:
|
|
1004
|
+
- Background image parallax
|
|
1005
|
+
- Floating decorative elements
|
|
1006
|
+
- Continuous progress indicators
|
|
1007
|
+
- Multi-layer depth effects
|
|
1008
|
+
- Scroll-responsive backgrounds
|
|
1009
|
+
|
|
1010
|
+
**Entry Animation (Rules 2, 5, 8)**:
|
|
1011
|
+
- Content reveals on scroll
|
|
1012
|
+
- Progressive image loading
|
|
1013
|
+
- Element introductions
|
|
1014
|
+
- Staggered content appearance
|
|
1015
|
+
|
|
1016
|
+
**Exit Animation (Rules 3, 6, 9)**:
|
|
1017
|
+
- Hero content fade-out
|
|
1018
|
+
- Navigation hiding
|
|
1019
|
+
- Content dismissals
|
|
1020
|
+
- Scroll-out transitions
|
|
1021
|
+
- Element cleanup effects
|
|
1022
|
+
|
|
1023
|
+
### Troubleshooting Common Issues
|
|
1024
|
+
|
|
1025
|
+
**Janky scroll performance**:
|
|
1026
|
+
- Use hardware-accelerated properties only
|
|
1027
|
+
- Simplify custom effect calculations
|
|
1028
|
+
- Test on lower-end devices
|
|
1029
|
+
|
|
1030
|
+
**Unexpected animation behavior**:
|
|
1031
|
+
- Check range configurations match intended behavior
|
|
1032
|
+
- Verify source element visibility throughout scroll
|
|
1033
|
+
- Ensure target elements exist and are selectable
|
|
1034
|
+
- Test range offset values
|
|
1035
|
+
|
|
1036
|
+
**Poor visual results**:
|
|
1037
|
+
- Adjust easing functions for scroll context
|
|
1038
|
+
- Fine-tune range start/end percentages
|
|
1039
|
+
- Consider element positioning and layering
|
|
1040
|
+
- Test across different content heights
|
|
1041
|
+
|
|
1042
|
+
---
|
|
1043
|
+
|
|
1044
|
+
These rules provide comprehensive coverage for ViewProgress trigger interactions in `@wix/interact`, supporting all range types (entry, cover, contain, exit) and effect types (named, keyframe, custom) as outlined in the development plan Stage 1.4.
|