use-scroll-animate 1.4.0 → 2.0.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 (66) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +184 -10
  3. package/README_ja.md +57 -1
  4. package/README_zh.md +68 -1
  5. package/dist/{index.mjs → chunks/core-CH41ekIo.cjs} +366 -393
  6. package/dist/chunks/core-CH41ekIo.cjs.map +1 -0
  7. package/dist/{index.esm.js → chunks/core-FUEi4ncH.js} +351 -393
  8. package/dist/chunks/core-FUEi4ncH.js.map +1 -0
  9. package/dist/chunks/stagger-DabrnrcE.js +84 -0
  10. package/dist/chunks/stagger-DabrnrcE.js.map +1 -0
  11. package/dist/chunks/stagger-XD-0-FQz.cjs +86 -0
  12. package/dist/chunks/stagger-XD-0-FQz.cjs.map +1 -0
  13. package/dist/element.cjs +97 -0
  14. package/dist/element.cjs.map +1 -0
  15. package/dist/{types/types.d.ts → element.d.cts} +87 -10
  16. package/dist/element.d.ts +216 -0
  17. package/dist/element.js +95 -0
  18. package/dist/element.js.map +1 -0
  19. package/dist/element.umd.js +2 -0
  20. package/dist/element.umd.js.map +1 -0
  21. package/dist/index.cjs +241 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/{index.d.mts → index.d.cts} +98 -51
  24. package/dist/index.d.ts +98 -51
  25. package/dist/index.js +105 -1070
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.umd.js +5 -3
  28. package/dist/index.umd.js.map +1 -1
  29. package/dist/react.cjs +67 -0
  30. package/dist/react.cjs.map +1 -0
  31. package/dist/react.d.cts +159 -0
  32. package/dist/react.d.ts +159 -0
  33. package/dist/react.js +64 -0
  34. package/dist/react.js.map +1 -0
  35. package/dist/solid.cjs +62 -0
  36. package/dist/solid.cjs.map +1 -0
  37. package/dist/solid.d.cts +245 -0
  38. package/dist/solid.d.ts +245 -0
  39. package/dist/solid.js +58 -0
  40. package/dist/solid.js.map +1 -0
  41. package/dist/svelte.cjs +65 -0
  42. package/dist/svelte.cjs.map +1 -0
  43. package/dist/svelte.d.cts +244 -0
  44. package/dist/svelte.d.ts +244 -0
  45. package/dist/svelte.js +62 -0
  46. package/dist/svelte.js.map +1 -0
  47. package/dist/vue.cjs +61 -0
  48. package/dist/vue.cjs.map +1 -0
  49. package/dist/vue.d.cts +159 -0
  50. package/dist/vue.d.ts +159 -0
  51. package/dist/vue.js +59 -0
  52. package/dist/vue.js.map +1 -0
  53. package/docs/API.md +139 -0
  54. package/docs/deprecations.md +20 -0
  55. package/docs/migration-from-aos.md +67 -0
  56. package/docs/migration-from-gsap-scrolltrigger.md +80 -0
  57. package/package.json +86 -15
  58. package/dist/index.esm.js.map +0 -1
  59. package/dist/index.mjs.map +0 -1
  60. package/dist/types/core.d.ts +0 -41
  61. package/dist/types/index.d.ts +0 -36
  62. package/dist/types/presets.d.ts +0 -15
  63. package/dist/types/react.d.ts +0 -29
  64. package/dist/types/sequence.d.ts +0 -38
  65. package/dist/types/stagger.d.ts +0 -25
  66. package/dist/types/vue.d.ts +0 -28
