react-native-ai-animations 0.0.0-stage → 0.8.1
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 +226 -2
- package/lib/module/brand/index.js +4 -0
- package/lib/module/brand/index.js.map +1 -0
- package/lib/module/brand/spinning-mark.js +96 -0
- package/lib/module/brand/spinning-mark.js.map +1 -0
- package/lib/module/chat/effort-bar.js +853 -0
- package/lib/module/chat/effort-bar.js.map +1 -0
- package/lib/module/chat/index.js +8 -0
- package/lib/module/chat/index.js.map +1 -0
- package/lib/module/chat/question-card.js +720 -0
- package/lib/module/chat/question-card.js.map +1 -0
- package/lib/module/chat/reasoning.js +38 -0
- package/lib/module/chat/reasoning.js.map +1 -0
- package/lib/module/chat/status-row.js +140 -0
- package/lib/module/chat/status-row.js.map +1 -0
- package/lib/module/chat/thinking-block.js +255 -0
- package/lib/module/chat/thinking-block.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/internal/color.js +15 -0
- package/lib/module/internal/color.js.map +1 -0
- package/lib/module/internal/use-elapsed.js +20 -0
- package/lib/module/internal/use-elapsed.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/skia/dot-orb.js +256 -0
- package/lib/module/skia/dot-orb.js.map +1 -0
- package/lib/module/skia/index.js +8 -0
- package/lib/module/skia/index.js.map +1 -0
- package/lib/module/skia/liquid-orb.js +303 -0
- package/lib/module/skia/liquid-orb.js.map +1 -0
- package/lib/module/skia/matrix-orb.js +272 -0
- package/lib/module/skia/matrix-orb.js.map +1 -0
- package/lib/module/skia/media-generating.js +524 -0
- package/lib/module/skia/media-generating.js.map +1 -0
- package/lib/module/skia/voice-wave.js +278 -0
- package/lib/module/skia/voice-wave.js.map +1 -0
- package/lib/module/text/index.js +6 -0
- package/lib/module/text/index.js.map +1 -0
- package/lib/module/text/shimmer-text.js +122 -0
- package/lib/module/text/shimmer-text.js.map +1 -0
- package/lib/module/text/spoken-text.js +85 -0
- package/lib/module/text/spoken-text.js.map +1 -0
- package/lib/module/text/streaming-text.js +135 -0
- package/lib/module/text/streaming-text.js.map +1 -0
- package/lib/module/theme/index.js +5 -0
- package/lib/module/theme/index.js.map +1 -0
- package/lib/module/theme/palette.js +45 -0
- package/lib/module/theme/palette.js.map +1 -0
- package/lib/module/theme/provider.js +65 -0
- package/lib/module/theme/provider.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/brand/index.d.ts +2 -0
- package/lib/typescript/src/brand/index.d.ts.map +1 -0
- package/lib/typescript/src/brand/spinning-mark.d.ts +21 -0
- package/lib/typescript/src/brand/spinning-mark.d.ts.map +1 -0
- package/lib/typescript/src/chat/effort-bar.d.ts +42 -0
- package/lib/typescript/src/chat/effort-bar.d.ts.map +1 -0
- package/lib/typescript/src/chat/index.d.ts +6 -0
- package/lib/typescript/src/chat/index.d.ts.map +1 -0
- package/lib/typescript/src/chat/question-card.d.ts +73 -0
- package/lib/typescript/src/chat/question-card.d.ts.map +1 -0
- package/lib/typescript/src/chat/reasoning.d.ts +20 -0
- package/lib/typescript/src/chat/reasoning.d.ts.map +1 -0
- package/lib/typescript/src/chat/status-row.d.ts +20 -0
- package/lib/typescript/src/chat/status-row.d.ts.map +1 -0
- package/lib/typescript/src/chat/thinking-block.d.ts +35 -0
- package/lib/typescript/src/chat/thinking-block.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/internal/color.d.ts +2 -0
- package/lib/typescript/src/internal/color.d.ts.map +1 -0
- package/lib/typescript/src/internal/use-elapsed.d.ts +3 -0
- package/lib/typescript/src/internal/use-elapsed.d.ts.map +1 -0
- package/lib/typescript/src/skia/dot-orb.d.ts +16 -0
- package/lib/typescript/src/skia/dot-orb.d.ts.map +1 -0
- package/lib/typescript/src/skia/index.d.ts +6 -0
- package/lib/typescript/src/skia/index.d.ts.map +1 -0
- package/lib/typescript/src/skia/liquid-orb.d.ts +20 -0
- package/lib/typescript/src/skia/liquid-orb.d.ts.map +1 -0
- package/lib/typescript/src/skia/matrix-orb.d.ts +12 -0
- package/lib/typescript/src/skia/matrix-orb.d.ts.map +1 -0
- package/lib/typescript/src/skia/media-generating.d.ts +59 -0
- package/lib/typescript/src/skia/media-generating.d.ts.map +1 -0
- package/lib/typescript/src/skia/voice-wave.d.ts +17 -0
- package/lib/typescript/src/skia/voice-wave.d.ts.map +1 -0
- package/lib/typescript/src/text/index.d.ts +4 -0
- package/lib/typescript/src/text/index.d.ts.map +1 -0
- package/lib/typescript/src/text/shimmer-text.d.ts +13 -0
- package/lib/typescript/src/text/shimmer-text.d.ts.map +1 -0
- package/lib/typescript/src/text/spoken-text.d.ts +15 -0
- package/lib/typescript/src/text/spoken-text.d.ts.map +1 -0
- package/lib/typescript/src/text/streaming-text.d.ts +16 -0
- package/lib/typescript/src/text/streaming-text.d.ts.map +1 -0
- package/lib/typescript/src/theme/index.d.ts +3 -0
- package/lib/typescript/src/theme/index.d.ts.map +1 -0
- package/lib/typescript/src/theme/palette.d.ts +33 -0
- package/lib/typescript/src/theme/palette.d.ts.map +1 -0
- package/lib/typescript/src/theme/provider.d.ts +13 -0
- package/lib/typescript/src/theme/provider.d.ts.map +1 -0
- package/package.json +133 -4
- package/src/brand/index.ts +5 -0
- package/src/brand/spinning-mark.tsx +118 -0
- package/src/chat/effort-bar.tsx +748 -0
- package/src/chat/index.ts +45 -0
- package/src/chat/question-card.tsx +649 -0
- package/src/chat/reasoning.ts +30 -0
- package/src/chat/status-row.tsx +127 -0
- package/src/chat/thinking-block.tsx +250 -0
- package/src/index.ts +2 -0
- package/src/internal/color.ts +18 -0
- package/src/internal/use-elapsed.ts +21 -0
- package/src/skia/dot-orb.tsx +248 -0
- package/src/skia/index.ts +34 -0
- package/src/skia/liquid-orb.tsx +274 -0
- package/src/skia/matrix-orb.tsx +337 -0
- package/src/skia/media-generating.tsx +389 -0
- package/src/skia/voice-wave.tsx +215 -0
- package/src/text/index.ts +3 -0
- package/src/text/shimmer-text.tsx +140 -0
- package/src/text/spoken-text.tsx +82 -0
- package/src/text/streaming-text.tsx +144 -0
- package/src/theme/index.ts +11 -0
- package/src/theme/palette.ts +62 -0
- package/src/theme/provider.tsx +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ahmet Kar
|
|
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
CHANGED
|
@@ -1,3 +1,227 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React Native AI Animations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/react-native-ai-animations)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
Animated React Native and Expo components for AI chat apps: streaming text,
|
|
7
|
+
shimmer loading text, thinking and status indicators, voice orbs and
|
|
8
|
+
waveforms, image and video generation loaders, question cards and reasoning
|
|
9
|
+
effort bars. Built on Reanimated and Skia, themeable, and reduce motion aware.
|
|
10
|
+
|
|
11
|
+
**Docs and live demos:** [nami-motion-web.vercel.app](https://nami-motion-web.vercel.app/)
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx expo install react-native-ai-animations react-native-reanimated react-native-worklets
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The orbs, waveforms and media loaders in `react-native-ai-animations/skia` also need Skia:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx expo install @shopify/react-native-skia
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Peer | Version |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `react` | 19.2 or newer |
|
|
28
|
+
| `react-native` | 0.83 or newer |
|
|
29
|
+
| `react-native-reanimated` | 4.2 or newer |
|
|
30
|
+
| `react-native-worklets` | 0.7 or newer |
|
|
31
|
+
| `@shopify/react-native-skia` | 2.4 or newer, optional |
|
|
32
|
+
|
|
33
|
+
> Formerly published as `nami-motion`. Replace `nami-motion` with
|
|
34
|
+
> `react-native-ai-animations` in your imports.
|
|
35
|
+
|
|
36
|
+
## Components
|
|
37
|
+
|
|
38
|
+
Every component and style is shown in motion, with copyable code and agent
|
|
39
|
+
prompts, on the [website](https://nami-motion-web.vercel.app/).
|
|
40
|
+
|
|
41
|
+
| Component | Entry | What it does |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| Shimmer Text | `/text` | A light band sweeping across text while a response is being generated. |
|
|
44
|
+
| Streaming Text | `/text` | Words of a streaming answer fade and rise in, with a blinking caret. |
|
|
45
|
+
| Spoken Text | `/text` | Highlights the word being spoken and dims the words already read aloud, for voice replies. |
|
|
46
|
+
| Spinning Mark | `/brand` | Spins any icon you pass (your logo) with optional breathing and entrance. |
|
|
47
|
+
| Status Row | `/chat` | Pulsing icon, shimmering label and elapsed timer for agent status. |
|
|
48
|
+
| Thinking Block | `/chat` | "Thought for Ns" header that springs open into a step timeline. |
|
|
49
|
+
| Question Card | `/chat` | Paged questions with numbered options and a custom answer, as a card or a sheet. |
|
|
50
|
+
| Effort Bar | `/chat` | Reasoning effort control wired to `reasoning.effort` (low, medium, high) in four styles: bars, track, segmented and slider. |
|
|
51
|
+
| Media Generating | `/skia` | One loading state for image and video generation in six variants: blobs, matrix, orbit, scan, constellation and helix. |
|
|
52
|
+
| Dot Orb | `/skia` | The dot sphere behind the media variants, also usable on its own. |
|
|
53
|
+
| Matrix Orb | `/skia` | Dot matrix orb with idle, listening and thinking states and per state colors. |
|
|
54
|
+
| Liquid Orb | `/skia` | Voice orb in metal, water, iridescent or ring material that reacts to idle, listening, thinking and speaking and a live level. |
|
|
55
|
+
| Voice Wave | `/skia` | Live microphone waveform in four styles: lines, bars, dots and pulse. |
|
|
56
|
+
|
|
57
|
+
Entries are subpaths of `react-native-ai-animations`. The root export holds
|
|
58
|
+
`/theme` and `/text`; nothing that needs Skia is imported from the root.
|
|
59
|
+
|
|
60
|
+
Every style is also its own component with the same props minus `variant`:
|
|
61
|
+
|
|
62
|
+
- `VoiceWaveLines`, `VoiceWaveBars`, `VoiceWaveDots`, `VoiceWavePulse`
|
|
63
|
+
- `EffortBars`, `EffortTrack`, `EffortSegmented`, `EffortSlider`
|
|
64
|
+
- `MediaGeneratingBlobs`, `MediaGeneratingMatrix`, `MediaGeneratingOrbit`,
|
|
65
|
+
`MediaGeneratingScan`, `MediaGeneratingConstellation`, `MediaGeneratingHelix`
|
|
66
|
+
- `QuestionSheet`
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
### Shimmer Text
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { ShimmerText } from "react-native-ai-animations/text";
|
|
74
|
+
|
|
75
|
+
<ShimmerText text="Generating a response" activeColor={colors.text} mutedColor={colors.textTertiary} />;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Streaming Text, Status Row, Thinking Block
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { StreamingText } from "react-native-ai-animations/text";
|
|
82
|
+
import { StatusRow, ThinkingBlock } from "react-native-ai-animations/chat";
|
|
83
|
+
|
|
84
|
+
<StatusRow label="Searching the web" icon={<Logo />} startedAt={startedAt} />;
|
|
85
|
+
<ThinkingBlock thinking={thinking} startedAt={startedAt} steps={steps} />;
|
|
86
|
+
<StreamingText text={answer} streaming={streaming} textStyle={{ fontFamily: "Inter" }} />;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Spinning Mark
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { SpinningMark } from "react-native-ai-animations/brand";
|
|
93
|
+
|
|
94
|
+
<SpinningMark size={160} duration={3600} breathe entrance>
|
|
95
|
+
<Image source={require("./logo.png")} style={{ width: 160, height: 160 }} />
|
|
96
|
+
</SpinningMark>;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Question Card
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { QuestionCard } from "react-native-ai-animations/chat";
|
|
103
|
+
|
|
104
|
+
<QuestionCard questions={questions} variant="sheet" onSubmit={(answers) => send(answers)} />;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`QuestionCard` and `QuestionSheet` take a `ref` with `select(index)`,
|
|
108
|
+
`goTo(page)` and `submit()`, so a demo, a voice flow or a test can drive the
|
|
109
|
+
questions without touches.
|
|
110
|
+
|
|
111
|
+
### Effort Bar
|
|
112
|
+
|
|
113
|
+
`REASONING_EFFORTS` matches the `reasoning.effort` values OpenRouter and most
|
|
114
|
+
reasoning APIs take (`low`, `medium`, `high`), with each level's share of
|
|
115
|
+
`max_tokens`. `reasoningRequest(effort)` builds the request fields.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { EffortBar, REASONING_EFFORTS, reasoningRequest } from "react-native-ai-animations/chat";
|
|
119
|
+
|
|
120
|
+
<EffortBar levels={REASONING_EFFORTS} value={index} onChange={(i, level) => setEffort(level.value)} accessibilityLabel="Reasoning" />;
|
|
121
|
+
<EffortBar levels={REASONING_EFFORTS} value={index} onChange={(i) => setIndex(i)} variant="segmented" />;
|
|
122
|
+
<EffortBar levels={REASONING_EFFORTS} value={index} onChange={(i) => setIndex(i)} variant="slider" tabLabel={model.name} fillColors={["#5AA2FF", "#2F7BF5"]} levelIcon={(i, color) => <BrainIcon level={i} color={color} />} />;
|
|
123
|
+
<EffortBar levels={REASONING_EFFORTS} value={index} variant="slider" size="compact" />;
|
|
124
|
+
|
|
125
|
+
const body = { model, messages, max_tokens: 16000, ...reasoningRequest(effort) };
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
| Prop | Does |
|
|
129
|
+
| --- | --- |
|
|
130
|
+
| `variant` | `bars` (default), `track`, `segmented` or `slider` |
|
|
131
|
+
| `size="compact"` | Small inline form for model rows |
|
|
132
|
+
| `fillColors` | Tints the fill of any style |
|
|
133
|
+
| `tabLabel` | Your model name in the slider's tab |
|
|
134
|
+
| `levelIcon` | Replaces every level name with your own icon |
|
|
135
|
+
|
|
136
|
+
EffortBar is only the control: put your own title, level label or description
|
|
137
|
+
around it.
|
|
138
|
+
|
|
139
|
+
### Media Generating
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { ImageGenerating, MediaGenerating, VideoGenerating } from "react-native-ai-animations/skia";
|
|
143
|
+
|
|
144
|
+
<MediaGenerating variant="constellation" label="Creating image" startedAt={startedAt} />;
|
|
145
|
+
<MediaGenerating variant="scan" chrome="overlay" status="generating" progress={0.42} meta="720p · 6s" aspectRatio={16 / 9} />;
|
|
146
|
+
<ImageGenerating variant="helix" startedAt={startedAt} />;
|
|
147
|
+
<VideoGenerating status="queued" variant="orbit" />;
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`ImageGenerating` and `VideoGenerating` are presets of `MediaGenerating`.
|
|
151
|
+
|
|
152
|
+
### Voice Wave
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { useSharedValue } from "react-native-reanimated";
|
|
156
|
+
import { VoiceWave } from "react-native-ai-animations/skia";
|
|
157
|
+
|
|
158
|
+
const level = useSharedValue(0);
|
|
159
|
+
|
|
160
|
+
<VoiceWave level={level} variant="bars" colors={["#1F8A70", "#88C9F7", "#5FC7B0"]} />;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Liquid Orb and Spoken Text
|
|
164
|
+
|
|
165
|
+
`LiquidOrb` comes in `metal`, `water` or `iridescent`, or three glowing loops
|
|
166
|
+
with `ring` (color them with `colors`). `state` switches between `idle`,
|
|
167
|
+
`listening`, `thinking` and `speaking`, and `level` (0 to 1) makes it swell
|
|
168
|
+
while listening or speaking. `SpokenText` highlights the word being read aloud:
|
|
169
|
+
pass the index of the current word as `spoken`.
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { useSharedValue } from "react-native-reanimated";
|
|
173
|
+
import { LiquidOrb } from "react-native-ai-animations/skia";
|
|
174
|
+
import { SpokenText } from "react-native-ai-animations/text";
|
|
175
|
+
|
|
176
|
+
const level = useSharedValue(0);
|
|
177
|
+
|
|
178
|
+
<LiquidOrb material="water" state={isRecording ? "listening" : "idle"} level={level} size={240} />;
|
|
179
|
+
<LiquidOrb material="ring" state="listening" level={level} size={240} colors={["#1F8A70", "#88C9F7", "#5FC7B0"]} />;
|
|
180
|
+
<SpokenText text={reply} spoken={wordIndex} textStyle={{ fontFamily: "Inter", fontSize: 20, lineHeight: 28 }} />;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Theming
|
|
184
|
+
|
|
185
|
+
Components use the built-in Moodstone palette until you wrap your app in
|
|
186
|
+
`MotionThemeProvider`. Pass one palette, or a `light` and `dark` pair with a
|
|
187
|
+
`colorScheme`. Components that read the palette also take `colors` to override
|
|
188
|
+
a single instance; Shimmer Text, Voice Wave, Dot Orb and Matrix Orb take their
|
|
189
|
+
colors directly (`activeColor`, `colors`, `color`).
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
import { useColorScheme } from "react-native";
|
|
193
|
+
import { MotionThemeProvider } from "react-native-ai-animations/theme";
|
|
194
|
+
|
|
195
|
+
<MotionThemeProvider
|
|
196
|
+
colorScheme={useColorScheme() ?? "light"}
|
|
197
|
+
palette={{ light: { accent: "#2F7BF5" }, dark: { accent: "#5AA2FF" } }}
|
|
198
|
+
>
|
|
199
|
+
<App />
|
|
200
|
+
</MotionThemeProvider>;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Palette keys: `background`, `card`, `surface`, `raised`, `text`,
|
|
204
|
+
`textSecondary`, `textTertiary`, `accent`, `accentContrast`, `success`,
|
|
205
|
+
`danger`. Read the active palette in your own components with
|
|
206
|
+
`useMotionPalette()`.
|
|
207
|
+
|
|
208
|
+
## Customization and accessibility
|
|
209
|
+
|
|
210
|
+
- Icons, fonts, colors and timing are props on every component; nothing is
|
|
211
|
+
tied to a brand or an icon set.
|
|
212
|
+
- When the system's reduce motion setting is on, every component settles to a
|
|
213
|
+
static final state.
|
|
214
|
+
|
|
215
|
+
## Development
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
npm install
|
|
219
|
+
npm run typecheck
|
|
220
|
+
npm run build
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Changes that affect the package need a changeset: `npm run changeset`.
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["SpinningMark"],"sourceRoot":"../../../src","sources":["brand/index.ts"],"mappings":";;AAAA,SACEA,YAAY,QAGP,oBAAiB","ignoreList":[]}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import Animated, { cancelAnimation, Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withRepeat, withSequence, withSpring, withTiming } from "react-native-reanimated";
|
|
5
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
|
+
const DEFAULT_DURATION = 3630;
|
|
7
|
+
const DEFAULT_BREATHE = {
|
|
8
|
+
scale: 1.06,
|
|
9
|
+
duration: 1800
|
|
10
|
+
};
|
|
11
|
+
const ENTRANCE_SPRING = {
|
|
12
|
+
damping: 14,
|
|
13
|
+
stiffness: 120,
|
|
14
|
+
mass: 1
|
|
15
|
+
};
|
|
16
|
+
export function SpinningMark({
|
|
17
|
+
children,
|
|
18
|
+
size,
|
|
19
|
+
duration = DEFAULT_DURATION,
|
|
20
|
+
direction = "clockwise",
|
|
21
|
+
easing = Easing.linear,
|
|
22
|
+
breathe = false,
|
|
23
|
+
entrance = false,
|
|
24
|
+
paused = false,
|
|
25
|
+
style,
|
|
26
|
+
accessibilityLabel
|
|
27
|
+
}) {
|
|
28
|
+
const reduceMotion = useReducedMotion();
|
|
29
|
+
const angle = useSharedValue(0);
|
|
30
|
+
const pulse = useSharedValue(1);
|
|
31
|
+
const intro = useSharedValue(entrance && !reduceMotion ? 0 : 1);
|
|
32
|
+
const sign = direction === "clockwise" ? 1 : -1;
|
|
33
|
+
const breatheScale = breathe ? (breathe === true ? undefined : breathe.scale) ?? DEFAULT_BREATHE.scale : 1;
|
|
34
|
+
const breatheDuration = breathe ? (breathe === true ? undefined : breathe.duration) ?? DEFAULT_BREATHE.duration : 0;
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (reduceMotion || paused) {
|
|
37
|
+
cancelAnimation(angle);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const start = angle.value % 360;
|
|
41
|
+
angle.value = start;
|
|
42
|
+
angle.value = withRepeat(withTiming(start + 360, {
|
|
43
|
+
duration,
|
|
44
|
+
easing
|
|
45
|
+
}), -1, false);
|
|
46
|
+
return () => cancelAnimation(angle);
|
|
47
|
+
}, [angle, duration, easing, paused, reduceMotion]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (reduceMotion || paused || breatheScale === 1) {
|
|
50
|
+
cancelAnimation(pulse);
|
|
51
|
+
pulse.value = withTiming(1, {
|
|
52
|
+
duration: 200
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const half = breatheDuration / 2;
|
|
57
|
+
const curve = Easing.inOut(Easing.sin);
|
|
58
|
+
pulse.value = withRepeat(withSequence(withTiming(breatheScale, {
|
|
59
|
+
duration: half,
|
|
60
|
+
easing: curve
|
|
61
|
+
}), withTiming(1, {
|
|
62
|
+
duration: half,
|
|
63
|
+
easing: curve
|
|
64
|
+
})), -1, false);
|
|
65
|
+
return () => cancelAnimation(pulse);
|
|
66
|
+
}, [breatheDuration, breatheScale, paused, pulse, reduceMotion]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!entrance || reduceMotion) {
|
|
69
|
+
intro.value = 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
intro.value = withSpring(1, ENTRANCE_SPRING);
|
|
73
|
+
}, [entrance, intro, reduceMotion]);
|
|
74
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
75
|
+
opacity: Math.min(1, intro.value * 1.4),
|
|
76
|
+
transform: [{
|
|
77
|
+
rotate: `${sign * angle.value - (1 - intro.value) * 90 * sign}deg`
|
|
78
|
+
}, {
|
|
79
|
+
scale: pulse.value * (0.6 + intro.value * 0.4)
|
|
80
|
+
}]
|
|
81
|
+
}));
|
|
82
|
+
return /*#__PURE__*/_jsx(Animated.View, {
|
|
83
|
+
accessible: !!accessibilityLabel,
|
|
84
|
+
accessibilityRole: accessibilityLabel ? "image" : undefined,
|
|
85
|
+
accessibilityLabel: accessibilityLabel,
|
|
86
|
+
style: [size !== undefined && {
|
|
87
|
+
width: size,
|
|
88
|
+
height: size
|
|
89
|
+
}, {
|
|
90
|
+
alignItems: "center",
|
|
91
|
+
justifyContent: "center"
|
|
92
|
+
}, style, animatedStyle],
|
|
93
|
+
children: children
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=spinning-mark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useEffect","Animated","cancelAnimation","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withRepeat","withSequence","withSpring","withTiming","jsx","_jsx","DEFAULT_DURATION","DEFAULT_BREATHE","scale","duration","ENTRANCE_SPRING","damping","stiffness","mass","SpinningMark","children","size","direction","easing","linear","breathe","entrance","paused","style","accessibilityLabel","reduceMotion","angle","pulse","intro","sign","breatheScale","undefined","breatheDuration","start","value","half","curve","inOut","sin","animatedStyle","opacity","Math","min","transform","rotate","View","accessible","accessibilityRole","width","height","alignItems","justifyContent"],"sourceRoot":"../../../src","sources":["brand/spinning-mark.tsx"],"mappings":"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","ignoreList":[]}
|