@yorozu/animations 0.3.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/LICENSE +21 -0
- package/README.md +199 -0
- package/index.d.ts +1 -0
- package/index.js +2229 -0
- package/package.json +21 -0
- package/playground/src/catalog.d.ts +8 -0
- package/playground/src/level-switch.d.ts +1 -0
- package/playground/src/level.d.ts +7 -0
- package/playground/src/main.d.ts +0 -0
- package/playground/src/overlay.d.ts +2 -0
- package/playground/src/testers/digit-flip.d.ts +1 -0
- package/playground/src/testers/dock.d.ts +1 -0
- package/playground/src/testers/fade.d.ts +1 -0
- package/playground/src/testers/list-layer.d.ts +1 -0
- package/playground/src/testers/list-reorder.d.ts +1 -0
- package/playground/src/testers/pinch-zoom.d.ts +1 -0
- package/playground/src/testers/popover.d.ts +1 -0
- package/playground/src/testers/presence-pop.d.ts +1 -0
- package/playground/src/testers/ripple.d.ts +1 -0
- package/playground/src/testers/scroll-tween.d.ts +1 -0
- package/playground/src/testers/send-flight.d.ts +1 -0
- package/playground/src/testers/shared-element.d.ts +1 -0
- package/playground/src/testers/sliding-indicator.d.ts +1 -0
- package/playground/src/testers/spoiler.d.ts +1 -0
- package/playground/src/testers/swipe-reveal.d.ts +1 -0
- package/playground/src/testers/tabs.d.ts +1 -0
- package/playground/src/testers/view-slide-modes.d.ts +3 -0
- package/playground/src/testers/view-slide.d.ts +1 -0
- package/playground/src/testers/waveform.d.ts +1 -0
- package/src/core/level.d.ts +10 -0
- package/src/core/playback.d.ts +7 -0
- package/src/core/raf.d.ts +1 -0
- package/src/core/reduced-motion.d.ts +1 -0
- package/src/core/styles.d.ts +2 -0
- package/src/core/tween.d.ts +11 -0
- package/src/core/types.d.ts +9 -0
- package/src/digit-flip/play.d.ts +12 -0
- package/src/digit-flip/slots.d.ts +14 -0
- package/src/dock/dock.d.ts +23 -0
- package/src/dock/transforms.d.ts +14 -0
- package/src/fade/fade.d.ts +11 -0
- package/src/index.d.ts +45 -0
- package/src/list-reorder/classify.d.ts +10 -0
- package/src/list-reorder/reorder.d.ts +19 -0
- package/src/pinch-zoom/math.d.ts +21 -0
- package/src/pinch-zoom/pinch.d.ts +15 -0
- package/src/popover/popover.d.ts +14 -0
- package/src/rect/fit.d.ts +5 -0
- package/src/rect/types.d.ts +17 -0
- package/src/ripple/ripple.d.ts +13 -0
- package/src/scroll-tween/scroll.d.ts +8 -0
- package/src/send-flight/flight.d.ts +14 -0
- package/src/shared-element/math.d.ts +45 -0
- package/src/shared-element/player.d.ts +52 -0
- package/src/sliding-indicator/indicator.d.ts +15 -0
- package/src/spoiler/spoiler.d.ts +14 -0
- package/src/swipe-reveal/swipe.d.ts +16 -0
- package/src/view-slide/session.d.ts +27 -0
- package/src/view-slide/transforms.d.ts +34 -0
- package/src/waveform/decode.d.ts +2 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ivan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# @yorozu/animations
|
|
2
|
+
|
|
3
|
+
Framework-agnostic compositor motion primitives for the browser.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @yorozu/animations
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Import from the package root:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {
|
|
15
|
+
createSharedElement,
|
|
16
|
+
playSharedElement,
|
|
17
|
+
createViewSlide,
|
|
18
|
+
createSlidingIndicator,
|
|
19
|
+
createListReorder,
|
|
20
|
+
prefersReducedMotion,
|
|
21
|
+
canAnimate,
|
|
22
|
+
resolveViewSlideMode,
|
|
23
|
+
} from "@yorozu/animations"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## What it is
|
|
27
|
+
|
|
28
|
+
Small, host-driven helpers built on the Web Animations API (WAAPI). Motion stays on compositor-friendly properties — primarily `transform` and `opacity` — so frames stay cheap.
|
|
29
|
+
|
|
30
|
+
The package is framework-agnostic: factories return plain controllers; you own the DOM, measurement timing, and when to call into the API. No framework effects, no layout thrashing inside the helpers.
|
|
31
|
+
|
|
32
|
+
## Shared element
|
|
33
|
+
|
|
34
|
+
Use a one-shot flight with `playSharedElement`, or a reusable controller with `createSharedElement` when open/close need to cancel each other.
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { createSharedElement, type Rect } from "@yorozu/animations"
|
|
38
|
+
|
|
39
|
+
const se = createSharedElement()
|
|
40
|
+
const from: Rect = thumb.getBoundingClientRect()
|
|
41
|
+
const to: Rect = stage.getBoundingClientRect()
|
|
42
|
+
|
|
43
|
+
const playback = se.play({
|
|
44
|
+
host: document.body,
|
|
45
|
+
from,
|
|
46
|
+
to,
|
|
47
|
+
imageUrl: thumb.src,
|
|
48
|
+
hideTarget: stage,
|
|
49
|
+
})
|
|
50
|
+
// playback?.done is Promise<boolean> — true if finished, false if cancelled
|
|
51
|
+
// se.cancel() aborts an in-flight clone
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`playSharedElement(opts)` is the same one-shot path without a retained controller (still returns `Playback | null`).
|
|
55
|
+
|
|
56
|
+
`playOpen` / `playClose` accept seeds and viewport insets when the host does not already have both rects. Math helpers (`computeFlight`, `computeOpenFlight`, …) are exported for custom layouts.
|
|
57
|
+
|
|
58
|
+
## Intensity
|
|
59
|
+
|
|
60
|
+
Three playback levels. The OS `prefers-reduced-motion` query is **seed only** — after the host stores a pick, that value owns playback.
|
|
61
|
+
|
|
62
|
+
| Level | Meaning |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `low` | no motion (instant switches) |
|
|
65
|
+
| `med` | softer transitions |
|
|
66
|
+
| `high` | full transitions |
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import {
|
|
70
|
+
defaultAnimationLevel,
|
|
71
|
+
canAnimate,
|
|
72
|
+
resolveViewSlideMode,
|
|
73
|
+
prefersReducedMotion,
|
|
74
|
+
} from "@yorozu/animations"
|
|
75
|
+
|
|
76
|
+
let level = stored ?? defaultAnimationLevel(prefersReducedMotion())
|
|
77
|
+
// first-run seed: med when the OS asks for reduce, otherwise high
|
|
78
|
+
|
|
79
|
+
resolveViewSlideMode(level, "stack") // none | crossfade | push
|
|
80
|
+
resolveViewSlideMode(level, "layer") // none | crossfade | cover
|
|
81
|
+
canAnimate(level) // false only for low
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`pickAnimationLevelFromRatio` / `stepAnimationLevel` / `cycleAnimationLevel` drive a three-stop slider. `prefersReducedMotion()` stays an honest media-query probe.
|
|
85
|
+
|
|
86
|
+
## View slide
|
|
87
|
+
|
|
88
|
+
Panel stack transitions driven by an active key. Built-in modes: `push` (full-width), `crossfade` (±1.5rem + opacity), `cover` (scale-out leave + 200% enter), `peek` (~20% back + dim), `lift` (vertical ±100%), `zoom` (scale 1.1 / 0.95), `reveal` (`clip-path` inset wipe), `none`.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { createViewSlide, slideDirectionByIndex, resolveViewSlideMode } from "@yorozu/animations"
|
|
92
|
+
|
|
93
|
+
const items = [{ id: "a" }, { id: "b" }, { id: "c" }]
|
|
94
|
+
const slide = createViewSlide({
|
|
95
|
+
getMode: () => resolveViewSlideMode(level, "stack"),
|
|
96
|
+
getDirection: (from, to) => slideDirectionByIndex(from, to, items),
|
|
97
|
+
mountPolicy: "active-plus-leaving", // or "keep-visited"
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Render only keys in slide.mountedKeys, then:
|
|
101
|
+
const handle = slide.attach(panelEl, key)
|
|
102
|
+
slide.setActive(nextKey)
|
|
103
|
+
// handle.update(newKey) / handle.destroy() when the node unmounts
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`setActive` starts the pair animation when both panels are attached. `mountedKeys`, `role`, and `isVisible` drive host rendering. Call `cancel` / `destroy` on teardown.
|
|
107
|
+
|
|
108
|
+
## Sliding indicator
|
|
109
|
+
|
|
110
|
+
Track an underline (or similar) under the active control. Size snaps; only position is tweened.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { createSlidingIndicator } from "@yorozu/animations"
|
|
114
|
+
|
|
115
|
+
const indicator = createSlidingIndicator({
|
|
116
|
+
getTrack: () => trackEl,
|
|
117
|
+
getIndicator: () => indicatorEl,
|
|
118
|
+
getActive: () => activeTabEl,
|
|
119
|
+
enabled: () => !prefersReducedMotion(),
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// After the active item changes (and layout has updated):
|
|
123
|
+
indicator.measure()
|
|
124
|
+
// ResizeObserver on the track also remeasures
|
|
125
|
+
// indicator.destroy() on teardown
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## List reorder
|
|
129
|
+
|
|
130
|
+
Index-based FLIP for fixed-height lists. Geometry is `delta = -orderDiff * itemHeight` — no `getBoundingClientRect`.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { createListReorder } from "@yorozu/animations"
|
|
134
|
+
|
|
135
|
+
const reorder = createListReorder<Item>({
|
|
136
|
+
getItemHeight: () => 48,
|
|
137
|
+
getKey: (item) => item.id,
|
|
138
|
+
isEnabled: () => true,
|
|
139
|
+
isReduced: () => prefersReducedMotion(),
|
|
140
|
+
isSuppressed: () => isDragging,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Per mounted row:
|
|
144
|
+
const handle = reorder.register(rowEl, item.id)
|
|
145
|
+
// After the order changes:
|
|
146
|
+
reorder.sync(items)
|
|
147
|
+
// handle.destroy() when the row leaves the window
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Classifier helpers `buildOrderDiff` and `classifyReorderAnim` are public if the host needs the same majority/minority rules outside the controller.
|
|
151
|
+
|
|
152
|
+
## Motion catalog
|
|
153
|
+
|
|
154
|
+
| Name | API | Notes |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| Intensity | `AnimationLevel` | `low` / `med` / `high`; OS seed-only |
|
|
157
|
+
| Stack slide | `createViewSlide` `push` | Full-width 100% translate |
|
|
158
|
+
| Soft slide | `createViewSlide` `crossfade` | ±1.5rem + opacity |
|
|
159
|
+
| Cover slide | `createViewSlide` `cover` | Scale-out leave + 200% enter (list-layer open/close) |
|
|
160
|
+
| Peek slide | `createViewSlide` `peek` | Incoming full-width; outgoing ~20% back + dim |
|
|
161
|
+
| Lift | `createViewSlide` `lift` | Vertical `translateY` ±100% |
|
|
162
|
+
| Zoom | `createViewSlide` `zoom` | Scale 1.1 / 0.95 + short opacity |
|
|
163
|
+
| Reveal | `createViewSlide` `reveal` | `clip-path` inset wipe |
|
|
164
|
+
| Shared element | `createSharedElement` | Thumb ↔ stage flight |
|
|
165
|
+
| Sliding indicator | `createSlidingIndicator` | Size snap, position tween |
|
|
166
|
+
| List reorder | `createListReorder` | Index FLIP, fixed height |
|
|
167
|
+
| Dock | `createDock` | Edge open/close + backdrop fade |
|
|
168
|
+
| Fade | `createFade` | Opacity-only show/hide |
|
|
169
|
+
| Popover | `createPopover` | Scale + fade from an origin |
|
|
170
|
+
| Digit flip | `buildDigitSlots` / `playDigitFlip` | Right-aligned slots + `rotateX` |
|
|
171
|
+
| Presence pop | `shouldPresencePop` / `playPresencePop` | Scale-in only on 0 → N |
|
|
172
|
+
| Send flight | `playSendFlight` | Clone from an origin to a list insert |
|
|
173
|
+
| Swipe reveal | `createSwipeReveal` | Pointer rubber + release tween |
|
|
174
|
+
| Scroll tween | `playScrollTween` | Animate `scrollLeft` / `scrollTop` |
|
|
175
|
+
| Ripple | `playRipple` | Touch ink at pointer |
|
|
176
|
+
| Pinch zoom | `createPinchZoom` | Clamp / origin zoom; pan when scale > 1 |
|
|
177
|
+
| Waveform | `decodeWaveform` / `fitWaveform` | Packed 5-bit samples, resampled bars |
|
|
178
|
+
| Spoiler | `createSpoiler` | Dot-field overlay; reveal fades it out |
|
|
179
|
+
|
|
180
|
+
## Reduced motion
|
|
181
|
+
|
|
182
|
+
Respect user preference and host suppressors:
|
|
183
|
+
|
|
184
|
+
- **`prefersReducedMotion()`** — reads `(prefers-reduced-motion: reduce)`. Use it only to **seed** `defaultAnimationLevel`. Stored `low` / `med` / `high` owns playback after that.
|
|
185
|
+
- **`canAnimate(level)`** — false only for `low`.
|
|
186
|
+
- **View slide `mode: "none"`** — skips animation; active key still updates and mount policy still applies.
|
|
187
|
+
- **List reorder `isReduced`** — clears the order baseline (no FLIP).
|
|
188
|
+
- **List reorder `isSuppressed`** — updates the baseline without animating (useful during drag).
|
|
189
|
+
- **Sliding indicator `enabled`** — when false, snaps transform without WAAPI.
|
|
190
|
+
|
|
191
|
+
Wire `getMode`, `isReduced`, and `enabled` from the stored intensity so every primitive stays consistent.
|
|
192
|
+
|
|
193
|
+
## Performance notes
|
|
194
|
+
|
|
195
|
+
- **Compositor-only:** prefer `transform` / `opacity`; avoid animating layout properties.
|
|
196
|
+
- **Index FLIP:** list reorder uses order indices × fixed height, not layout measurement, so it stays safe for virtualized windows.
|
|
197
|
+
- **Cancel before retarget:** controllers cancel in-flight WAAPI before starting a new run; call `cancel` / `destroy` when unmounting hosts to avoid leaked animations and clones.
|
|
198
|
+
- **`dualRaf`:** exported for hosts that need two animation frames before measuring or starting motion after a paint.
|
|
199
|
+
- **First layout is a baseline:** first indicator measure and first reorder `sync` establish state without animating.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./src/index.js"
|