package/CHANGELOG.md CHANGED
@@ -7,6 +7,74 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.0.0] - 2026-10-07
11
+
12
+ 2.0 collects the 1.6–1.9 roadmap (native scroll timeline, Svelte/Solid/Web Component entries, exit animations and `parallax()`, docs and demo) and removes what 1.9 deprecated. See **MIGRATION from 1.x** below.
13
+
14
+ ### ⚠ Breaking changes
15
+ - **`engine` defaults to `'auto'`**: presets run on the native scroll-driven timeline (`animation-timeline: view()`) where supported — scroll-linked instead of time-based. `'auto'` still picks the JS engine when an element sets `duration`, `delay`, `offset` or `stagger` itself. Set `defaultEngine: 'js'` for 1.x behaviour.
16
+ - **Removed** the `createReactHooks` / `createVueComposables` re-exports from the main entry: import them from `use-scroll-animate/react` / `use-scroll-animate/vue`.
17
+ - **ESM-first package** (`"type": "module"`): `import` → `dist/*.js` + `dist/*.d.ts`, `require` → `dist/*.cjs` + `dist/*.d.cts` for every entry; `main` is `dist/index.cjs`.
18
+ - **Removed legacy build artefacts**: the `module` field, `dist/index.esm.js`, `dist/index.mjs`, `dist/*.d.mts`, the per-file `dist/types/*` declarations, and `use-scroll-animate/dist/*` deep imports (only the documented entry points resolve). `dist/index.umd.js` and `dist/element.umd.js` keep their CDN URLs.
19
+ - **ES2020 output** (was ES2018): optional chaining / nullish coalescing are no longer down-levelled. Every browser that has `Animation.commitStyles()` (Chrome 84, Firefox 75, Safari 13.1), which the library already relied on, supports ES2020. Together with the removed re-exports: UMD 7.55 → 6.99 kB gz, core-only import 5.63 → 5.40 kB gz.
20
+ - `engines.node >= 18` declared (only relevant for SSR imports).
21
+
22
+ ### Added
23
+ - **Native scroll-driven engine** (1.6): new `engine: 'auto' | 'js' | 'css'` option (`defaultEngine` config, `data-sa-engine` attribute). With `'auto'`/`'css'`, browsers that support `animation-timeline: view()` run the preset on a native `ViewTimeline` (scroll-linked, off the main thread); others fall back to the JS engine (default `'auto'`, see Breaking changes). New `viewRange` option (`data-sa-view-range`) and `supportsScrollTimeline()` helper.
24
+ - **Svelte actions** (1.7): `use-scroll-animate/svelte` exports `scrollAnimate` and `scrollStagger` (`use:` actions with `update`/`destroy`; no `svelte` import).
25
+ - **Solid primitives** (1.7): `use-scroll-animate/solid` exports the `scrollAnimate` / `scrollStagger` directives (typed via `JSX.Directives`) and `useScrollAnimate()` ref primitive. `solid-js` is an optional peer dependency.
26
+ - **`<scroll-animate>` Web Component** (1.7): `use-scroll-animate/element` exports `defineScrollAnimate(tagName?, instance?)`; attributes mirror `data-sa-*`, and it dispatches `sa:enter`/`sa:leave`/`sa:start`/`sa:complete`/`sa:progress` events. `dist/element.umd.js` registers it on load for CDN use.
27
+ - **Subpath exports** (1.7): `./react`, `./vue`, `./svelte`, `./solid`, `./element` (ESM + CJS, each with types). Entries share code through `dist/chunks/`, so importing several never duplicates the core. Optional peer dependencies: `solid-js`, `svelte`.
28
+ - **Exit animations** (1.8): `exit: true | preset | presets | { from, to }` (`data-sa-exit`, `exit` attribute on `<scroll-animate>`) plays the entrance (or the given animation) in reverse when the element leaves the viewport and replays the entrance on re-entry; implies `repeat` unless set. Scroll-linked over the `exit` range with the native engine; class swap in class-name mode; skipped under reduced motion.
29
+ - **`parallax(target, { speed, axis, progressVar, root, respectReducedMotion })`** (1.8): standalone parallax helper on the scroll-progress scale used by `progressVar`. Writes the progress to `--sa-parallax` and the offset to the individual `translate` property (composes with `transform`/entrance animations); no offset under reduced motion; listens only while targets are visible; returns a stop function. < 1 kB gzipped when tree-shaken.
30
+ - **Size budgets** (1.6): `size-budget.json` defines a gzip budget per entry (UMD bundle and tree-shaken imports); `npm run size:check` fails when one is exceeded and runs in CI.
31
+ - **Docs** (1.9): `docs/API.md` (full API reference), `docs/migration-from-aos.md`, `docs/migration-from-gsap-scrolltrigger.md`, `docs/deprecations.md` (now "Upgrading to 2.0"), and `demo/index.html` — a no-build preset playground (every preset clickable, scroll-triggered cards, parallax) that loads the UMD bundle.
32
+
33
+ ### Changed
34
+ - The default instance export is annotated `/* @__PURE__ */`, so bundlers drop the core when only standalone helpers such as `parallax` are imported (1.8).
35
+ - Size budgets for the UMD bundle and "import everything" raised from 7.5 to 8 kB gzip for exit + parallax (1.8).
36
+ - Build (1.7): ESM/CJS entries are small files that import shared chunks from `dist/chunks/`; the UMD bundles stay single files.
37
+ - Build uses Rollup's ESM config (`rollup.config.mjs`); `@rollup/plugin-commonjs` dropped (no CommonJS inputs). `npm run build` cleans `dist/` first.
38
+
39
+ ### Fixed
40
+ - Class-name mode: `destroy()` now clears pending completion timers, so `onComplete` no longer fires after the instance was destroyed. Other instances' timers are unaffected.
41
+
42
+ ### Repository
43
+ - Dependabot (npm + GitHub Actions, weekly, grouped), issue templates (bug report, feature request) and a pull-request template.
44
+
45
+ ### MIGRATION from 1.x
46
+
47
+ 1. **React / Vue imports**
48
+ ```diff
49
+ - import { createReactHooks } from 'use-scroll-animate';
50
+ + import { createReactHooks } from 'use-scroll-animate/react';
51
+ - import { createVueComposables } from 'use-scroll-animate';
52
+ + import { createVueComposables } from 'use-scroll-animate/vue';
53
+ ```
54
+ (1.9 already logged a dev-only warning for these.)
55
+ 2. **Engine**: if you rely on time-based entrances (`duration`/`delay` set globally via `defaultDuration`/`defaultDelay`, `onComplete` timing, `threshold`-based triggering), keep 1.x behaviour with
56
+ ```js
57
+ ScrollAnimate.configure({ defaultEngine: 'js' }); // default instance
58
+ createScrollAnimate({ defaultEngine: 'js' }); // own instances
59
+ ```
60
+ or per element `engine: 'js'` / `data-sa-engine="js"`. Elements that set `duration`, `delay`, `offset` or `stagger` themselves already stay on JS.
61
+ 3. **Deep imports**: replace `use-scroll-animate/dist/index.js`, `dist/index.mjs`, `dist/index.esm.js` or `dist/types/...` with `use-scroll-animate` (or a subpath entry). Type-only imports come from the package name: `import type { AnimateOptions } from 'use-scroll-animate'`.
62
+ 4. **CommonJS** consumers: `require('use-scroll-animate')` keeps working (now `dist/index.cjs`). If you referenced `dist/index.js` as CommonJS by path, it is ESM now.
63
+ 5. **`<script>` / CDN**: no change — `https://unpkg.com/use-scroll-animate/dist/index.umd.js` (global `ScrollAnimate`) and `dist/element.umd.js`.
64
+ 6. **Old browsers**: if you must support browsers without ES2020 (pre-2020 Safari/Chrome), transpile `use-scroll-animate` in your bundler, or stay on 1.x.
65
+
66
+ ## [1.5.0] - 2026-10-07
67
+
68
+ ### Added
69
+ - `watch(root?)` instance method: automatically observes `[data-sa]` elements added to the DOM later; returns a stop function, and `destroy()` stops all watchers.
70
+ - `progressVar` option and `data-sa-progress-var` attribute: expose scroll progress (0–1) as a CSS custom property.
71
+
72
+ ### Fixed
73
+ - Stopping `staggerChildren` or cancelling a triggered `sequence()` before the content entered the viewport left it at `opacity: 0`; it is now restored (also affects React/Vue `useScrollStagger` unmounting off-screen).
74
+
75
+ ### Tests / CI
76
+ - 47 new tests covering reduced motion, SSR, unmount cleanup and lifecycle; CI job timeout and `npm pack --dry-run`.
77
+
10
78
  ## [1.4.0] - 2026-10-06
