@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,1074 +1,148 @@
1
1
  # PointerMove Trigger Rules for @wix/interact
2
2
 
3
- These rules help generate pointer-driven interactions using the `@wix/interact` library. PointerMove triggers create real-time animations that respond to mouse movement over elements, perfect for 3D effects, cursor followers, and interactive cards.
3
+ These rules help generate pointer-driven interactions using `@wix/interact`. PointerMove triggers create real-time animations that respond to mouse movement over elements or the entire viewport.
4
4
 
5
- ## Core Concepts
5
+ ## Table of Contents
6
6
 
7
- ### Effect Types for PointerMove
7
+ - [Trigger Source Elements with `hitArea: 'self'`](#trigger-source-elements-with-hitarea-self)
8
+ - [PointerMoveParams](#pointermoveparams)
9
+ - [Progress Object Structure](#progress-object-structure)
10
+ - [Centering with `centeredToTarget`](#centering-with-centeredtotarget)
11
+ - [Device Conditions](#device-conditions)
12
+ - [Rule 1: namedEffect](#rule-1-namedeffect)
13
+ - [Rule 2: keyframeEffect with Single Axis](#rule-2-keyframeeffect-with-single-axis)
14
+ - [Rule 3: Two keyframeEffects with Two Axes and `composite`](#rule-3-two-keyframeeffects-with-two-axes-and-composite)
15
+ - [Rule 4: customEffect](#rule-4-customeffect)
8
16
 
9
- The `pointerMove` trigger provides 2D progress (x and y coordinates). You can use:
17
+ ## Trigger Source Elements with `hitArea: 'self'`
10
18
 
11
- 1. **`namedEffect`** (Preferred): Pre-built mouse presets from `@wix/motion-presets` that handle 2D progress internally
12
- 2. **`customEffect`** (Advanced): Custom function receiving the 2D progress object for full control
13
- 3. **`keyframeEffect`** (Single-axis): The pointer position on a single axis is mapped to linear 0-1 progress for keyframe animations. Use `axis: 'x'` or `axis: 'y'` (defaults to `'y'`)
19
+ When using `hitArea: 'self'`, the source element is the hit area for pointer tracking:
14
20
 
15
- ### Hit Area Configuration (`hitArea`)
21
+ - The source element **MUST NOT** have `pointer-events: none` — it needs to receive pointer events.
22
+ - **CRITICAL**: MUST AVOID using the same element as both source and target with effects that change size or position (e.g. `transform: translate(…)`, `scale(…)`). The transform shifts the hit area, causing jittery re-entry cycles. Instead, use `selector` to target a child element for the animation.
16
23
 
17
- The `hitArea` parameter determines where mouse movement is tracked:
18
-
19
- | Value | Behavior | Best For |
20
- | -------- | ----------------------------------------------------- | ---------------------------------------- |
21
- | `'self'` | Tracks mouse within the source element's bounds only | Local hover effects, card interactions |
22
- | `'root'` | Tracks mouse anywhere in the viewport (document root) | Global cursor followers, ambient effects |
23
-
24
- ### Progress Object Structure (for `customEffect`)
25
-
26
- When using `customEffect` with `pointerMove`, the progress parameter is an object:
27
-
28
- ```typescript
29
- type Progress = {
30
- x: number; // 0-1: horizontal position (0 = left edge, 1 = right edge)
31
- y: number; // 0-1: vertical position (0 = top edge, 1 = bottom edge)
32
- v?: {
33
- // Velocity (optional)
34
- x: number; // Horizontal velocity
35
- y: number; // Vertical velocity
36
- };
37
- active?: boolean; // Whether mouse is currently in the hit area
38
- };
39
- ```
40
-
41
- ### Centering with `centeredToTarget`
42
-
43
- Controls how the progress range is calculated:
44
-
45
- | Value | Behavior | Use When |
46
- | ------- | -------------------------------------------------- | ---------------------------------------- |
47
- | `true` | Centers the coordinate range at the target element | Source and target are different elements |
48
- | `false` | Uses source element bounds for calculations | Cursor followers, global effects |
49
-
50
- ## Rule 1: Single Element Pointer Effects with 3D Named Effects
51
-
52
- **Use Case**: Interactive 3D transformations on individual elements that respond to mouse position (e.g., card tilting, 3D product showcases, interactive buttons)
53
-
54
- **When to Apply**:
55
-
56
- - For interactive card hover effects
57
- - When creating 3D product showcases
58
- - For engaging button interactions
59
- - When building interactive UI elements that respond to mouse movement
60
-
61
- **Pattern**:
62
-
63
- ```typescript
64
- {
65
- key: '[SOURCE_KEY]',
66
- trigger: 'pointerMove',
67
- params: {
68
- hitArea: '[HIT_AREA]'
69
- },
70
- effects: [
71
- {
72
- key: '[TARGET_KEY]',
73
- namedEffect: {
74
- type: '[3D_EFFECT_TYPE]',
75
- [EFFECT_PROPERTIES]
76
- },
77
- centeredToTarget: [CENTERED_TO_TARGET],
78
- effectId: '[UNIQUE_EFFECT_ID]'
79
- }
80
- ]
81
- }
82
- ```
83
-
84
- **Variables**:
85
-
86
- - `[SOURCE_KEY]`: Unique identifier for source element that tracks mouse movement
87
- - `[TARGET_KEY]`: Unique identifier for target element to animate (can be same as source or different)
88
- - `[HIT_AREA]`: 'self' (mouse within source element) or 'root' (mouse anywhere in viewport)
89
- - `[3D_EFFECT_TYPE]`: 'Tilt3DMouse', 'Track3DMouse', 'SwivelMouse'
90
- - `[EFFECT_PROPERTIES]`: Named effect specific properties (angle, perspective, direction, etc.)
91
- - `[CENTERED_TO_TARGET]`: true (center range at target) or false (use source element bounds)
92
- - `[UNIQUE_EFFECT_ID]`: Optional unique identifier
93
-
94
- **Example - Interactive Product Card**:
95
-
96
- ```typescript
97
- {
98
- key: 'product-card',
99
- trigger: 'pointerMove',
100
- params: {
101
- hitArea: 'self'
102
- },
103
- effects: [
104
- {
105
- key: 'product-card',
106
- namedEffect: {
107
- type: 'Tilt3DMouse',
108
- angle: 15,
109
- perspective: 1000
110
- }
111
- }
112
- ]
113
- }
114
- ```
115
-
116
- ---
117
-
118
- ## Rule 2: Single Element Pointer Effects with Movement Named Effects
119
-
120
- **Use Case**: Cursor-following and position-tracking effects on individual elements (e.g., floating elements, cursor followers, responsive decorations)
121
-
122
- **When to Apply**:
123
-
124
- - For cursor-following elements
125
- - When creating floating responsive decorations
126
- - For interactive element positioning
127
- - When building mouse-aware UI components
128
-
129
- **Pattern**:
130
-
131
- ```typescript
132
- {
133
- key: '[SOURCE_KEY]',
134
- trigger: 'pointerMove',
135
- params: {
136
- hitArea: '[HIT_AREA]'
137
- },
138
- effects: [
139
- {
140
- key: '[TARGET_KEY]',
141
- namedEffect: {
142
- type: '[MOVEMENT_EFFECT_TYPE]',
143
- distance: { value: [DISTANCE_VALUE], unit: '[DISTANCE_UNIT]' },
144
- axis: '[AXIS_CONSTRAINT]'
145
- },
146
- centeredToTarget: [CENTERED_TO_TARGET],
147
- effectId: '[UNIQUE_EFFECT_ID]'
148
- }
149
- ]
150
- }
151
- ```
152
-
153
- **Variables**:
154
-
155
- - `[MOVEMENT_EFFECT_TYPE]`: 'TrackMouse', 'AiryMouse', 'BounceMouse'
156
- - `[DISTANCE_VALUE]`: Numeric value for movement distance
157
- - `[DISTANCE_UNIT]`: 'px', 'percentage', 'vw', 'vh'
158
- - `[AXIS_CONSTRAINT]`: 'both', 'horizontal', 'vertical'
159
- - Other variables same as Rule 1
160
-
161
- **Example - Cursor Follower Element**:
162
-
163
- ```typescript
164
- {
165
- key: 'cursor-follower',
166
- trigger: 'pointerMove',
167
- params: {
168
- hitArea: 'root'
169
- },
170
- effects: [
171
- {
172
- namedEffect: {
173
- type: 'TrackMouse',
174
- distance: { value: 50, unit: 'percentage' },
175
- axis: 'both'
176
- },
177
- centeredToTarget: false
178
- }
179
- ]
180
- }
181
- ```
182
-
183
- **Example - Floating Decoration**:
184
-
185
- ```typescript
186
- {
187
- key: 'hero-section',
188
- trigger: 'pointerMove',
189
- params: {
190
- hitArea: 'self'
191
- },
192
- effects: [
193
- {
194
- key: 'floating-element',
195
- namedEffect: {
196
- type: 'AiryMouse',
197
- distance: { value: 30, unit: 'px' },
198
- axis: 'both'
199
- },
200
- centeredToTarget: true,
201
- effectId: 'hero-float'
202
- }
203
- ]
204
- }
205
- ```
206
-
207
- ---
208
-
209
- ## Rule 3: Single Element Pointer Effects with Scale Named Effects
210
-
211
- **Use Case**: Dynamic scaling and deformation effects on individual elements based on mouse position (e.g., interactive scaling, organic transformations, blob effects)
212
-
213
- **When to Apply**:
214
-
215
- - For interactive scaling buttons
216
- - When creating organic blob-like interactions
217
- - For dynamic size responsive elements
218
- - When building creative morphing interfaces
219
-
220
- **Pattern**:
221
-
222
- ```typescript
223
- {
224
- key: '[SOURCE_KEY]',
225
- trigger: 'pointerMove',
226
- params: {
227
- hitArea: '[HIT_AREA]'
228
- },
229
- effects: [
230
- {
231
- key: '[TARGET_KEY]',
232
- namedEffect: {
233
- type: '[SCALE_EFFECT_TYPE]',
234
- [SCALE_PROPERTIES]
235
- },
236
- centeredToTarget: [CENTERED_TO_TARGET],
237
- effectId: '[UNIQUE_EFFECT_ID]'
238
- }
239
- ]
240
- }
241
- ```
242
-
243
- **Variables**:
244
-
245
- - `[SCALE_EFFECT_TYPE]`: 'ScaleMouse', 'BlobMouse', 'SkewMouse'
246
- - `[SCALE_PROPERTIES]`: Effect-specific properties (scale, distance, axis)
247
- - Other variables same as Rule 1
248
-
249
- **Example - Interactive Scale Button**:
250
-
251
- ```typescript
252
- {
253
- key: 'scale-button',
254
- trigger: 'pointerMove',
255
- params: {
256
- hitArea: 'self'
257
- },
258
- effects: [
259
- {
260
- key: 'scale-button',
261
- namedEffect: {
262
- type: 'ScaleMouse',
263
- scale: 1.1,
264
- distance: { value: 100, unit: 'px' },
265
- axis: 'both'
266
- },
267
- centeredToTarget: true
268
- }
269
- ]
270
- }
271
- ```
272
-
273
- **Example - Organic Blob Effect**:
274
-
275
- ```typescript
276
- {
277
- key: 'blob-container',
278
- trigger: 'pointerMove',
279
- params: {
280
- hitArea: 'self'
281
- },
282
- effects: [
283
- {
284
- key: 'blob-shape',
285
- namedEffect: {
286
- type: 'BlobMouse',
287
- intensity: 0.8,
288
- smoothness: 0.6
289
- },
290
- effectId: 'blob-morph'
291
- }
292
- ]
293
- }
294
- ```
295
-
296
- ---
297
-
298
- ## Rule 4: Single Element Pointer Effects with Visual Named Effects
299
-
300
- **Use Case**: Visual effect transformations on individual elements based on mouse position (e.g., motion blur, rotation effects, visual filters)
301
-
302
- **When to Apply**:
303
-
304
- - For creative visual interfaces
305
- - When adding motion blur to interactions
306
- - For rotation-based mouse effects
307
- - When creating dynamic visual feedback
308
-
309
- **Pattern**:
310
-
311
- ```typescript
312
- {
313
- key: '[SOURCE_KEY]',
314
- trigger: 'pointerMove',
315
- params: {
316
- hitArea: '[HIT_AREA]'
317
- },
318
- effects: [
319
- {
320
- key: '[TARGET_KEY]',
321
- namedEffect: {
322
- type: '[VISUAL_EFFECT_TYPE]',
323
- [VISUAL_PROPERTIES]
324
- },
325
- centeredToTarget: [CENTERED_TO_TARGET],
326
- effectId: '[UNIQUE_EFFECT_ID]'
327
- }
328
- ]
329
- }
330
- ```
331
-
332
- **Variables**:
333
-
334
- - `[VISUAL_EFFECT_TYPE]`: 'BlurMouse', 'SpinMouse'
335
- - `[VISUAL_PROPERTIES]`: Effect-specific properties (blur amount, rotation speed)
336
- - Other variables same as Rule 1
337
-
338
- **Example - Motion Blur Card**:
339
-
340
- ```typescript
341
- {
342
- key: 'motion-card',
343
- trigger: 'pointerMove',
344
- params: {
345
- hitArea: 'self'
346
- },
347
- effects: [
348
- {
349
- key: 'motion-card',
350
- namedEffect: {
351
- type: 'BlurMouse',
352
- blurAmount: 5,
353
- motionIntensity: 0.7
354
- },
355
- centeredToTarget: true
356
- }
357
- ]
358
- }
359
- ```
360
-
361
- **Example - Spinning Element**:
362
-
363
- ```typescript
364
- {
365
- key: 'spin-trigger',
366
- trigger: 'pointerMove',
367
- params: {
368
- hitArea: 'self'
369
- },
370
- effects: [
371
- {
372
- key: 'spinning-icon',
373
- namedEffect: {
374
- type: 'SpinMouse',
375
- rotationSpeed: 0.5,
376
- direction: 'clockwise'
377
- },
378
- centeredToTarget: false,
379
- effectId: 'icon-spin'
380
- }
381
- ]
382
- }
383
- ```
384
-
385
- ---
386
-
387
- ## Rule 5: Multi-Element Pointer Parallax Effects with Named Effects
388
-
389
- **Use Case**: Coordinated pointer-driven animations across multiple elements creating layered parallax effects (e.g., multi-layer backgrounds, depth effects, coordinated element responses)
390
-
391
- **When to Apply**:
392
-
393
- - For multi-layer background effects
394
- - When creating depth and parallax interactions
395
- - For coordinated UI element responses
396
- - When building immersive pointer-driven experiences
397
-
398
- **Pattern**:
399
-
400
- ```typescript
401
- {
402
- key: '[CONTAINER_KEY]',
403
- trigger: 'pointerMove',
404
- params: {
405
- hitArea: '[HIT_AREA]'
406
- },
407
- effects: [
408
- {
409
- key: '[BACKGROUND_LAYER_KEY]',
410
- namedEffect: {
411
- type: '[BACKGROUND_EFFECT_TYPE]',
412
- distance: { value: [BACKGROUND_DISTANCE], unit: '[DISTANCE_UNIT]' }
413
- },
414
- centeredToTarget: [CENTERED_TO_TARGET]
415
- },
416
- {
417
- key: '[MIDGROUND_LAYER_KEY]',
418
- namedEffect: {
419
- type: '[MIDGROUND_EFFECT_TYPE]',
420
- distance: { value: [MIDGROUND_DISTANCE], unit: '[DISTANCE_UNIT]' }
421
- },
422
- centeredToTarget: [CENTERED_TO_TARGET]
423
- },
424
- {
425
- key: '[FOREGROUND_LAYER_KEY]',
426
- namedEffect: {
427
- type: '[FOREGROUND_EFFECT_TYPE]',
428
- distance: { value: [FOREGROUND_DISTANCE], unit: '[DISTANCE_UNIT]' }
429
- },
430
- centeredToTarget: [CENTERED_TO_TARGET]
431
- }
432
- ]
433
- }
434
- ```
435
-
436
- **Variables**:
437
-
438
- - `[CONTAINER_KEY]`: Unique identifier for container element tracking mouse
439
- - `[*_LAYER_KEY]`: Unique identifier for different layer elements
440
- - `[*_EFFECT_TYPE]`: Named effects for each layer (typically movement effects)
441
- - `[*_DISTANCE]`: Movement distance for each layer (creating depth)
442
- - Other variables same as previous rules
443
-
444
- **Example - Parallax Card Layers**:
445
-
446
- ```typescript
447
- {
448
- key: 'parallax-card',
449
- trigger: 'pointerMove',
450
- params: {
451
- hitArea: 'self'
452
- },
453
- effects: [
454
- {
455
- key: 'bg-layer',
456
- namedEffect: {
457
- type: 'AiryMouse',
458
- distance: { value: 15, unit: 'px' },
459
- axis: 'both'
460
- },
461
- centeredToTarget: true
462
- },
463
- {
464
- key: 'mid-layer',
465
- namedEffect: {
466
- type: 'TrackMouse',
467
- distance: { value: 25, unit: 'px' },
468
- axis: 'both'
469
- },
470
- centeredToTarget: true
471
- },
472
- {
473
- key: 'fg-layer',
474
- namedEffect: {
475
- type: 'BounceMouse',
476
- distance: { value: 35, unit: 'px' },
477
- axis: 'both'
478
- },
479
- centeredToTarget: true
480
- }
481
- ]
482
- }
483
- ```
484
-
485
- **Example - Multi-Layer Hero Section**:
486
-
487
- ```typescript
488
- {
489
- key: 'hero-container',
490
- trigger: 'pointerMove',
491
- params: {
492
- hitArea: 'self'
493
- },
494
- effects: [
495
- {
496
- key: 'hero-bg',
497
- namedEffect: {
498
- type: 'AiryMouse',
499
- distance: { value: 20, unit: 'px' },
500
- axis: 'both'
501
- },
502
- centeredToTarget: true
503
- },
504
- {
505
- key: 'hero-content',
506
- namedEffect: {
507
- type: 'TrackMouse',
508
- distance: { value: 40, unit: 'px' },
509
- axis: 'horizontal'
510
- },
511
- centeredToTarget: true
512
- },
513
- {
514
- key: 'hero-decorations',
515
- namedEffect: {
516
- type: 'ScaleMouse',
517
- scale: 1.05,
518
- distance: { value: 60, unit: 'px' }
519
- },
520
- centeredToTarget: true
521
- }
522
- ]
523
- }
524
- ```
525
-
526
- ---
527
-
528
- ## Rule 6: Coordinated Group Pointer Effects with Named Effects
529
-
530
- **Use Case**: Synchronized pointer-driven animations across related elements with different responses (e.g., card grids, navigation menus, interactive galleries)
531
-
532
- **When to Apply**:
533
-
534
- - For interactive card grids
535
- - When building responsive navigation systems
536
- - For gallery hover effects
537
- - When creating coordinated interface responses
538
-
539
- **Pattern**:
540
-
541
- ```typescript
542
- {
543
- key: '[CONTAINER_KEY]',
544
- trigger: 'pointerMove',
545
- params: {
546
- hitArea: '[HIT_AREA]'
547
- },
548
- effects: [
549
- {
550
- key: '[PRIMARY_ELEMENTS_KEY]',
551
- namedEffect: {
552
- type: '[PRIMARY_EFFECT_TYPE]',
553
- [PRIMARY_EFFECT_PROPERTIES]
554
- },
555
- centeredToTarget: [PRIMARY_CENTERED]
556
- },
557
- {
558
- key: '[SECONDARY_ELEMENTS_KEY]',
559
- namedEffect: {
560
- type: '[SECONDARY_EFFECT_TYPE]',
561
- [SECONDARY_EFFECT_PROPERTIES]
562
- },
563
- centeredToTarget: [SECONDARY_CENTERED]
564
- }
565
- ]
566
- }
567
- ```
568
-
569
- **Variables**:
570
-
571
- - `[PRIMARY_ELEMENTS_KEY]`: Unique identifier for primary responsive elements
572
- - `[SECONDARY_ELEMENTS_KEY]`: Unique identifier for secondary responsive elements
573
- - `[PRIMARY_EFFECT_TYPE]`: Named effect for primary elements
574
- - `[SECONDARY_EFFECT_TYPE]`: Named effect for secondary elements
575
- - `[*_EFFECT_PROPERTIES]`: Properties specific to each effect type
576
- - `[*_CENTERED]`: Centering configuration for each element group
577
- - Other variables same as previous rules
578
-
579
- **Example - Interactive Card Grid**:
580
-
581
- ```typescript
582
- {
583
- key: 'card-grid',
584
- trigger: 'pointerMove',
585
- params: {
586
- hitArea: 'self'
587
- },
588
- effects: [
589
- {
590
- key: 'grid-card',
591
- namedEffect: {
592
- type: 'Tilt3DMouse',
593
- angle: 12,
594
- perspective: 1000
595
- },
596
- centeredToTarget: true
597
- },
598
- {
599
- key: 'card-shadow',
600
- namedEffect: {
601
- type: 'AiryMouse',
602
- distance: { value: 20, unit: 'px' },
603
- axis: 'both'
604
- },
605
- centeredToTarget: true
606
- }
607
- ]
608
- }
609
- ```
610
-
611
- **Example - Navigation Menu Response**:
612
-
613
- ```typescript
614
- {
615
- key: 'nav-container',
616
- trigger: 'pointerMove',
617
- params: {
618
- hitArea: 'self'
619
- },
620
- effects: [
621
- {
622
- key: 'nav-item',
623
- namedEffect: {
624
- type: 'ScaleMouse',
625
- scale: 1.05,
626
- distance: { value: 80, unit: 'px' }
627
- },
628
- centeredToTarget: true
629
- },
630
- {
631
- key: 'nav-indicator',
632
- namedEffect: {
633
- type: 'TrackMouse',
634
- distance: { value: 15, unit: 'px' },
635
- axis: 'horizontal'
636
- },
637
- centeredToTarget: false
638
- }
639
- ]
640
- }
641
- ```
642
-
643
- ---
644
-
645
- ## Rule 7: Global Cursor Follower Effects with Named Effects
646
-
647
- **Use Case**: Page-wide cursor following elements that respond to mouse movement anywhere (e.g., custom cursors, global decorative followers, interactive overlays)
648
-
649
- **When to Apply**:
650
-
651
- - For custom cursor implementations
652
- - When creating global interactive overlays
653
- - For page-wide decorative followers
654
- - When building immersive cursor experiences
655
-
656
- **Pattern**:
657
-
658
- ```typescript
659
- {
660
- key: '[FOLLOWER_KEY]',
661
- trigger: 'pointerMove',
662
- params: {
663
- hitArea: 'root'
664
- },
665
- effects: [
666
- {
667
- namedEffect: {
668
- type: '[FOLLOWER_EFFECT_TYPE]',
669
- distance: { value: [FOLLOWER_DISTANCE], unit: '[DISTANCE_UNIT]' },
670
- [FOLLOWER_PROPERTIES]
671
- },
672
- centeredToTarget: false,
673
- effectId: '[FOLLOWER_EFFECT_ID]'
674
- }
675
- ]
676
- }
677
- ```
678
-
679
- **Variables**:
680
-
681
- - `[FOLLOWER_KEY]`: Unique identifier for cursor follower element
682
- - `[FOLLOWER_EFFECT_TYPE]`: 'TrackMouse', 'AiryMouse', 'BounceMouse'
683
- - `[FOLLOWER_DISTANCE]`: Distance/lag for follower (0 for perfect following)
684
- - `[FOLLOWER_PROPERTIES]`: Additional effect properties
685
- - `[FOLLOWER_EFFECT_ID]`: Unique identifier for the follower effect
686
- - Other variables same as previous rules
687
-
688
- **Example - Custom Cursor Follower**:
689
-
690
- ```typescript
691
- {
692
- key: 'custom-cursor',
693
- trigger: 'pointerMove',
694
- params: {
695
- hitArea: 'root'
696
- },
697
- effects: [
698
- {
699
- namedEffect: {
700
- type: 'TrackMouse',
701
- distance: { value: 0, unit: 'px' },
702
- axis: 'both'
703
- },
704
- centeredToTarget: false,
705
- effectId: 'global-cursor'
706
- }
707
- ]
708
- }
709
- ```
710
-
711
- **Example - Floating Decoration Follower**:
712
-
713
- ```typescript
714
- {
715
- key: 'floating-decoration',
716
- trigger: 'pointerMove',
717
- params: {
718
- hitArea: 'root'
719
- },
720
- effects: [
721
- {
722
- namedEffect: {
723
- type: 'AiryMouse',
724
- distance: { value: 50, unit: 'px' },
725
- axis: 'both'
726
- },
727
- centeredToTarget: false,
728
- effectId: 'decoration-follower'
729
- }
730
- ]
731
- }
732
- ```
733
-
734
- ---
735
-
736
- ## Rule 8: Custom Pointer Effects with customEffect
737
-
738
- **Use Case**: When you need full control over pointer-driven animations that cannot be achieved with named effects, such as custom physics, complex multi-property animations, or unique visual transformations.
739
-
740
- **When to Apply**:
741
-
742
- - For custom physics-based animations
743
- - When creating unique visual effects not covered by named effects
744
- - When controlling WebGL/WebGPU effects or other JavaScript controlled effects
745
- - For complex DOM manipulations based on mouse position
746
- - When implementing grid-based or particle effects
747
- - For animations requiring access to velocity data
748
-
749
- **IMPORTANT**: Only use `customEffect` when `namedEffect` cannot achieve the desired result. Named effects are optimized and GPU-friendly.
750
-
751
- **Pattern - Basic customEffect**:
752
-
753
- ```typescript
754
- {
755
- key: '[SOURCE_KEY]',
756
- trigger: 'pointerMove',
757
- params: {
758
- hitArea: '[HIT_AREA]'
759
- },
760
- effects: [
761
- {
762
- key: '[TARGET_KEY]',
763
- customEffect: (element, progress) => {
764
- // progress.x: 0-1 horizontal position
765
- // progress.y: 0-1 vertical position
766
- // progress.v: { x, y } velocity (optional)
767
- // progress.active: boolean (optional)
768
-
769
- [CUSTOM_ANIMATION_LOGIC]
770
- },
771
- centeredToTarget: [CENTERED_TO_TARGET]
772
- }
773
- ]
774
- }
775
- ```
776
-
777
- **Variables**:
24
+ ---
778
25
 
779
- - `[SOURCE_KEY]`: Unique identifier for source element tracking mouse movement
780
- - `[TARGET_KEY]`: Unique identifier for target element to animate
781
- - `[HIT_AREA]`: 'self' or 'root'
782
- - `[CUSTOM_ANIMATION_LOGIC]`: Your custom animation code using the progress object
783
- - `[CENTERED_TO_TARGET]`: true or false
26
+ ## PointerMoveParams
784
27
 
785
- **Example - Custom Rotation Based on Mouse Position**:
28
+ `params` object for `pointerMove` interactions:
786
29
 
787
30
  ```typescript
788
- {
789
- key: 'rotation-container',
790
- trigger: 'pointerMove',
791
- params: {
792
- hitArea: 'self'
793
- },
794
- effects: [
795
- {
796
- customEffect: (element, progress) => {
797
- // Convert progress to angle (0-360 degrees)
798
- const angle = Math.atan2(
799
- progress.y - 0.5,
800
- progress.x - 0.5
801
- ) * (180 / Math.PI);
802
-
803
- element.style.transform = `rotate(${angle}deg)`;
804
- },
805
- centeredToTarget: true
806
- }
807
- ]
808
- }
31
+ type PointerMoveParams = {
32
+ hitArea?: 'root' | 'self';
33
+ axis?: 'x' | 'y';
34
+ };
809
35
  ```
810
36
 
811
- **Example - Magnetic Effect with Distance Calculation**:
37
+ ### Properties
812
38
 
813
- ```typescript
814
- {
815
- key: 'magnetic-button',
816
- trigger: 'pointerMove',
817
- params: {
818
- hitArea: 'self'
819
- },
820
- effects: [
821
- {
822
- customEffect: (element, progress) => {
823
- // Calculate distance from center (0.5, 0.5)
824
- const dx = (progress.x - 0.5) * 2; // -1 to 1
825
- const dy = (progress.y - 0.5) * 2; // -1 to 1
39
+ - `hitArea` — determines where mouse movement is tracked:
40
+ - `'self'` — tracks pointer within the source element's bounds only. Use for local pointer-tracking effects on a specific element.
41
+ - `'root'` — tracks pointer anywhere in the viewport. Use for global cursor followers, ambient effects.
42
+ - `axis` — restricts pointer tracking to a single axis. Used with `keyframeEffect` to map one axis to 0–1 progress; ignored by `namedEffect` and `customEffect` which receive the full 2D progress:
43
+ - `'x'` — maps horizontal pointer position to 0–1 progress for keyframe interpolation.
44
+ - `'y'` — maps vertical pointer position to 0–1 progress for keyframe interpolation. **Default** when `keyframeEffect` is used.
45
+ - For `namedEffect` or `customEffect` both axes are available via the 2D progress object, and will be ignored.
826
46
 
827
- // Apply magnetic pull effect
828
- const maxMove = 20; // pixels
829
- const moveX = dx * maxMove;
830
- const moveY = dy * maxMove;
47
+ ---
831
48
 
832
- element.style.transform = `translate(${moveX}px, ${moveY}px)`;
833
- },
834
- centeredToTarget: true
835
- }
836
- ]
837
- }
838
- ```
49
+ ## Progress Object Structure
839
50
 
840
- **Example - Velocity-Based Motion Blur**:
51
+ When using `customEffect` with `pointerMove`, the progress parameter is an object:
841
52
 
842
53
  ```typescript
843
- {
844
- key: 'velocity-element',
845
- trigger: 'pointerMove',
846
- params: {
847
- hitArea: 'root'
848
- },
849
- effects: [
850
- {
851
- customEffect: (element, progress) => {
852
- // Use velocity for motion blur intensity
853
- const velocity = progress.v || { x: 0, y: 0 };
854
- const speed = Math.sqrt(velocity.x ** 2 + velocity.y ** 2);
855
-
856
- // Apply blur based on speed
857
- const blurAmount = Math.min(speed * 0.5, 10);
858
- element.style.filter = `blur(${blurAmount}px)`;
859
-
860
- // Move element towards mouse
861
- const offsetX = (progress.x - 0.5) * 100;
862
- const offsetY = (progress.y - 0.5) * 100;
863
- element.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
864
- },
865
- centeredToTarget: false
866
- }
867
- ]
868
- }
54
+ type Progress = {
55
+ x: number; // 0-1: horizontal position (0 = left edge, 1 = right edge)
56
+ y: number; // 0-1: vertical position (0 = top edge, 1 = bottom edge)
57
+ v?: {
58
+ x: number; // Horizontal velocity: negative = moving left, positive = moving right. Magnitude reflects speed.
59
+ y: number; // Vertical velocity: negative = moving up, positive = moving down. Magnitude reflects speed.
60
+ };
61
+ active?: boolean; // Whether mouse is currently in the hit area
62
+ };
869
63
  ```
870
64
 
871
- **Example - Grid Cell Rotation Effect**:
872
-
873
- ```typescript
874
- // First, cache grid cell positions for performance
875
- const cellCache = new Map();
876
- // Cache viewport size
877
- const windowWidth = window.innerWidth;
878
- const windowHeight = window.innerHeight;
879
- // ... populate cache with cell center positions
880
- // ... update `windowWidth/height` on window `resize` event
65
+ ---
881
66
 
882
- {
883
- key: 'interactive-grid',
884
- trigger: 'pointerMove',
885
- params: {
886
- hitArea: 'root'
887
- },
888
- effects: [
889
- {
890
- customEffect: (element, progress) => {
891
- // Convert progress to viewport coordinates
892
- const mouseX = progress.x * windowWidth;
893
- const mouseY = progress.y * windowHeight;
67
+ ## Centering with `centeredToTarget`
894
68
 
895
- // Iterate through cached grid cells
896
- for (const [cell, cache] of cellCache) {
897
- const deltaX = mouseX - cache.x;
898
- const deltaY = mouseY - cache.y;
69
+ Controls which element's bounds define the 0–1 progress range.
899
70
 
900
- // Calculate angle pointing towards mouse
901
- const angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI) + 90;
71
+ - **`false` (default)**: Progress is calculated against the **source element's** (or viewport's) bounds. The `50%` progress of the timeline is at the center of the source element.
72
+ - **`true`**: `50%` progress of the timeline is calculated against the **target element's center**. The edges of the timeline are still calculated against the edges of the source element/viewport depending on `hitAea`.
902
73
 
903
- // Calculate distance-based intensity
904
- const dist = Math.sqrt(deltaX ** 2 + deltaY ** 2);
905
- const intensity = Math.max(0, 1 - dist / 500);
74
+ ---
906
75
 
907
- cell.style.transform = `rotate(${angle}deg) scale(${1 + intensity * 0.2})`;
908
- }
909
- },
910
- centeredToTarget: false
911
- }
912
- ]
913
- }
914
- ```
76
+ ## Device Conditions
915
77
 
916
- **Example - Active State Handling**:
78
+ `pointerMove` works best on hover-capable devices. Use a `conditions` entry with a `(hover: hover)` media query to prevent the interaction from registering on touch-only devices. On touch-only devices, consider a fallback to `viewEnter` or `viewProgress` based interactions:
917
79
 
918
80
  ```typescript
919
81
  {
920
- key: 'active-aware-element',
921
- trigger: 'pointerMove',
922
- params: {
923
- hitArea: 'self'
82
+ conditions: {
83
+ '[CONDITION_NAME]': { type: 'media', predicate: '(hover: hover)' }
924
84
  },
925
- effects: [
85
+ interactions: [
926
86
  {
927
- customEffect: (element, progress) => {
928
- if (!progress.active) {
929
- // Mouse left the hit area - reset or animate out
930
- element.style.transform = 'scale(1)';
931
- element.style.opacity = '0.7';
932
- return;
933
- }
934
-
935
- // Mouse is active in hit area
936
- const scale = 1 + (1 - Math.abs(progress.x - 0.5) * 2) * 0.1;
937
- element.style.transform = `scale(${scale})`;
938
- element.style.opacity = '1';
939
- },
940
- centeredToTarget: true
87
+ key: '[SOURCE_KEY]',
88
+ trigger: 'pointerMove',
89
+ conditions: ['[CONDITION_NAME]'],
90
+ params: { hitArea: '[HIT_AREA]' },
91
+ effects: [ /* ... */ ]
941
92
  }
942
93
  ]
943
94
  }
944
95
  ```
945
96
 
946
- ### customEffect with Transition Smoothing
947
-
948
- For smoother animations, you can use `transitionDuration` and `transitionEasing`:
949
-
950
- ```typescript
951
- {
952
- key: 'smooth-custom',
953
- trigger: 'pointerMove',
954
- params: {
955
- hitArea: 'self'
956
- },
957
- effects: [
958
- {
959
- customEffect: (element, progress) => {
960
- const x = (progress.x - 0.5) * 50;
961
- const y = (progress.y - 0.5) * 50;
962
- element.style.transform = `translate(${x}px, ${y}px)`;
963
- },
964
- transitionDuration: 100,
965
- transitionEasing: 'easeOut',
966
- centeredToTarget: true
967
- }
968
- ]
969
- }
970
- ```
97
+ For devices with dynamic viewport sizes (e.g. mobile browsers where the address bar collapses), consider using viewport-relative units carefully and prefer `lvh`/`svh` over `dvh` unless dynamic viewport behavior is specifically desired.
971
98
 
972
99
  ---
973
100
 
974
- ## Rule 9: Multi-Element Custom Parallax with customEffect
975
-
976
- **Use Case**: Complex parallax effects with custom physics or non-standard transformations across multiple layers.
101
+ ## Rule 1: namedEffect
977
102
 
978
- **When to Apply**:
103
+ Use pre-built mouse presets from `@wix/motion-presets` that handle 2D mouse tracking internally. Mouse presets are preferred over `keyframeEffect` for 2D effects.
979
104
 
980
- - For parallax with custom easing or physics
981
- - When layers need different calculation methods
982
- - For effects combining multiple CSS properties
983
-
984
- **Pattern**:
105
+ **Multiple effects:** The `effects` array can contain multiple effects — all share the same pointer tracking and fire together. Use this to animate different targets from the same pointer movement.
985
106
 
986
107
  ```typescript
987
108
  {
988
- key: '[CONTAINER_KEY]',
109
+ key: '[SOURCE_KEY]',
989
110
  trigger: 'pointerMove',
990
111
  params: {
991
112
  hitArea: '[HIT_AREA]'
992
113
  },
993
114
  effects: [
994
115
  {
995
- key: '[LAYER_1_KEY]',
996
- customEffect: (element, progress) => {
997
- [LAYER_1_CUSTOM_LOGIC]
116
+ key: '[TARGET_KEY]',
117
+ namedEffect: {
118
+ type: '[NAMED_EFFECT_TYPE]',
119
+ [EFFECT_PROPERTIES]
998
120
  },
999
- centeredToTarget: true
121
+ centeredToTarget: [CENTERED_TO_TARGET],
122
+ transitionDuration: [TRANSITION_DURATION_MS],
123
+ transitionEasing: '[TRANSITION_EASING]'
1000
124
  },
1001
- {
1002
- key: '[LAYER_2_KEY]',
1003
- customEffect: (element, progress) => {
1004
- [LAYER_2_CUSTOM_LOGIC]
1005
- },
1006
- centeredToTarget: true
1007
- }
125
+ // additional effects targeting other elements can be added here
1008
126
  ]
1009
127
  }
1010
128
  ```
1011
129
 
1012
- **Example - Depth-Based Custom Parallax**:
130
+ ### Variables
1013
131
 
1014
- ```typescript
1015
- {
1016
- key: 'parallax-scene',
1017
- trigger: 'pointerMove',
1018
- params: {
1019
- hitArea: 'self'
1020
- },
1021
- effects: [
1022
- {
1023
- key: 'bg-stars',
1024
- customEffect: (element, progress) => {
1025
- // Background: subtle movement, inverted direction
1026
- const x = (0.5 - progress.x) * 10;
1027
- const y = (0.5 - progress.y) * 10;
1028
- element.style.transform = `translate(${x}px, ${y}px)`;
1029
- },
1030
- centeredToTarget: true
1031
- },
1032
- {
1033
- key: 'mid-clouds',
1034
- customEffect: (element, progress) => {
1035
- // Midground: moderate movement with rotation
1036
- const x = (progress.x - 0.5) * 30;
1037
- const y = (progress.y - 0.5) * 20;
1038
- const rotation = (progress.x - 0.5) * 5;
1039
- element.style.transform = `translate(${x}px, ${y}px) rotate(${rotation}deg)`;
1040
- },
1041
- centeredToTarget: true
1042
- },
1043
- {
1044
- key: 'fg-elements',
1045
- customEffect: (element, progress) => {
1046
- // Foreground: strong movement with scale
1047
- const x = (progress.x - 0.5) * 60;
1048
- const y = (progress.y - 0.5) * 40;
1049
- const scale = 1 + Math.abs(progress.x - 0.5) * 0.1;
1050
- element.style.transform = `translate(${x}px, ${y}px) scale(${scale})`;
1051
- },
1052
- centeredToTarget: true
1053
- }
1054
- ]
1055
- }
1056
- ```
132
+ - `[SOURCE_KEY]` — identifier matching the element's key (`data-interact-key` for web, `interactKey` for React). The element that tracks pointer movement.
133
+ - `[TARGET_KEY]` — identifier matching the element's key on the element to animate (can be same as source or different).
134
+ - `[HIT_AREA]` — `'self'` (track pointer within source element) or `'root'` (track pointer anywhere in viewport).
135
+ - `[NAMED_EFFECT_TYPE]` — a registered effect name, or a preset from `@wix/motion-presets` `mouse` library.
136
+ - `[EFFECT_PROPERTIES]` — preset-specific options. Refer to motion-presets rules for each preset's available options and their value types. Do NOT guess preset option names or types; omit unknown options and rely on defaults.
137
+ - `[CENTERED_TO_TARGET]` — `true` or `false`. See **Centering with `centeredToTarget`** above.
138
+ - `[TRANSITION_DURATION_MS]` — optional number. Milliseconds for smoothing (interpolating) between progress updates. The animation does not jump to the new progress value instantly; instead it transitions over this duration. Use to add inertia/lag to the effect, making it feel more physical (e.g. `200`–`600`).
139
+ - `[TRANSITION_EASING]` — optional string. CSS easing or named easing from `@wix/motion`. Adds a natural deceleration feel when used with `transitionDuration`.
1057
140
 
1058
141
  ---
1059
142
 
1060
- ## Rule 10: KeyframeEffect with Axis Mapping
1061
-
1062
- **Use Case**: When you want to use standard keyframe animations driven by pointer movement along a single axis (e.g., horizontal sliders, vertical progress indicators, single-axis parallax effects)
1063
-
1064
- **When to Apply**:
1065
-
1066
- - For slider-like interactions driven by horizontal mouse position
1067
- - For vertical scroll-like effects driven by vertical mouse position
1068
- - When you have existing keyframe animations you want to control with pointer movement
1069
- - For simple linear interpolation effects along one axis
143
+ ## Rule 2: keyframeEffect with Single Axis
1070
144
 
1071
- **Pattern**:
145
+ Use `keyframeEffect` when the pointer position along a single axis should drive a keyframe animation. The pointer's position on the chosen axis is mapped to linear 0–1 progress.
1072
146
 
1073
147
  ```typescript
1074
148
  {
@@ -1076,457 +150,130 @@ For smoother animations, you can use `transitionDuration` and `transitionEasing`
1076
150
  trigger: 'pointerMove',
1077
151
  params: {
1078
152
  hitArea: '[HIT_AREA]',
1079
- axis: '[AXIS]' // 'x' or 'y'
153
+ axis: '[AXIS]'
1080
154
  },
1081
155
  effects: [
1082
156
  {
1083
157
  key: '[TARGET_KEY]',
1084
158
  keyframeEffect: {
1085
- name: '[ANIMATION_NAME]',
1086
- keyframes: [
1087
- { [PROPERTY]: '[START_VALUE]' },
1088
- { [PROPERTY]: '[END_VALUE]' }
1089
- ]
1090
- },
1091
- fill: '[FILL_MODE]',
1092
- centeredToTarget: [CENTERED_TO_TARGET]
1093
- }
1094
- ]
1095
- }
1096
- ```
1097
-
1098
- **Variables**:
1099
-
1100
- - `[SOURCE_KEY]`: Unique identifier for source element tracking mouse movement
1101
- - `[TARGET_KEY]`: Unique identifier for target element to animate
1102
- - `[HIT_AREA]`: 'self' or 'root'
1103
- - `[AXIS]`: 'x' (maps x position) or 'y' (maps y position) - **defaults to 'y'** (in `params`)
1104
- - `[ANIMATION_NAME]`: Name for the keyframe animation
1105
- - `[PROPERTY]`: CSS property to animate (transform, opacity, etc.)
1106
- - `[START_VALUE]`: Value at progress 0 (left/top edge)
1107
- - `[END_VALUE]`: Value at progress 1 (right/bottom edge)
1108
- - `[FILL_MODE]`: 'none', 'forwards', 'backwards', 'both'
1109
- - `[CENTERED_TO_TARGET]`: true or false
1110
-
1111
- **Example - Horizontal Slider with Multiple Targets**:
1112
-
1113
- This example shows a pointer-driven slider where the X position controls both a sliding element and an indicator's opacity/scale.
1114
-
1115
- ```typescript
1116
- {
1117
- interactions: [
1118
- {
1119
- key: 'pointer-container',
1120
- trigger: 'pointerMove',
1121
- params: { hitArea: 'self', axis: 'x' },
1122
- effects: [
1123
- {
1124
- key: 'pointer-slider',
1125
- effectId: 'slide-effect',
1126
- },
1127
- {
1128
- key: 'pointer-indicator',
1129
- effectId: 'indicator-effect',
1130
- },
1131
- ],
1132
- },
1133
- ],
1134
- effects: {
1135
- 'slide-effect': {
1136
- keyframeEffect: {
1137
- name: 'slide-x',
1138
- keyframes: [
1139
- { transform: 'translateX(0px)' },
1140
- { transform: 'translateX(220px)' },
1141
- ],
1142
- },
1143
- fill: 'both',
1144
- },
1145
- 'indicator-effect': {
1146
- keyframeEffect: {
1147
- name: 'indicator-fade-scale',
1148
- keyframes: [
1149
- { opacity: '0.3', transform: 'scale(0.8)' },
1150
- { opacity: '1', transform: 'scale(1)' },
1151
- ],
159
+ name: '[EFFECT_NAME]',
160
+ keyframes: [KEYFRAMES]
1152
161
  },
1153
162
  fill: 'both',
163
+ centeredToTarget: [CENTERED_TO_TARGET],
164
+ transitionDuration: [TRANSITION_DURATION_MS],
165
+ transitionEasing: '[TRANSITION_EASING]',
166
+ effectId: '[UNIQUE_EFFECT_ID]'
1154
167
  },
1155
- },
168
+ // additional effects targeting other elements can be added here
169
+ ]
1156
170
  }
1157
171
  ```
1158
172
 
1159
- **Important Notes**:
173
+ ### Variables
1160
174
 
1161
- - `axis` defaults to `'y'` when using `keyframeEffect` with `pointerMove`
1162
- - For 2D effects that need both axes, you can use composite animations (Rule 11), `namedEffect`, or `customEffect`
175
+ - `[SOURCE_KEY]` / `[TARGET_KEY]` same as Rule 1.
176
+ - `[HIT_AREA]` `'self'` or `'root'`.
177
+ - `[AXIS]` — `'x'` (horizontal) or `'y'` (vertical). Defaults to `'y'` when omitted.
178
+ - `[EFFECT_NAME]` — unique string name for the keyframe effect.
179
+ - `[KEYFRAMES]` — array of CSS keyframe objects (e.g. `[{ transform: 'rotate(-10deg)' }, { transform: 'rotate(0)' }, { transform: 'rotate(10deg)' }]`). Distributed evenly across 0–1 progress: first keyframe = progress 0 (left/top edge), last = progress 1 (right/bottom edge). Any number of keyframes is allowed.
180
+ - `[CENTERED_TO_TARGET]` — optional. `true` or `false`. See **Centering with `centeredToTarget`** above. Defaults to `false`.
181
+ - `[TRANSITION_DURATION_MS]` — optional. Milliseconds for smoothing between progress updates. See Rule 1 for details.
182
+ - `[TRANSITION_EASING]` — optional. CSS easing string or named easing from `@wix/motion`. See Rule 1 for supported values.
183
+ - `[UNIQUE_EFFECT_ID]` — optional string identifier.
1163
184
 
1164
185
  ---
1165
186
 
1166
- ## Rule 11: Multi-Axis KeyframeEffect (X + Y)
1167
-
1168
- **Use Case**: Independent X/Y axis control using two `keyframeEffect` animations on the same target.
187
+ ## Rule 3: Two keyframeEffects with Two Axes and `composite`
1169
188
 
1170
- **Pattern**:
1171
- Define two interactions on the same source/target pair—one for `axis: 'x'`, one for `axis: 'y'`. When animating the same CSS property (e.g. `transform`), use the `composite` option to combine the effects.
1172
-
1173
- **Example - 2D Scale Control**:
1174
-
1175
- X axis controls `scaleX`, Y axis controls `scaleY`.
189
+ Use two separate interactions on the same source/target pair — one for `axis: 'x'`, one for `axis: 'y'` — for independent 2D control with keyframes. When both effects animate the same CSS property (e.g. `transform` or `filter`), use `composite` to combine them.
1176
190
 
1177
191
  ```typescript
1178
192
  {
1179
193
  interactions: [
1180
194
  {
1181
- key: 'composite-add-container',
195
+ key: '[SOURCE_KEY]',
1182
196
  trigger: 'pointerMove',
1183
- params: { hitArea: 'self', axis: 'x' },
1184
- effects: [
1185
- {
1186
- key: 'composite-add-ball',
1187
- effectId: 'scale-x-effect',
1188
- },
1189
- ],
197
+ params: { hitArea: '[HIT_AREA]', axis: 'x' },
198
+ effects: [{ key: '[TARGET_KEY]', effectId: '[X_EFFECT_ID]' }]
1190
199
  },
1191
200
  {
1192
- key: 'composite-add-container',
201
+ key: '[SOURCE_KEY]',
1193
202
  trigger: 'pointerMove',
1194
- params: { hitArea: 'self', axis: 'y' },
1195
- effects: [
1196
- {
1197
- key: 'composite-add-ball',
1198
- effectId: 'scale-y-effect',
1199
- },
1200
- ],
1201
- },
203
+ params: { hitArea: '[HIT_AREA]', axis: 'y' },
204
+ effects: [{ key: '[TARGET_KEY]', effectId: '[Y_EFFECT_ID]' }]
205
+ }
1202
206
  ],
1203
207
  effects: {
1204
- 'scale-x-effect': {
208
+ '[X_EFFECT_ID]': {
1205
209
  keyframeEffect: {
1206
- name: 'scale-x',
1207
- keyframes: [
1208
- { transform: 'scaleX(0.5)' },
1209
- { transform: 'scaleX(1.5)' },
1210
- ],
210
+ name: '[X_EFFECT_NAME]',
211
+ keyframes: [X_KEYFRAMES]
1211
212
  },
1212
- fill: 'both',
1213
- composite: 'add',
213
+ fill: '[FILL_MODE]', // usually 'both'
214
+ composite: '[COMPOSITE_OPERATION]',
215
+ transitionDuration: [TRANSITION_DURATION_MS],
216
+ transitionEasing: '[TRANSITION_EASING]'
1214
217
  },
1215
- 'scale-y-effect': {
218
+ '[Y_EFFECT_ID]': {
1216
219
  keyframeEffect: {
1217
- name: 'scale-y',
1218
- keyframes: [
1219
- { transform: 'scaleY(0.5)' },
1220
- { transform: 'scaleY(1.5)' },
1221
- ],
220
+ name: '[Y_EFFECT_NAME]',
221
+ keyframes: [Y_KEYFRAMES]
1222
222
  },
1223
- fill: 'both',
1224
- composite: 'add',
1225
- },
1226
- },
1227
- }
1228
- ```
1229
-
1230
- ---
1231
-
1232
- ## Advanced Patterns and Combinations
1233
-
1234
- ### Responsive Pointer Effects
1235
-
1236
- `pointerMove` only fires on pointer-capable devices, but touch users still visit the page. Use the `conditions` config map to define device/motion guards — condition IDs are arbitrary strings you define, matched against the media query predicates you provide.
1237
-
1238
- ```typescript
1239
- {
1240
- conditions: {
1241
- // Only run pointer effects on devices that support hover (non-touch)
1242
- 'supports-hover': { type: 'media', predicate: '(hover: hover)' },
1243
- // Suppress animations for users who prefer reduced motion
1244
- 'prefers-motion': { type: 'media', predicate: '(prefers-reduced-motion: no-preference)' },
1245
- },
1246
- interactions: [
1247
- {
1248
- key: 'responsive-element',
1249
- trigger: 'pointerMove',
1250
- conditions: ['supports-hover', 'prefers-motion'],
1251
- params: { hitArea: 'self' },
1252
- effects: [
1253
- {
1254
- key: 'responsive-element',
1255
- namedEffect: { type: 'Tilt3DMouse', angle: 20, perspective: 800 },
1256
- centeredToTarget: true
1257
- }
1258
- ]
223
+ fill: '[FILL_MODE]', // usually 'both'
224
+ composite: '[COMPOSITE_OPERATION]',
225
+ transitionDuration: [TRANSITION_DURATION_MS],
226
+ transitionEasing: '[TRANSITION_EASING]'
1259
227
  }
1260
- ]
228
+ }
1261
229
  }
1262
230
  ```
1263
231
 
1264
- ### Contextual Hit Areas
232
+ ### Variables
1265
233
 
1266
- Different hit areas for different interaction contexts:
234
+ - `[SOURCE_KEY]` / `[TARGET_KEY]` same as Rule 1.
235
+ - `[HIT_AREA]` — `'self'` or `'root'`.
236
+ - `[X_EFFECT_ID]` / `[Y_EFFECT_ID]` — unique string identifiers for the X-axis and Y-axis effects. Required — they map to keys in the top-level `effects` map.
237
+ - `[X_EFFECT_NAME]` / `[Y_EFFECT_NAME]` — unique string names for each keyframe effect.
238
+ - `[X_KEYFRAMES]` / `[Y_KEYFRAMES]` — arrays of WAAPI keyframe objects for the X-axis and Y-axis effects respectively. Each effect can vary in propertise and keyframes.
239
+ - `[COMPOSITE_OPERATION]` — `'add'` or `'accumulate'`. Required when both effects animate `transform` and/or both animate `filter`, so their values combine rather than override. `'add'`: composited transform functions are appended. `'accumulate'`: matching function arguments are summed.
240
+ - `[FILL_MODE]` — typically `'both'` to ensure the effect keeps applying after exiting the effect's active range.
241
+ - `[TRANSITION_DURATION_MS]` — optional. Milliseconds for smoothing between progress updates. See Rule 1 for details.
242
+ - `[TRANSITION_EASING]` — optional. CSS easing function for the smoothing transition. See Rule 1 for supported values.
1267
243
 
1268
- ```typescript
1269
- // Local interaction - mouse must be over element
1270
- {
1271
- key: 'local-card',
1272
- trigger: 'pointerMove',
1273
- params: {
1274
- hitArea: 'self'
1275
- },
1276
- effects: [
1277
- {
1278
- key: 'local-card',
1279
- namedEffect: {
1280
- type: 'Tilt3DMouse',
1281
- angle: 15
1282
- },
1283
- centeredToTarget: true
1284
- }
1285
- ]
1286
- },
1287
- // Global interaction - responds to mouse anywhere
1288
- {
1289
- key: 'global-background',
1290
- trigger: 'pointerMove',
1291
- params: {
1292
- hitArea: 'root'
1293
- },
1294
- effects: [
1295
- {
1296
- key: 'ambient-element',
1297
- namedEffect: {
1298
- type: 'AiryMouse',
1299
- distance: { value: 30, unit: 'px' }
1300
- },
1301
- centeredToTarget: false
1302
- }
1303
- ]
1304
- }
1305
- ```
244
+ ---
1306
245
 
1307
- ### Axis-Constrained Effects
246
+ ## Rule 4: customEffect
1308
247
 
1309
- Controlling movement direction for specific design needs:
248
+ Use `customEffect` when you need full imperative control over pointer-driven animations — custom physics, complex multi-property animations, velocity-reactive effects, or controlling WebGL/WebGPU and other JavaScript-driven effects. The callback receives the 2D progress object (see **Progress Object Structure**).
1310
249
 
1311
250
  ```typescript
1312
251
  {
1313
- key: 'constrained-container',
252
+ key: '[SOURCE_KEY]',
1314
253
  trigger: 'pointerMove',
1315
254
  params: {
1316
- hitArea: 'self'
255
+ hitArea: '[HIT_AREA]'
1317
256
  },
1318
257
  effects: [
1319
258
  {
1320
- key: 'horizontal-slider',
1321
- namedEffect: {
1322
- type: 'TrackMouse',
1323
- distance: { value: 100, unit: 'px' },
1324
- axis: 'horizontal'
259
+ key: '[TARGET_KEY]',
260
+ customEffect: (element: Element, progress: Progress) => {
261
+ [CUSTOM_ANIMATION_LOGIC]
1325
262
  },
1326
- centeredToTarget: true
263
+ centeredToTarget: [CENTERED_TO_TARGET],
264
+ transitionDuration: [TRANSITION_DURATION_MS],
265
+ transitionEasing: '[TRANSITION_EASING]'
1327
266
  },
1328
- {
1329
- key: 'vertical-indicator',
1330
- namedEffect: {
1331
- type: 'ScaleMouse',
1332
- scale: 1.2,
1333
- distance: { value: 150, unit: 'px' },
1334
- axis: 'vertical'
1335
- },
1336
- centeredToTarget: true
1337
- }
267
+ // additional effects targeting other elements can be added here
1338
268
  ]
1339
269
  }
1340
270
  ```
1341
271
 
1342
- ---
1343
-
1344
- ## Best Practices for PointerMove Interactions
1345
-
1346
- ### Effect Type Selection Guidelines
1347
-
1348
- **When to use `namedEffect` (Preferred)**:
1349
-
1350
- 1. For standard mouse-tracking effects (tilt, track, scale, blur)
1351
- 2. When GPU-optimized performance is critical
1352
- 3. For effects that match preset behavior (3D tilt, elastic following)
1353
- 4. When you don't need custom physics or calculations
1354
-
1355
- **When to use `customEffect`**:
1356
-
1357
- 1. For custom physics-based animations (springs, gravity)
1358
- 2. When you need access to velocity data (`progress.v`)
1359
- 3. For complex DOM manipulations (updating multiple elements)
1360
- 4. When creating effects not covered by named presets
1361
- 5. For grid/particle systems with many elements
1362
- 6. For controlling WebGL/WebGPU effects
1363
-
1364
- **When to use `keyframeEffect`**:
1365
-
1366
- 1. When you want single-axis control using the `axis` parameter ('x' or 'y')
1367
- 2. For slider-like interactions driven by pointer position along one axis
1368
- 3. For 2D control, use two `keyframeEffect` interactions with `composite` (see Rule 11)
1369
-
1370
- ### Performance Guidelines
1371
-
1372
- 1. **Limit simultaneous pointer effects** - too many can cause performance issues
1373
- 2. **Test on various devices** - pointer sensitivity varies across hardware
1374
- 3. **Cache DOM queries outside `customEffect` callbacks** - avoid repeated `querySelector` calls inside the callback
1375
- 4. **Use `requestAnimationFrame` sparingly** - the library already handles frame timing
1376
- 5. **Prefer `namedEffect` over `customEffect`** - named effects are optimized for GPU acceleration
1377
-
1378
- ### Hit Area Guidelines
1379
-
1380
- 1. **Use `hitArea: 'self'`** for local element interactions (cards, buttons, hover effects)
1381
- 2. **Use `hitArea: 'root'`** for global cursor followers and ambient effects
1382
- 3. **Consider container boundaries** when choosing hit areas
1383
- 4. **Test hit area responsiveness** across different screen sizes
1384
- 5. **`'self'`** is more performant than `'root'` - use when possible
1385
-
1386
- ### Centering Guidelines
1387
-
1388
- 1. **Set `centeredToTarget: true`** when target differs from source (e.g., animating child element from parent)
1389
- 2. **Use `centeredToTarget: false`** for cursor followers and global effects
1390
- 3. **Test centering behavior** with different element sizes
1391
- 4. **Consider responsive design** when setting centering
1392
- 5. **Centering affects how progress.x/y map to element position**
1393
-
1394
- ### Common Use Cases by Pattern
1395
-
1396
- **Single Element 3D Effects (Rule 1)** - `namedEffect`:
1397
-
1398
- - Interactive product cards
1399
- - 3D showcase elements
1400
- - Immersive button interactions
1401
- - Portfolio item presentations
1402
-
1403
- **Movement Followers (Rule 2)** - `namedEffect`:
1404
-
1405
- - Cursor follower elements
1406
- - Floating decorative elements
1407
- - Responsive UI indicators
1408
- - Interactive overlays
1409
-
1410
- **Scale & Deformation (Rule 3)** - `namedEffect`:
1411
-
1412
- - Organic interface elements
1413
- - Interactive morphing shapes
1414
- - Creative scaling buttons
1415
- - Blob-like interactions
1416
-
1417
- **Visual Effects (Rule 4)** - `namedEffect`:
1418
-
1419
- - Creative interface elements
1420
- - Motion blur interactions
1421
- - Spinning decorative elements
1422
- - Dynamic visual feedback
1423
-
1424
- **Multi-Element Parallax (Rule 5)** - `namedEffect`:
1425
-
1426
- - Layered background effects
1427
- - Depth-based interactions
1428
- - Immersive hero sections
1429
- - Complex scene responses
1430
-
1431
- **Group Coordination (Rule 6)** - `namedEffect`:
1432
-
1433
- - Interactive card grids
1434
- - Navigation menu systems
1435
- - Gallery hover effects
1436
- - Coordinated UI responses
1437
-
1438
- **Global Followers (Rule 7)** - `namedEffect`:
1439
-
1440
- - Custom cursor implementations
1441
- - Page-wide decorative elements
1442
- - Global interactive overlays
1443
- - Immersive cursor experiences
1444
-
1445
- **Custom Pointer Effects (Rule 8)** - `customEffect`:
1446
-
1447
- - Grid-based rotation systems
1448
- - Magnetic pull/push effects
1449
- - Physics-based animations
1450
- - Velocity-reactive effects
1451
- - Complex DOM manipulations
1452
- - Particle systems
1453
-
1454
- **Multi-Element Custom Parallax (Rule 9)** - `customEffect`:
1455
-
1456
- - Non-linear parallax physics
1457
- - Layers with different calculation methods
1458
- - Combined transform effects per layer
1459
- - Custom easing per element
1460
-
1461
- **Single-Axis Keyframe Control (Rule 10)** - `keyframeEffect`:
1462
-
1463
- - Horizontal slider interactions
1464
- - Vertical progress indicators
1465
- - Single-axis reveal effects
1466
- - Linear interpolation along one axis
1467
-
1468
- **Composite Keyframe (Rule 11)** - Two `keyframeEffect` + `composite`:
1469
-
1470
- - 2D element positioning with pointer
1471
- - Combined X/Y transform animations
1472
- - Independent axis control with keyframes
1473
- - Declarative 2D animations without customEffect
1474
-
1475
- ### Troubleshooting Common Issues
1476
-
1477
- **Poor pointer responsiveness**:
1478
-
1479
- - Verify `hitArea` configuration
1480
- - Test `centeredToTarget` settings
1481
- - Ensure target elements are properly positioned
1482
-
1483
- **Performance issues**:
1484
-
1485
- - Reduce number of simultaneous effects
1486
- - Use simpler named effects
1487
- - Check for CSS conflicts
1488
- - Test on lower-end devices
1489
- - In customEffect: cache DOM queries outside the callback
1490
- - Avoid creating objects inside customEffect callbacks
1491
-
1492
- **customEffect not updating smoothly**:
1493
-
1494
- - Add `transitionDuration` and `transitionEasing` for smoother transitions
1495
- - Avoid expensive calculations inside the callback
1496
- - Consider debouncing complex logic
1497
-
1498
- **customEffect progress values unexpected**:
1499
-
1500
- - Remember x/y are 0-1 normalized (not pixel values)
1501
- - Check `centeredToTarget` setting affects coordinate mapping
1502
- - Verify `hitArea` matches expected tracking area
1503
- - Use `progress.active` to handle edge cases
1504
-
1505
- **Unexpected behavior on touch devices**:
1506
-
1507
- - Implement appropriate conditions for touch vs. mouse
1508
- - Provide touch-friendly alternatives
1509
- - Test pointer events on mobile devices
1510
- - Consider disabling complex effects on touch
1511
-
1512
- **Effects not triggering**:
1513
-
1514
- - Verify source element exists and is visible
1515
- - Check `data-interact-key` matches CSS selector
1516
- - Ensure proper hit area configuration
1517
- - Test mouse event propagation
1518
-
1519
- ---
1520
-
1521
- ## Quick Reference: Effect Type Selection
272
+ ### Variables
1522
273
 
1523
- | Requirement | Use This | Why |
1524
- | --------------------------- | ------------------------------------------ | ------------------------------------------------------ |
1525
- | Standard 3D tilt | `namedEffect: { type: 'Tilt3DMouse' }` | GPU-optimized, battle-tested |
1526
- | Cursor following | `namedEffect: { type: 'TrackMouse' }` | Built-in physics |
1527
- | Horizontal progress control | `keyframeEffect` + `params: { axis: 'x' }` | Maps x position to keyframes |
1528
- | Vertical progress control | `keyframeEffect` + `params: { axis: 'y' }` | Maps y position to keyframes |
1529
- | Multi-axis keyframe (X + Y) | Two interactions with `keyframeEffect` | Use `composite: 'add'` or `'accumulate'` for same prop |
1530
- | Custom physics | `customEffect` | Full control over calculations |
1531
- | Velocity-based effects | `customEffect` | Access to `progress.v` |
1532
- | Grid/particle systems | `customEffect` | Can manipulate many elements |
274
+ - `[SOURCE_KEY]` / `[TARGET_KEY]` — same as Rule 1.
275
+ - `[HIT_AREA]` `'self'` or `'root'`.
276
+ - `[CUSTOM_ANIMATION_LOGIC]` JavaScript using `progress.x`, `progress.y`, `progress.v`, and `progress.active` to apply the effect. See **Progress Object Structure** above.
277
+ - `[CENTERED_TO_TARGET]` optional. `true` or `false`. See **Centering with `centeredToTarget`** above. Defaults to `false`.
278
+ - `[TRANSITION_DURATION_MS]` optional. Milliseconds for smoothing between progress updates. See Rule 1 for details.
279
+ - `[TRANSITION_EASING]` optional. CSS easing function for the smoothing transition. See Rule 1 for supported values.