use-scroll-animate 1.2.0 â 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +125 -58
- package/LICENSE +21 -21
- package/README.md +256 -214
- package/README_ja.md +106 -69
- package/README_zh.md +106 -69
- package/dist/index.d.mts +316 -0
- package/dist/index.d.ts +316 -0
- package/dist/index.esm.js +1249 -697
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1263 -708
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1249 -0
- package/dist/index.mjs.map +1 -0
- package/dist/index.umd.js +13 -14
- package/dist/index.umd.js.map +1 -1
- package/dist/types/core.d.ts +41 -7
- package/dist/types/index.d.ts +36 -33
- package/dist/types/presets.d.ts +15 -15
- package/dist/types/react.d.ts +29 -18
- package/dist/types/sequence.d.ts +38 -0
- package/dist/types/stagger.d.ts +25 -0
- package/dist/types/types.d.ts +153 -123
- package/dist/types/vue.d.ts +28 -18
- package/package.json +81 -35
- package/CONTRIBUTING.md +0 -76
- package/examples/react/App.tsx +0 -61
- package/examples/vanilla/index.html +0 -201
- package/rollup.config.js +0 -41
- package/src/core.ts +0 -373
- package/src/index.ts +0 -48
- package/src/presets.ts +0 -148
- package/src/react.ts +0 -175
- package/src/types.ts +0 -161
- package/src/vue.ts +0 -118
- package/tsconfig.json +0 -17
package/README.md
CHANGED
|
@@ -1,214 +1,256 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
# use-scroll-animate ð
|
|
4
|
-
|
|
5
|
-
**A lightweight (~
|
|
6
|
-
|
|
7
|
-
[](https://github.com/HarrisonCN/use-scroll-animate/releases)
|
|
8
|
-
[](https://github.com/HarrisonCN/use-scroll-animate)
|
|
9
|
-
[](https://opensource.org/licenses/MIT)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
import {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
##
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
|
132
|
-
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
ScrollAnimate.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# use-scroll-animate ð
|
|
4
|
+
|
|
5
|
+
**A lightweight (~5KB gzipped), dependency-free scroll animation library for the modern web.**
|
|
6
|
+
|
|
7
|
+
[](https://github.com/HarrisonCN/use-scroll-animate/releases)
|
|
8
|
+
[](https://github.com/HarrisonCN/use-scroll-animate)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
|
|
11
|
+
[English](./README.md) | [įŪä―äļæ](./README_zh.md) | [æĨæŽčŠ](./README_ja.md)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
## Why `use-scroll-animate`?
|
|
16
|
+
|
|
17
|
+
In 2025, performance is everything. Traditional scroll animation libraries often bundle heavy dependencies, rely on outdated scroll event listeners, or force you into a specific framework.
|
|
18
|
+
|
|
19
|
+
`use-scroll-animate` is built differently:
|
|
20
|
+
- ⥠**Zero Dependencies**: Pure Vanilla JS/TypeScript.
|
|
21
|
+
- ð **High Performance**: Powered by `IntersectionObserver` and the native `Web Animations API`. No scroll event listeners by default (the opt-in scroll-progress mode uses a single passive, rAF-throttled listener, only while tracked elements are on screen).
|
|
22
|
+
- ðŠķ **Ultra Lightweight**: ~4.8KB gzipped for the core (tree-shaken, minified ESM); everything incl. `sequence`, `staggerChildren` and the React/Vue helpers is ~6.1KB (UMD ~6.2KB).
|
|
23
|
+
- ð§Đ **Framework Agnostic**: Works seamlessly with Vanilla JS, React, Vue, Svelte, and more. First-class React Hooks and Vue Composables included.
|
|
24
|
+
- âŋ **Accessible**: Respects `prefers-reduced-motion` out of the box (content is shown immediately, no entrance or parallax motion).
|
|
25
|
+
- ðĨïļ **SSR-safe**: Importing (and even calling) the API on the server is a no-op.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install use-scroll-animate
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## v1.4.0 New Features âĻ
|
|
34
|
+
|
|
35
|
+
### True scroll progress (`progressMode: 'scroll'`)
|
|
36
|
+
By default `onProgress` reports the element's *visible ratio*, which never reaches 1 for elements taller than the screen. Opt in to real scroll progress: `0` when the element's top reaches the bottom of the viewport, `1` when its bottom leaves the top.
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
ScrollAnimate.observe('.chapter', {
|
|
40
|
+
progressMode: 'scroll',
|
|
41
|
+
onProgress: (el, p) => el.style.setProperty('--progress', p),
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Parallax uses the same progress, so `progressMode: 'scroll'` also gives smooth parallax on tall sections. HTML: `data-sa-progress="scroll"`. Also exported as a helper: `getScrollProgress(el, root?)`.
|
|
46
|
+
|
|
47
|
+
### Stagger dynamically added children
|
|
48
|
+
`staggerChildren()` (vanilla), `useScrollStagger()` (React **and now Vue**) accept `observeChildren: true`. A `MutationObserver` picks up children added later (infinite lists, "load more"): those added before the reveal join the stagger; those added after it animate when they scroll into view, staggered per batch.
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
import { staggerChildren } from 'use-scroll-animate';
|
|
52
|
+
const stop = staggerChildren(document.querySelector('#feed'), {
|
|
53
|
+
animation: 'fade-in-up', stagger: 60, observeChildren: true,
|
|
54
|
+
});
|
|
55
|
+
// later: stop();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Timelines with `sequence()`
|
|
59
|
+
Chain animations across elements. Each step starts when the previous one ends; `gap` adds a pause (negative values overlap) and `at` sets an absolute start time.
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { sequence } from 'use-scroll-animate';
|
|
63
|
+
|
|
64
|
+
const tl = sequence([
|
|
65
|
+
{ target: '.hero h1', animation: 'fade-in-up', duration: 700 },
|
|
66
|
+
{ target: '.hero p', animation: 'blur-in', gap: -300 },
|
|
67
|
+
{ target: '.hero .btn', animation: 'scale-up', stagger: 80 },
|
|
68
|
+
], { trigger: '.hero', easing: 'soft-spring' }); // auto-plays once when .hero enters
|
|
69
|
+
|
|
70
|
+
await tl.play(); // or play manually; resolves when every step is done
|
|
71
|
+
tl.cancel(); // stop and leave everything visible
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### New presets
|
|
75
|
+
`scale-up`, `blur-in-up`, `flip-up`, `flip-down`, `rotate-left`, `rotate-right`, and clip-path reveals `clip-up`, `clip-down`, `clip-left`, `clip-right`, `clip-circle`.
|
|
76
|
+
|
|
77
|
+
### Smaller memory footprint
|
|
78
|
+
Finished `once` elements are dropped from the registry right after they animate (unless they still need parallax/`onProgress`), so long pages and SPAs don't keep thousands of records alive. They're remembered in a `WeakSet`, so `init()`/`observe()` never replay them. Set `createScrollAnimate({ autoUnregister: false })` to keep them listed in `getObservedElements()` as before.
|
|
79
|
+
|
|
80
|
+
### Proper `exports` map
|
|
81
|
+
Node ESM (`import`) resolves to `dist/index.mjs`, CommonJS (`require`) to `dist/index.js`, each with matching bundled types. The legacy `main`/`module`/`unpkg` fields and `dist/*` deep imports keep working.
|
|
82
|
+
|
|
83
|
+
## v1.3.0: Custom Easing ðĻ
|
|
84
|
+
|
|
85
|
+
You can now use custom cubic-bezier curves or even JavaScript functions to create complex physical effects.
|
|
86
|
+
|
|
87
|
+
### 1. Cubic-Bezier Array
|
|
88
|
+
Pass an array of 4 numbers to define a custom cubic-bezier curve.
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
ScrollAnimate.observe('.box', {
|
|
92
|
+
animation: 'fade-in-up',
|
|
93
|
+
easing: [0.68, -0.55, 0.265, 1.55] // Custom bounce effect
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 2. Custom Easing Function
|
|
98
|
+
Pass a function `(t: number) => number` for complete control over the animation timing.
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
ScrollAnimate.observe('.box', {
|
|
102
|
+
easing: (t) => t * t * (3 - 2 * t) // Custom smooth-step
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 3. New Physics Presets
|
|
107
|
+
We've added high-quality physics-based easing presets:
|
|
108
|
+
- `spring`: Standard spring effect.
|
|
109
|
+
- `soft-spring`: Gentle, bouncy entrance.
|
|
110
|
+
- `heavy-bounce`: Dramatic bounce effect.
|
|
111
|
+
|
|
112
|
+
## Quick Start (Vanilla JS / HTML)
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<div data-sa data-sa-animation="fade-in-up" data-sa-easing="soft-spring">
|
|
116
|
+
I have a soft spring effect!
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div data-sa data-sa-animation="zoom-in" data-sa-easing="[0.34, 1.56, 0.64, 1]">
|
|
120
|
+
I use a custom cubic-bezier array!
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<script type="module">
|
|
124
|
+
import ScrollAnimate from 'use-scroll-animate';
|
|
125
|
+
ScrollAnimate.init();
|
|
126
|
+
</script>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Options
|
|
130
|
+
|
|
131
|
+
| Option | Type | Default | Description |
|
|
132
|
+
|--------|------|---------|-------------|
|
|
133
|
+
| `animation` | `string` \| `string[]` \| `{ from, to }` | `'fade-in-up'` | Preset name, array of presets to combine, or custom keyframes |
|
|
134
|
+
| `duration` | `number` | `600` | Duration in ms |
|
|
135
|
+
| `delay` | `number` | `0` | Delay in ms |
|
|
136
|
+
| `easing` | `string` \| `number[]` \| `function` | `'ease'` | CSS easing, preset (`spring`, `soft-spring`, `heavy-bounce`), cubic-bezier array, or `(t) => number` |
|
|
137
|
+
| `threshold` | `number` \| `number[]` | `0.1` | IntersectionObserver threshold(s) |
|
|
138
|
+
| `rootMargin` | `string` | `'0px'` | IntersectionObserver root margin |
|
|
139
|
+
| `once` | `boolean` | `true` | Animate only the first time the element enters |
|
|
140
|
+
| `repeat` | `boolean` | `false` | Re-hide on leave and replay on every entry |
|
|
141
|
+
| `offset` | `number` | `0` | Trigger the animation this many px after the element enters the viewport |
|
|
142
|
+
| `stagger` | `number` | `0` | Extra delay (ms) per sibling revealed in the same batch |
|
|
143
|
+
| `parallax` | `{ x, y, rotate, scale, speed }` | `{}` | Scroll-driven parallax (keeps running after the entrance animation) |
|
|
144
|
+
| `onStart` / `onComplete` / `onEnter` / `onLeave` | `(el) => void` | â | Lifecycle callbacks |
|
|
145
|
+
| `onProgress` | `(el, progress) => void` | â | Progress (0â1) as the element scrolls â visible ratio, or true scroll progress with `progressMode: 'scroll'` |
|
|
146
|
+
| `progressMode` | `'ratio'` \| `'scroll'` | `'ratio'` | How `onProgress`/parallax progress is measured (`'scroll'`: 0 = top enters at the bottom, 1 = bottom leaves at the top) |
|
|
147
|
+
| `progressVar` | `string` | â | Write progress (0â1, same value as `onProgress`) to this CSS custom property, e.g. `'--sa-progress'`, for scroll-driven effects in plain CSS |
|
|
148
|
+
|
|
149
|
+
Every option is also available as a data attribute: `data-sa-animation`, `data-sa-duration`, `data-sa-delay`, `data-sa-easing`, `data-sa-threshold`, `data-sa-root-margin`, `data-sa-once`, `data-sa-repeat`, `data-sa-offset`, `data-sa-stagger`, `data-sa-progress`, `data-sa-progress-var` (bare attribute = `--sa-progress`), `data-sa-parallax-x|y|rotate|scale|speed`.
|
|
150
|
+
|
|
151
|
+
**Presets:** `fade-in`, `fade-in-up|down|left|right`, `zoom-in`, `zoom-out`, `scale-up`, `flip-x`, `flip-y`, `flip-up`, `flip-down`, `slide-up|down|left|right`, `bounce`, `rotate-in`, `rotate-left`, `rotate-right`, `blur-in`, `blur-in-up`, `skew-in`, `scale-x`, `scale-y`, `clip-up|down|left|right`, `clip-circle`, `shimmer`, `pulse`, `swing`. Combine them with an array, e.g. `['fade-in', 'clip-up']`.
|
|
152
|
+
|
|
153
|
+
**Global config** (`createScrollAnimate(config)` / `configure()`): `defaultAnimation`, `defaultDuration`, `defaultDelay`, `defaultEasing`, `defaultThreshold`, `defaultRootMargin`, `defaultRepeat`, `defaultOnce`, `defaultOffset`, `hiddenClass`, `visibleClass`, `useClassNames`, `disabled`, `root`, `autoUnregister` (default `true`).
|
|
154
|
+
|
|
155
|
+
### Progress as a CSS variable (`progressVar`)
|
|
156
|
+
|
|
157
|
+
Drive any CSS property from scroll position without writing JavaScript callbacks. The element's progress is written to a custom property on the element itself:
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<div data-sa data-sa-progress="scroll" data-sa-progress-var class="hero">âĶ</div>
|
|
161
|
+
|
|
162
|
+
<style>
|
|
163
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
164
|
+
.hero { transform: translateY(calc((1 - var(--sa-progress, 0)) * 60px)); opacity: calc(0.4 + var(--sa-progress, 0)); }
|
|
165
|
+
}
|
|
166
|
+
</style>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
ScrollAnimate.observe('.bar', { progressVar: '--fill', progressMode: 'scroll' });
|
|
171
|
+
// .bar::after { transform: scaleX(var(--fill, 0)); }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
It uses the same rAF-throttled / IntersectionObserver pipeline as `onProgress`, keeps updating after the entrance animation, and is still written under reduced motion (it is data) â guard motion in your CSS with `prefers-reduced-motion` as above.
|
|
175
|
+
|
|
176
|
+
## Instance API
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
import ScrollAnimate, { createScrollAnimate } from 'use-scroll-animate';
|
|
180
|
+
|
|
181
|
+
ScrollAnimate.init(root?); // observe every [data-sa] element (safe to call again after DOM changes)
|
|
182
|
+
const stop = ScrollAnimate.watch(root?); // init() + auto-observe [data-sa] elements added later; stop() to end
|
|
183
|
+
ScrollAnimate.observe(target, opts); // selector, Element, NodeList or Element[]
|
|
184
|
+
ScrollAnimate.unobserve(target); // stop observing (elements that never animated are made visible)
|
|
185
|
+
ScrollAnimate.animate(target, opts); // play an animation right now
|
|
186
|
+
ScrollAnimate.refresh(); // rebuild observers, e.g. after configure({ root })
|
|
187
|
+
ScrollAnimate.configure({ ... }); // update global defaults
|
|
188
|
+
ScrollAnimate.destroy(); // disconnect everything
|
|
189
|
+
|
|
190
|
+
const sa = createScrollAnimate({ root: document.querySelector('#scroller') }); // isolated instance
|
|
191
|
+
|
|
192
|
+
// Helpers (tree-shakeable)
|
|
193
|
+
import { sequence, staggerChildren, getScrollProgress } from 'use-scroll-animate';
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Watching the DOM (`watch()`)
|
|
197
|
+
|
|
198
|
+
For SPAs, CMS content, infinite lists or anything rendered after page load, `watch()` replaces "call `init()` again after every DOM change":
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
import ScrollAnimate from 'use-scroll-animate';
|
|
202
|
+
|
|
203
|
+
const stop = ScrollAnimate.watch(); // or watch(document.querySelector('#app'))
|
|
204
|
+
// [data-sa] elements inserted later â even deep inside a new subtree, or an existing
|
|
205
|
+
// element that gains the data-sa attribute â are observed with their data-sa-* options.
|
|
206
|
+
// Elements removed from the DOM are released; finished `once` elements are never replayed.
|
|
207
|
+
stop(); // stop watching (destroy() also stops every watcher)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
It uses a single `MutationObserver` per call and is a no-op on the server or without `MutationObserver`.
|
|
211
|
+
|
|
212
|
+
Via a `<script>` tag (UMD build), the default instance lives at `ScrollAnimate.default`:
|
|
213
|
+
|
|
214
|
+
```html
|
|
215
|
+
<script src="https://unpkg.com/use-scroll-animate"></script>
|
|
216
|
+
<script>ScrollAnimate.default.init();</script>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## React & Vue
|
|
220
|
+
|
|
221
|
+
```jsx
|
|
222
|
+
import React from 'react';
|
|
223
|
+
import { createReactHooks } from 'use-scroll-animate';
|
|
224
|
+
const { useScrollAnimate, useScrollStagger } = createReactHooks(React);
|
|
225
|
+
|
|
226
|
+
function Card() {
|
|
227
|
+
const ref = useScrollAnimate({ animation: 'zoom-in', easing: 'spring' });
|
|
228
|
+
return <div ref={ref}>Hello</div>;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
import { ref, onMounted, onUnmounted } from 'vue';
|
|
234
|
+
import { createVueComposables } from 'use-scroll-animate';
|
|
235
|
+
const { useScrollAnimate, useScrollStagger } = createVueComposables({ ref, onMounted, onUnmounted });
|
|
236
|
+
const { animateRef } = useScrollAnimate({ animation: 'fade-in-left' });
|
|
237
|
+
const { staggerRef } = useScrollStagger({ stagger: 60, observeChildren: true }); // <ul ref="staggerRef">
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```jsx
|
|
241
|
+
// React: also animate items appended later
|
|
242
|
+
function Feed({ items }) {
|
|
243
|
+
const ref = useScrollStagger({ animation: 'fade-in-up', stagger: 60, observeChildren: true });
|
|
244
|
+
return <ul ref={ref}>{items.map((i) => <li key={i.id}>{i.title}</li>)}</ul>;
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Hooks and composables share the core engine, so `once`, `offset`, custom easing functions, parallax and reduced-motion handling behave exactly like the vanilla API.
|
|
249
|
+
|
|
250
|
+
## Contributing
|
|
251
|
+
|
|
252
|
+
Contributions are always welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|