11
79
 
12
80
  ### Added
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # use-scroll-animate 🚀
4
4
 
5
- **A lightweight (~5KB gzipped), dependency-free scroll animation library for the modern web.**
5
+ **A lightweight (~5.7KB gzipped), dependency-free scroll animation library for the modern web.**
6
6
 
7
7
  [![GitHub release (latest by date)](https://img.shields.io/github/v/release/HarrisonCN/use-scroll-animate?style=flat-square)](https://github.com/HarrisonCN/use-scroll-animate/releases)
8
8
  [![GitHub repo size](https://img.shields.io/github/repo-size/HarrisonCN/use-scroll-animate?style=flat-square)](https://github.com/HarrisonCN/use-scroll-animate)
@@ -19,17 +19,57 @@ In 2025, performance is everything. Traditional scroll animation libraries often
19
19
  `use-scroll-animate` is built differently:
20
20
  - ⚡ **Zero Dependencies**: Pure Vanilla JS/TypeScript.
21
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.
22
+ - 🪶 **Ultra Lightweight**: ~5.4–5.7KB gzipped for the core (tree-shaken, minified ESM); everything from the main entry is ~6.8KB (UMD ~7.0KB); `parallax()` alone < 1KB. Every entry has a gzip budget enforced in CI (`size-budget.json`, `npm run size:check`).
23
+ - 🧩 **Framework Agnostic**: Vanilla JS, React hooks, Vue composables, Svelte actions, Solid directives and a `<scroll-animate>` Web Component, each as its own entry point (`use-scroll-animate/react`, `/vue`, `/svelte`, `/solid`, `/element`).
24
24
  - ♿ **Accessible**: Respects `prefers-reduced-motion` out of the box (content is shown immediately, no entrance or parallax motion).
25
25
  - 🖥️ **SSR-safe**: Importing (and even calling) the API on the server is a no-op.
26
26
 
27
+ ## v2.0.0 🎉
28
+
29
+ - **Native scroll-driven animations by default** (`engine: 'auto'`) where the browser supports `animation-timeline: view()`, JS everywhere else.
30
+ - **ESM-first package** with types for every entry: `use-scroll-animate`, `/react`, `/vue`, `/svelte`, `/solid`, `/element`.
31
+ - **Breaking:** React/Vue factories moved to `/react` and `/vue`; `dist/index.mjs`, `dist/index.esm.js`, `dist/types/*` and `dist/*` deep imports are gone; ES2020 output. The CDN URLs `dist/index.umd.js` and `dist/element.umd.js` are unchanged. Upgrade steps: [MIGRATION](./CHANGELOG.md#migration-from-1x).
32
+
33
+ ## Documentation
34
+
35
+ - 📖 [API reference](./docs/API.md) — every export, option, attribute and config key
36
+ - 🎛️ [Demo / preset playground](./demo/index.html) — every preset clickable, no build step (open `demo/index.html` from a clone)
37
+ - 🔁 Migration guides: [from AOS](./docs/migration-from-aos.md) · [from GSAP ScrollTrigger](./docs/migration-from-gsap-scrolltrigger.md)
38
+ - ⚠️ [Upgrading to 2.0](./docs/deprecations.md) — what 2.0 removed and what replaces it (also the MIGRATION section of the [CHANGELOG](./CHANGELOG.md))
39
+
27
40
  ## Installation
28
41
 
29
42
  ```bash
30
43
  npm install use-scroll-animate
31
44
  ```
32
45
 
46
+ ## Native scroll-driven engine (`engine`) 🏎️
47
+
48
+ In browsers that support CSS scroll-driven animations (`CSS.supports('animation-timeline: view()')`), presets can run on the browser's native **view timeline** instead of the JavaScript engine. The animation is then linked to the scroll position (it plays as the element scrolls in, off the main thread) rather than started by IntersectionObserver and played over a fixed `duration`.
49
+
50
+ ```js
51
+ ScrollAnimate.observe('.card', { animation: 'fade-in-up' }); // native where supported (default 'auto')
52
+ ScrollAnimate.observe('.card', { animation: 'fade-in-up', engine: 'js' }); // always time-based
53
+ const sa = createScrollAnimate({ defaultEngine: 'js' }); // 1.x behaviour for an instance
54
+ ```
55
+
56
+ ```html
57
+ <div data-sa data-sa-animation="zoom-in" data-sa-engine="auto" data-sa-view-range="entry 0%, cover 40%">…</div>
58
+ ```
59
+
60
+ | `engine` | Behaviour |
61
+ |---|---|
62
+ | `'auto'` | **Default since 2.0.** Native view timeline when supported, otherwise JS. Also JS when the element sets `duration`, `delay`, `offset` or `stagger` itself (those only mean something for a time-based animation). |
63
+ | `'css'` | Native view timeline whenever supported (ignores time-based options), otherwise JS. |
64
+ | `'js'` | IntersectionObserver + time-based Web Animation (the 1.x default). `createScrollAnimate({ defaultEngine: 'js' })` restores 1.x behaviour everywhere. |
65
+
66
+ Notes:
67
+ - With the native engine, `duration`, `delay`, `threshold`, `offset` and `stagger` don't apply; the animation spans `viewRange` (default `['entry 0%', 'entry 100%']`, i.e. from the moment the element starts entering until it is fully in view). `easing` still applies.
68
+ - `once` (default) freezes the end state when the animation completes, so scrolling back up does not reverse it; with `repeat: true` it keeps following the scroll in both directions.
69
+ - Callbacks (`onEnter`, `onLeave`, `onStart`, `onComplete`), `onProgress`, `progressVar` and `parallax` keep working.
70
+ - Class-name mode (`useClassNames`), `prefers-reduced-motion`, `animate()`, `sequence()` and `staggerChildren()` always use the JS engine.
71
+ - `supportsScrollTimeline()` is exported if you want to branch on support yourself.
72
+
33
73
  ## v1.4.0 New Features ✨
34
74
 
35
75
  ### True scroll progress (`progressMode: 'scroll'`)
@@ -78,7 +118,7 @@ tl.cancel(); // stop and leave everything visible
78
118
  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
119
 
80
120
  ### 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.
121
+ ESM-first since 2.0: `import` resolves to `dist/*.js` + `dist/*.d.ts`, `require` to `dist/*.cjs` + `dist/*.d.cts`, for the main entry and every subpath. See [2.0](#v200-) below.
82
122
 
83
123
  ## v1.3.0: Custom Easing 🎨
84
124
 
@@ -144,12 +184,73 @@ We've added high-quality physics-based easing presets:
144
184
  | `onStart` / `onComplete` / `onEnter` / `onLeave` | `(el) => void` | – | Lifecycle callbacks |
145
185
  | `onProgress` | `(el, progress) => void` | – | Progress (0–1) as the element scrolls — visible ratio, or true scroll progress with `progressMode: 'scroll'` |
146
186
  | `progressMode` | `'ratio'` \| `'scroll'` | `'ratio'` | How `onProgress`/parallax progress is measured (`'scroll'`: 0 = top enters at the bottom, 1 = bottom leaves at the top) |
187
+ | `engine` | `'auto'` \| `'js'` \| `'css'` | `'auto'` | Run presets on the browser's native scroll-driven timeline when supported (`'auto'`/`'css'`), falling back to JS. See [Native scroll-driven engine](#native-scroll-driven-engine-engine-) |
188
+ | `exit` | `boolean` \| preset \| `{ from, to }` | `false` | Animate out (reverse) when leaving the viewport, back in on re-entry. Implies `repeat`. See [Exit animations](#exit-animations-exit) |
189
+ | `viewRange` | `[string, string]` | `['entry 0%', 'entry 100%']` | Native engine only: view-timeline range of the entrance |
190
+ | `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 |
147
191
 
148
- 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-parallax-x|y|rotate|scale|speed`.
192
+ 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-engine`, `data-sa-exit` (bare = `true`, or a preset), `data-sa-view-range` (`"entry 0%, cover 40%"`), `data-sa-parallax-x|y|rotate|scale|speed`.
149
193
 
150
194
  **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']`.
151
195
 
152
- **Global config** (`createScrollAnimate(config)` / `configure()`): `defaultAnimation`, `defaultDuration`, `defaultDelay`, `defaultEasing`, `defaultThreshold`, `defaultRootMargin`, `defaultRepeat`, `defaultOnce`, `defaultOffset`, `hiddenClass`, `visibleClass`, `useClassNames`, `disabled`, `root`, `autoUnregister` (default `true`).
196
+ **Global config** (`createScrollAnimate(config)` / `configure()`): `defaultAnimation`, `defaultDuration`, `defaultDelay`, `defaultEasing`, `defaultThreshold`, `defaultRootMargin`, `defaultRepeat`, `defaultOnce`, `defaultOffset`, `hiddenClass`, `visibleClass`, `useClassNames`, `disabled`, `root`, `autoUnregister` (default `true`), `defaultEngine` (default `'auto'`).
197
+
198
+ ### Progress as a CSS variable (`progressVar`)
199
+
200
+ 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:
201
+
202
+ ```html
203
+ <div data-sa data-sa-progress="scroll" data-sa-progress-var class="hero">…</div>
204
+
205
+ <style>
206
+ @media (prefers-reduced-motion: no-preference) {
207
+ .hero { transform: translateY(calc((1 - var(--sa-progress, 0)) * 60px)); opacity: calc(0.4 + var(--sa-progress, 0)); }
208
+ }
209
+ </style>
210
+ ```
211
+
212
+ ```js
213
+ ScrollAnimate.observe('.bar', { progressVar: '--fill', progressMode: 'scroll' });
214
+ // .bar::after { transform: scaleX(var(--fill, 0)); }
215
+ ```
216
+
217
+ 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.
218
+
219
+ ### Exit animations (`exit`)
220
+
221
+ Animate elements out when they leave the viewport, and back in when they return:
222
+
223
+ ```js
224
+ ScrollAnimate.observe('.card', { animation: 'fade-in-up', exit: true }); // reverse of the entrance
225
+ ScrollAnimate.observe('.toast', { animation: 'zoom-in', exit: 'fade-in-down' }); // leave with another preset (played in reverse)
226
+ ```
227
+
228
+ ```html
229
+ <div data-sa data-sa-animation="fade-in-left" data-sa-exit>…</div>
230
+ <div data-sa data-sa-exit="zoom-out">…</div>
231
+ ```
232
+
233
+ `exit` accepts `true`, a preset name, an array of presets or `{ from, to }`; the exit plays that animation **in reverse** over `duration` (no delay) and the element stays in its hidden state until it re-enters. It implies `repeat: true` (set `repeat` explicitly to override). With the native engine the exit is scroll-linked too (the view timeline's `exit` range). In class-name mode the hidden/visible classes are swapped back. Under `prefers-reduced-motion` nothing moves and the element stays visible.
234
+
235
+ ### Parallax helper (`parallax()`)
236
+
237
+ ```js
238
+ import { parallax } from 'use-scroll-animate';
239
+
240
+ const stop = parallax('.hero-bg', { speed: 0.3 }); // lags behind the scroll (background)
241
+ parallax('.badge', { speed: -0.15, axis: 'x' }); // drifts sideways, ahead of the scroll
242
+ stop(); // remove listeners and the inline styles it set
243
+ ```
244
+
245
+ | Option | Default | Description |
246
+ |---|---|---|
247
+ | `speed` | `0.2` | Total shift while the element crosses the viewport, as a fraction of the viewport (`0.2` = 20vh / 20vw). Positive = slower than the page, negative = faster |
248
+ | `axis` | `'y'` | `'y'` or `'x'` |
249
+ | `progressVar` | `'--sa-parallax'` | CSS custom property receiving the scroll progress (0–1, same scale as `progressVar` with `progressMode: 'scroll'`) |
250
+ | `root` | viewport | Scroll container |
251
+ | `respectReducedMotion` | `true` | Under `prefers-reduced-motion: reduce` only the variable is written, no offset |
252
+
253
+ It writes the offset to the individual CSS **`translate`** property, so it composes with entrance animations and any `transform` you set. One IntersectionObserver plus a passive, rAF-throttled scroll listener that is attached only while a target is on screen. Tree-shaken it adds under 1 kB gzipped. (The older `parallax: { x, y, rotate, scale }` option still works; it writes `transform`.)
153
254
 
154
255
  ## Instance API
155
256
 
@@ -157,6 +258,7 @@ Every option is also available as a data attribute: `data-sa-animation`, `data-s
157
258
  import ScrollAnimate, { createScrollAnimate } from 'use-scroll-animate';
158
259
 
159
260
  ScrollAnimate.init(root?); // observe every [data-sa] element (safe to call again after DOM changes)
261
+ const stop = ScrollAnimate.watch(root?); // init() + auto-observe [data-sa] elements added later; stop() to end
160
262
  ScrollAnimate.observe(target, opts); // selector, Element, NodeList or Element[]
161
263
  ScrollAnimate.unobserve(target); // stop observing (elements that never animated are made visible)
162
264
  ScrollAnimate.animate(target, opts); // play an animation right now
@@ -170,6 +272,22 @@ const sa = createScrollAnimate({ root: document.querySelector('#scroller') }); /
170
272
  import { sequence, staggerChildren, getScrollProgress } from 'use-scroll-animate';
171
273
  ```
172
274
 
275
+ ### Watching the DOM (`watch()`)
276
+
277
+ For SPAs, CMS content, infinite lists or anything rendered after page load, `watch()` replaces "call `init()` again after every DOM change":
278
+
279
+ ```js
280
+ import ScrollAnimate from 'use-scroll-animate';
281
+
282
+ const stop = ScrollAnimate.watch(); // or watch(document.querySelector('#app'))
283
+ // [data-sa] elements inserted later — even deep inside a new subtree, or an existing
284
+ // element that gains the data-sa attribute — are observed with their data-sa-* options.
285
+ // Elements removed from the DOM are released; finished `once` elements are never replayed.
286
+ stop(); // stop watching (destroy() also stops every watcher)
287
+ ```
288
+
289
+ It uses a single `MutationObserver` per call and is a no-op on the server or without `MutationObserver`.
290
+
173
291
  Via a `<script>` tag (UMD build), the default instance lives at `ScrollAnimate.default`:
174
292
 
175
293
  ```html
@@ -177,11 +295,13 @@ Via a `<script>` tag (UMD build), the default instance lives at `ScrollAnimate.d
177
295
  <script>ScrollAnimate.default.init();</script>
178
296
  ```
179
297
 
180
- ## React & Vue
298
+ ## Frameworks
299
+
300
+ ### React & Vue
181
301
 
182
302
  ```jsx
183
303
  import React from 'react';
184
- import { createReactHooks } from 'use-scroll-animate';
304
+ import { createReactHooks } from 'use-scroll-animate/react';
185
305
  const { useScrollAnimate, useScrollStagger } = createReactHooks(React);
186
306
 
187
307
  function Card() {
@@ -192,7 +312,7 @@ function Card() {
192
312
 
193
313
  ```js
194
314
  import { ref, onMounted, onUnmounted } from 'vue';
195
- import { createVueComposables } from 'use-scroll-animate';
315
+ import { createVueComposables } from 'use-scroll-animate/vue';
196
316
  const { useScrollAnimate, useScrollStagger } = createVueComposables({ ref, onMounted, onUnmounted });
197
317
  const { animateRef } = useScrollAnimate({ animation: 'fade-in-left' });
198
318
  const { staggerRef } = useScrollStagger({ stagger: 60, observeChildren: true }); // <ul ref="staggerRef">
@@ -206,7 +326,61 @@ function Feed({ items }) {
206
326
  }
207
327
  ```
208
328
 
209
- Hooks and composables share the core engine, so `once`, `offset`, custom easing functions, parallax and reduced-motion handling behave exactly like the vanilla API.
329
+ > Since 2.0 `createReactHooks` / `createVueComposables` are only available from `use-scroll-animate/react` / `use-scroll-animate/vue`.
330
+
331
+ ### Svelte (`use-scroll-animate/svelte`)
332
+
333
+ Actions, no `svelte` import needed (Svelte 3, 4 and 5):
334
+
335
+ ```svelte
336
+ <script>
337
+ import { scrollAnimate, scrollStagger } from 'use-scroll-animate/svelte';
338
+ let items = [];
339
+ </script>
340
+
341
+ <h2 use:scrollAnimate={{ animation: 'fade-in-up', duration: 800 }}>Title</h2>
342
+ <ul use:scrollStagger={{ stagger: 60, observeChildren: true }}>
343
+ {#each items as item}<li>{item}</li>{/each}
344
+ </ul>
345
+ ```
346
+
347
+ Updating the action's parameter swaps the callbacks immediately; other options are applied if the element has not animated yet (so visible content is never re-hidden). Pass `instance` to use your own `createScrollAnimate()` instance.
348
+
349
+ ### Solid (`use-scroll-animate/solid`)
350
+
351
+ Directives and a `ref` primitive (`solid-js` is an optional peer dependency, needed only for this entry):
352
+
353
+ ```tsx
354
+ import { scrollAnimate, scrollStagger, useScrollAnimate } from 'use-scroll-animate/solid';
355
+ scrollAnimate; scrollStagger; // keep the directive imports (TypeScript)
356
+
357
+ <div use:scrollAnimate={{ animation: 'zoom-in' }}>…</div>
358
+ <ul use:scrollStagger={{ stagger: 60 }}>…</ul>
359
+ <div ref={useScrollAnimate({ animation: 'fade-in-left' })}>…</div>
360
+ ```
361
+
362
+ `use:scrollAnimate` / `use:scrollStagger` are typed through `JSX.Directives`. Elements are observed on mount and released on cleanup.
363
+
364
+ ### Web Component (`use-scroll-animate/element`)
365
+
366
+ ```html
367
+ <script type="module">
368
+ import { defineScrollAnimate } from 'use-scroll-animate/element';
369
+ defineScrollAnimate(); // registers <scroll-animate>; defineScrollAnimate('my-reveal') for another tag
370
+ </script>
371
+
372
+ <scroll-animate animation="fade-in-up" duration="800" easing="spring">…</scroll-animate>
373
+ ```
374
+
375
+ Or without a build step (registers `<scroll-animate>` on load):
376
+
377
+ ```html
378
+ <script src="https://unpkg.com/use-scroll-animate/dist/element.umd.js"></script>
379
+ ```
380
+
381
+ Attributes are the `data-sa-*` attributes without the prefix (`animation`, `duration`, `delay`, `easing`, `threshold`, `root-margin`, `offset`, `once`, `repeat`, `engine`, `view-range`, `progress`, `progress-var`, `exit`, `parallax-*`). The element dispatches `sa:enter`, `sa:leave`, `sa:start`, `sa:complete` and, with `progress`/`progress-var`, `sa:progress` (`event.detail.progress`). It renders as `display: block` unless you style it.
382
+
383
+ All integrations share the core engine (and, through shared chunks, the same code when you import several entries), so `once`, `offset`, custom easing functions, parallax, the native engine and reduced-motion handling behave exactly like the vanilla API.
210
384
 
211
385
  ## Contributing
212
386
 
package/README_ja.md CHANGED
@@ -23,6 +23,12 @@
23
23
  - 🧩 **フレームワークに依存しない**:Vanilla JS、React、Vue、Svelteなどとシームレスに動作。一流の React Hooks と Vue Composables を内蔵。
24
24
  - ♿ **アクセシブル**:`prefers-reduced-motion` を標準でサポート。
25
25
 
26
+ ## ドキュメント
27
+
28
+ - [API リファレンス](./docs/API.md)(英語)· [デモ](./demo/index.html)(全プリセットをクリックで再生、ビルド不要)
29
+ - 移行ガイド:[AOS から](./docs/migration-from-aos.md) · [GSAP ScrollTrigger から](./docs/migration-from-gsap-scrolltrigger.md)
30
+ - [2.0 へのアップグレード](./docs/deprecations.md):`createReactHooks` / `createVueComposables` は `use-scroll-animate/react` / `/vue` からのみ。`dist/index.mjs`・`dist/index.esm.js`・`dist/types/*`・`dist/*` ディープインポートは削除、デフォルトエンジンは `'auto'`。CDN の `dist/index.umd.js` は変更なし。詳細は [CHANGELOG](./CHANGELOG.md) の MIGRATION。
31
+
26
32
  ## インストール
27
33
 
28
34
  ```bash
@@ -46,6 +52,19 @@ npm install use-scroll-animate
46
52
  </script>
47
53
  ```
48
54
 
55
+ ## ネイティブのスクロール駆動エンジン `engine`(v1.6)
56
+
57
+ CSS スクロール駆動アニメーション(`CSS.supports('animation-timeline: view()')`)に対応したブラウザでは、プリセットをブラウザ標準の **view timeline** 上で実行できます。進行度はスクロール位置に連動し(メインスレッド外)、IntersectionObserver で開始して固定の `duration` で再生する方式ではありません。
58
+
59
+ ```js
60
+ ScrollAnimate.observe('.card', { animation: 'fade-in-up', engine: 'auto' });
61
+ const sa = createScrollAnimate({ defaultEngine: 'auto' });
62
+ ```
63
+
64
+ - `'auto'`:**2.0 からのデフォルト**。対応ブラウザではネイティブ、非対応なら JS。要素が `duration`・`delay`・`offset`・`stagger` を自分で指定した場合も JS。`'css'`:対応時は常にネイティブ。`'js'`:1.x の動作(`defaultEngine: 'js'` で全体に適用)。
65
+ - ネイティブエンジンでは `duration`・`delay`・`threshold`・`offset`・`stagger` は無効で、範囲は `viewRange`(デフォルト `['entry 0%', 'entry 100%']`)。`easing` は有効。HTML:`data-sa-engine`、`data-sa-view-range`。
66
+ - `once`(デフォルト)は完了時に最終状態を固定、`repeat: true` ではスクロールに双方向で追従。クラス名モード・reduced motion・`animate()`・`sequence()`・`staggerChildren()` は常に JS エンジン。`supportsScrollTimeline()` もエクスポート。
67
+
49
68
  ## v1.4.0 の新機能 ✨
50
69
 
51
70
  - **本当のスクロール進捗 `progressMode: 'scroll'`**(オプトイン):`onProgress` はデフォルトで要素の表示比率を返すため、画面より高い要素では 1 に到達しません。有効にすると、要素の上端がビューポート下端に達したとき `0`、下端がビューポート上端を抜けたとき `1` になります。パララックスも同じ進捗を使用します。HTML では `data-sa-progress="scroll"`。ヘルパー `getScrollProgress(el, root?)` もエクスポートされています。
@@ -78,7 +97,7 @@ npm install use-scroll-animate
78
97
 
79
98
  - **新しいプリセット**:`scale-up`、`blur-in-up`、`flip-up`、`flip-down`、`rotate-left`、`rotate-right`、clip-path による `clip-up`、`clip-down`、`clip-left`、`clip-right`、`clip-circle`。
80
99
  - **メモリ使用量の削減**:`once` 要素はアニメーション開始後に自動でレジストリから削除されます(パララックス/`onProgress` が必要な要素を除く)。`WeakSet` で記憶されるため、`init()`/`observe()` で再生されることはありません。従来の挙動は `createScrollAnimate({ autoUnregister: false })`。
81
- - **正しい `exports` マップ**:Node ESM は `dist/index.mjs`、CommonJS は `dist/index.js` に解決され、それぞれ対応する型定義付き。従来の `main`/`module`/`unpkg` と `dist/*` のディープインポートも引き続き利用可能。
100
+ - **正しい `exports` マップ**:2.0 から ESM 優先:`import` → `dist/*.js` + `*.d.ts`、`require` → `dist/*.cjs` + `*.d.cts`。
82
101
 
83
102
  ## v1.2.0 の新機能
84
103
 
@@ -87,6 +106,43 @@ npm install use-scroll-animate
87
106
  - **新しいプリセット**:`shimmer`(シマー)、`pulse`(パルス)、`swing`(スイング)を追加。
88
107
  - **多言語サポート**:中国語と日本語のドキュメントを追加。
89
108
 
109
+ ## フレームワーク連携(v1.7)
110
+
111
+ 各連携は独立したエントリポイント(`use-scroll-animate/react`・`/vue`・`/svelte`・`/solid`・`/element`)で、コアのコードを共有します。
112
+
113
+ ```svelte
114
+ <!-- Svelte:action(svelte の import 不要) -->
115
+ <script>import { scrollAnimate, scrollStagger } from 'use-scroll-animate/svelte';</script>
116
+ <div use:scrollAnimate={{ animation: 'fade-in-up' }}>…</div>
117
+ ```
118
+
119
+ ```tsx
120
+ // Solid:ディレクティブ + ref プリミティブ(solid-js は optional な peer 依存)
121
+ import { scrollAnimate, useScrollAnimate } from 'use-scroll-animate/solid';
122
+ <div use:scrollAnimate={{ animation: 'zoom-in' }}>…</div>
123
+ ```
124
+
125
+ ```html
126
+ <!-- Web Component:属性は data-sa-* から接頭辞を除いたもの。sa:enter / sa:leave / sa:start / sa:complete / sa:progress イベントを発火 -->
127
+ <script type="module">
128
+ import { defineScrollAnimate } from 'use-scroll-animate/element';
129
+ defineScrollAnimate();
130
+ </script>
131
+ <scroll-animate animation="fade-in-up" duration="800">…</scroll-animate>
132
+ ```
133
+
134
+ ## 退場アニメーションとパララックス(v1.8)
135
+
136
+ ```js
137
+ ScrollAnimate.observe('.card', { animation: 'fade-in-up', exit: true }); // ビューポート外へ出るとき入場を逆再生
138
+ ScrollAnimate.observe('.toast', { animation: 'zoom-in', exit: 'fade-in-down' }); // 別プリセットを逆再生して退場
139
+ import { parallax } from 'use-scroll-animate';
140
+ parallax('.hero-bg', { speed: 0.3 }); // 正:ページより遅い、負:速い。axis: 'x' も可
141
+ ```
142
+
143
+ - `exit`:`true`・プリセット名・配列・`{ from, to }`。`repeat: true` を暗黙に有効化。`data-sa-exit` 属性にも対応。reduced motion 時は再生しません。
144
+ - `parallax()`:進行度を CSS 変数(既定 `--sa-parallax`)に、オフセットを個別の `translate` プロパティに書き込むため `transform` と競合しません。reduced motion 時はオフセットなし。停止関数を返します。
145
+
90
146
  ## 主な設定
91
147
 
92
148
  | オプション | 型 | デフォルト | 説明 |
package/README_zh.md CHANGED
@@ -23,6 +23,12 @@
23
23
  - 🧩 **框架无关**:完美支持原生 JS、React、Vue、Svelte 等。内置一流的 React Hooks 和 Vue Composables。
24
24
  - ♿ **无障碍**:原生支持 `prefers-reduced-motion`。
25
25
 
26
+ ## 文档
27
+
28
+ - [API 参考](./docs/API.md)(英文)· [演示页](./demo/index.html)(每个预设都可点击,无需构建)
29
+ - 迁移指南:[从 AOS 迁移](./docs/migration-from-aos.md) · [从 GSAP ScrollTrigger 迁移](./docs/migration-from-gsap-scrolltrigger.md)
30
+ - [升级到 2.0](./docs/deprecations.md):`createReactHooks` / `createVueComposables` 只能从 `use-scroll-animate/react` / `/vue` 导入;`dist/index.mjs`、`dist/index.esm.js`、`dist/types/*` 与 `dist/*` 深层导入已移除;默认引擎改为 `'auto'`。CDN 地址 `dist/index.umd.js` 不变。详见 [CHANGELOG](./CHANGELOG.md) 的 MIGRATION 部分。
31
+
26
32
  ## 安装
27
33
 
28
34
  ```bash
@@ -46,6 +52,20 @@ npm install use-scroll-animate
46
52
  </script>
47
53
  ```
48
54
 
55
+ ## 原生滚动驱动引擎 `engine`(v1.6)
56
+
57
+ 在支持 CSS 滚动驱动动画(`CSS.supports('animation-timeline: view()')`)的浏览器中,预设动画可以运行在浏览器原生的 **view timeline** 上:动画进度跟随滚动位置(在主线程之外),而不是由 IntersectionObserver 触发后按固定 `duration` 播放。
58
+
59
+ ```js
60
+ ScrollAnimate.observe('.card', { animation: 'fade-in-up', engine: 'auto' });
61
+ const sa = createScrollAnimate({ defaultEngine: 'auto' }); // 实例级默认
62
+ ```
63
+
64
+ - `'auto'`:**2.0 起的默认值**,支持时使用原生时间线,否则回退 JS;若元素自行设置了 `duration`、`delay`、`offset` 或 `stagger`,则使用 JS。`'css'`:支持时始终使用原生时间线。`'js'`:1.x 的行为(`defaultEngine: 'js'` 可全局恢复)。
65
+ - 原生引擎下 `duration`、`delay`、`threshold`、`offset`、`stagger` 不生效;动画区间由 `viewRange` 决定(默认 `['entry 0%', 'entry 100%']`),`easing` 仍然有效。HTML:`data-sa-engine`、`data-sa-view-range="entry 0%, cover 40%"`。
66
+ - `once`(默认)在动画完成后固定最终状态;`repeat: true` 时随滚动双向播放。回调、`onProgress`、`progressVar`、视差照常工作。
67
+ - 类名模式、`prefers-reduced-motion`、`animate()`、`sequence()`、`staggerChildren()` 始终使用 JS 引擎。另导出 `supportsScrollTimeline()`。
68
+
49
69
  ## v1.4.0 新特性 ✨
50
70
 
51
71
  - **真实滚动进度 `progressMode: 'scroll'`**(可选):`onProgress` 默认返回元素的可见比例,对高于屏幕的元素永远到不了 1。开启后进度为:元素顶部到达视口底部时为 `0`,底部离开视口顶部时为 `1`。视差同样使用该进度。HTML 写法:`data-sa-progress="scroll"`;另导出辅助函数 `getScrollProgress(el, root?)`。
@@ -78,7 +98,7 @@ npm install use-scroll-animate
78
98
 
79
99
  - **新预设**:`scale-up`、`blur-in-up`、`flip-up`、`flip-down`、`rotate-left`、`rotate-right`,以及 clip-path 揭示 `clip-up`、`clip-down`、`clip-left`、`clip-right`、`clip-circle`。
80
100
  - **更省内存**:`once` 元素动画触发后自动从注册表移除(仍需视差/`onProgress` 的除外),并记录在 `WeakSet` 中,`init()`/`observe()` 不会重复播放。如需旧行为可设置 `createScrollAnimate({ autoUnregister: false })`。
81
- - **规范的 `exports` 字段**:Node ESM 解析到 `dist/index.mjs`,CommonJS 解析到 `dist/index.js`,均带对应类型声明;原有 `main`/`module`/`unpkg` 与 `dist/*` 深层导入保持可用。
101
+ - **规范的 `exports` 字段**:2.0 起以 ESM 为主:`import` → `dist/*.js` + `*.d.ts`,`require` → `dist/*.cjs` + `*.d.cts`。
82
102
 
83
103
  ## v1.2.0 新特性
84
104
 
@@ -87,6 +107,51 @@ npm install use-scroll-animate
87
107
  - **新预设**:新增 `shimmer`(流光)、`pulse`(脉冲)、`swing`(摇摆)。
88
108
  - **多语言支持**:新增中文和日文文档。
89
109
 
110
+ ## 框架集成(v1.7)
111
+
112
+ 每个集成都是独立的入口(`use-scroll-animate/react`、`/vue`、`/svelte`、`/solid`、`/element`),共享同一份核心代码。
113
+
114
+ ```svelte
115
+ <!-- Svelte:action,无需引入 svelte -->
116
+ <script>import { scrollAnimate, scrollStagger } from 'use-scroll-animate/svelte';</script>
117
+ <div use:scrollAnimate={{ animation: 'fade-in-up' }}>…</div>
118
+ <ul use:scrollStagger={{ stagger: 60 }}>…</ul>
119
+ ```
120
+
121
+ ```tsx
122
+ // Solid:指令 + ref 原语(solid-js 为可选 peer 依赖)
123
+ import { scrollAnimate, useScrollAnimate } from 'use-scroll-animate/solid';
124
+ <div use:scrollAnimate={{ animation: 'zoom-in' }}>…</div>
125
+ <div ref={useScrollAnimate({ animation: 'fade-in-left' })}>…</div>
126
+ ```
127
+
128
+ ```html
129
+ <!-- Web Component:属性与 data-sa-* 相同(去掉前缀),并派发 sa:enter / sa:leave / sa:start / sa:complete / sa:progress 事件 -->
130
+ <script type="module">
131
+ import { defineScrollAnimate } from 'use-scroll-animate/element';
132
+ defineScrollAnimate();
133
+ </script>
134
+ <scroll-animate animation="fade-in-up" duration="800">…</scroll-animate>
135
+ <!-- 无构建:<script src="https://unpkg.com/use-scroll-animate/dist/element.umd.js"></script> -->
136
+ ```
137
+
138
+ ## 退场动画与视差辅助函数(v1.8)
139
+
140
+ ```js
141
+ ScrollAnimate.observe('.card', { animation: 'fade-in-up', exit: true }); // 离开视口时反向播放入场动画
142
+ ScrollAnimate.observe('.toast', { animation: 'zoom-in', exit: 'fade-in-down' }); // 用另一个预设(反向)退场
143
+ ```
144
+
145
+ - `exit`:`true`、预设名、预设数组或 `{ from, to }`;离开视口时反向播放,再次进入时重新入场(默认隐含 `repeat: true`)。HTML:`data-sa-exit` / `data-sa-exit="zoom-out"`。原生引擎下退场同样随滚动驱动;减少动态效果时不播放。
146
+
147
+ ```js
148
+ import { parallax } from 'use-scroll-animate';
149
+ const stop = parallax('.hero-bg', { speed: 0.3 }); // 正值:比页面慢(背景);负值:比页面快
150
+ parallax('.badge', { speed: -0.15, axis: 'x' });
151
+ ```
152
+
153
+ - `parallax(target, { speed = 0.2, axis = 'y', progressVar = '--sa-parallax', root, respectReducedMotion = true })`:进度写入 CSS 变量,位移写入独立的 `translate` 属性(与 `transform` 和入场动画互不冲突);`prefers-reduced-motion` 时只写变量不位移。返回停止函数。
154
+
90
155
  ## 核心配置
91
156
 
92
157
  | 选项 | 类型 | 默认值 | 描述 |
@@ -100,6 +165,8 @@ npm install use-scroll-animate
100
165
  | `stagger` | `number` | `0` | 同批次显现的兄弟元素之间的额外延迟 (ms) |
101
166
  | `onProgress` | `(el, progress) => void` | – | 滚动进度回调 (0–1) |
102
167
  | `progressMode` | `'ratio'` \| `'scroll'` | `'ratio'` | 进度计算方式:可见比例或真实滚动进度 |
168
+ | `engine` | `'auto'` \| `'js'` \| `'css'` | `'auto'` | 支持时使用原生滚动驱动时间线,否则回退 JS |
169
+ | `viewRange` | `[string, string]` | `['entry 0%', 'entry 100%']` | 仅原生引擎:入场动画的时间线区间 |
103
170
 
104
171
  ## 许可证
105
172