@grandgular/rive-angular 0.1.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/README.md
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# @grandgular/rive-angular
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@grandgular/rive-angular)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Modern Angular wrapper for [Rive](https://rive.app) animations with reactive state management, built with Angular signals and zoneless architecture.
|
|
7
|
+
|
|
8
|
+
## What is Rive?
|
|
9
|
+
|
|
10
|
+
[Rive](https://rive.app) is a real-time interactive design and animation tool. It allows designers and developers to create animations that respond to different states and user inputs. Rive animations are lightweight, interactive, and can be used in apps, games, and websites.
|
|
11
|
+
|
|
12
|
+
## Why @grandgular/rive-angular?
|
|
13
|
+
|
|
14
|
+
This library provides a **modern, Angular-native** way to integrate Rive animations into your Angular applications:
|
|
15
|
+
|
|
16
|
+
- ๐ **Modern Angular**: Built with Angular 18+ signals, standalone components, and zoneless architecture
|
|
17
|
+
- โก **Performance-first**: Runs outside Angular zone, uses OnPush change detection, and IntersectionObserver for automatic rendering optimization
|
|
18
|
+
- ๐ฏ **Type-safe**: Full TypeScript support with strict typing
|
|
19
|
+
- ๐ **Reactive**: Signal-based API for reactive state management
|
|
20
|
+
- ๐ **SSR-ready**: Full server-side rendering support
|
|
21
|
+
- ๐งน **Automatic cleanup**: Proper resource management and lifecycle handling
|
|
22
|
+
- ๐ฆ **File caching**: Built-in service for preloading and caching .riv files
|
|
23
|
+
|
|
24
|
+
### Comparison with alternatives
|
|
25
|
+
|
|
26
|
+
#### vs. ng-rive (unmaintained)
|
|
27
|
+
|
|
28
|
+
[ng-rive](https://www.npmjs.com/package/ng-rive) was the previous Angular wrapper for Rive, but it has been **unmaintained since 2021** and is incompatible with modern Angular versions:
|
|
29
|
+
|
|
30
|
+
| Feature | @grandgular/rive-angular | ng-rive |
|
|
31
|
+
|---------|--------------------------|---------|
|
|
32
|
+
| Angular version | 18+ (modern) | 9-12 (legacy) |
|
|
33
|
+
| Architecture | Signals, standalone | Modules, Zone.js |
|
|
34
|
+
| Maintenance | โ
Active | โ Abandoned (3+ years) |
|
|
35
|
+
| TypeScript | Strict typing | Partial |
|
|
36
|
+
| SSR support | โ
Full | โ ๏ธ Limited |
|
|
37
|
+
| Performance | Optimized (zoneless) | Standard |
|
|
38
|
+
| File caching | โ
Built-in service | โ Manual |
|
|
39
|
+
|
|
40
|
+
#### vs. rive-react
|
|
41
|
+
|
|
42
|
+
This library follows the design principles of the official [rive-react](https://github.com/rive-app/rive-react) library but adapts them to Angular's reactive paradigm:
|
|
43
|
+
|
|
44
|
+
| Aspect | @grandgular/rive-angular | rive-react |
|
|
45
|
+
|--------|--------------------------|------------|
|
|
46
|
+
| Component API | `<rive-canvas>` | `<Rive>` component |
|
|
47
|
+
| Reactivity | Signals | Hooks (useState, useEffect) |
|
|
48
|
+
| File preloading | `RiveFileService` | `useRiveFile` hook |
|
|
49
|
+
| State access | Public signals | Hook return values |
|
|
50
|
+
| Lifecycle | DestroyRef | useEffect cleanup |
|
|
51
|
+
|
|
52
|
+
Both libraries provide similar features and follow the same philosophy of providing a thin, reactive wrapper around the core Rive runtime.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm install @grandgular/rive-angular @rive-app/canvas
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or with yarn:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
yarn add @grandgular/rive-angular @rive-app/canvas
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
### Basic usage
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { Component } from '@angular/core';
|
|
72
|
+
import { RiveCanvasComponent, Fit, Alignment } from '@grandgular/rive-angular';
|
|
73
|
+
|
|
74
|
+
@Component({
|
|
75
|
+
selector: 'app-root',
|
|
76
|
+
standalone: true,
|
|
77
|
+
imports: [RiveCanvasComponent],
|
|
78
|
+
template: `
|
|
79
|
+
<rive-canvas
|
|
80
|
+
src="assets/animation.riv"
|
|
81
|
+
[autoplay]="true"
|
|
82
|
+
[fit]="Fit.Cover"
|
|
83
|
+
[alignment]="Alignment.Center"
|
|
84
|
+
(loaded)="onLoaded()"
|
|
85
|
+
(loadError)="onError($event)"
|
|
86
|
+
/>
|
|
87
|
+
`,
|
|
88
|
+
styles: [`
|
|
89
|
+
rive-canvas {
|
|
90
|
+
width: 100%;
|
|
91
|
+
height: 400px;
|
|
92
|
+
}
|
|
93
|
+
`]
|
|
94
|
+
})
|
|
95
|
+
export class AppComponent {
|
|
96
|
+
Fit = Fit;
|
|
97
|
+
Alignment = Alignment;
|
|
98
|
+
|
|
99
|
+
onLoaded() {
|
|
100
|
+
console.log('Animation loaded!');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
onError(error: Error) {
|
|
104
|
+
console.error('Failed to load animation:', error);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### With state machines
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { Component, viewChild } from '@angular/core';
|
|
113
|
+
import { RiveCanvasComponent } from '@grandgular/rive-angular';
|
|
114
|
+
|
|
115
|
+
@Component({
|
|
116
|
+
selector: 'app-interactive',
|
|
117
|
+
standalone: true,
|
|
118
|
+
imports: [RiveCanvasComponent],
|
|
119
|
+
template: `
|
|
120
|
+
<rive-canvas
|
|
121
|
+
src="assets/interactive.riv"
|
|
122
|
+
[stateMachines]="'StateMachine'"
|
|
123
|
+
(loaded)="onLoaded()"
|
|
124
|
+
/>
|
|
125
|
+
<button (click)="triggerAction()">Trigger</button>
|
|
126
|
+
`
|
|
127
|
+
})
|
|
128
|
+
export class InteractiveComponent {
|
|
129
|
+
riveCanvas = viewChild.required(RiveCanvasComponent);
|
|
130
|
+
|
|
131
|
+
onLoaded() {
|
|
132
|
+
// Set initial state
|
|
133
|
+
this.riveCanvas().setInput('StateMachine', 'isActive', true);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
triggerAction() {
|
|
137
|
+
// Fire a trigger
|
|
138
|
+
this.riveCanvas().fireTrigger('StateMachine', 'onClick');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Preloading files with RiveFileService
|
|
144
|
+
|
|
145
|
+
For better performance, you can preload and cache .riv files:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { Component, inject, DestroyRef } from '@angular/core';
|
|
149
|
+
import { RiveCanvasComponent, RiveFileService } from '@grandgular/rive-angular';
|
|
150
|
+
|
|
151
|
+
@Component({
|
|
152
|
+
selector: 'app-preload',
|
|
153
|
+
standalone: true,
|
|
154
|
+
imports: [RiveCanvasComponent],
|
|
155
|
+
template: `
|
|
156
|
+
@if (fileState().status === 'success') {
|
|
157
|
+
<rive-canvas
|
|
158
|
+
[riveFile]="fileState().riveFile"
|
|
159
|
+
[autoplay]="true"
|
|
160
|
+
/>
|
|
161
|
+
}
|
|
162
|
+
@if (fileState().status === 'loading') {
|
|
163
|
+
<p>Loading animation...</p>
|
|
164
|
+
}
|
|
165
|
+
@if (fileState().status === 'failed') {
|
|
166
|
+
<p>Failed to load animation</p>
|
|
167
|
+
}
|
|
168
|
+
`
|
|
169
|
+
})
|
|
170
|
+
export class PreloadComponent {
|
|
171
|
+
private riveFileService = inject(RiveFileService);
|
|
172
|
+
private destroyRef = inject(DestroyRef);
|
|
173
|
+
|
|
174
|
+
// Load and cache the file
|
|
175
|
+
fileState = this.riveFileService.loadFile({
|
|
176
|
+
src: 'assets/animation.riv'
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
constructor() {
|
|
180
|
+
// Auto-release on component destroy
|
|
181
|
+
this.destroyRef.onDestroy(() => {
|
|
182
|
+
this.riveFileService.releaseFile({ src: 'assets/animation.riv' });
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## API Reference
|
|
189
|
+
|
|
190
|
+
### RiveCanvasComponent
|
|
191
|
+
|
|
192
|
+
#### Inputs
|
|
193
|
+
|
|
194
|
+
| Input | Type | Default | Description |
|
|
195
|
+
|-------|------|---------|-------------|
|
|
196
|
+
| `src` | `string` | - | URL to the .riv file |
|
|
197
|
+
| `buffer` | `ArrayBuffer` | - | ArrayBuffer containing .riv file data |
|
|
198
|
+
| `riveFile` | `RiveFile` | - | Preloaded RiveFile instance (from RiveFileService) |
|
|
199
|
+
| `artboard` | `string` | - | Name of the artboard to display |
|
|
200
|
+
| `animations` | `string \| string[]` | - | Animation(s) to play |
|
|
201
|
+
| `stateMachines` | `string \| string[]` | - | State machine(s) to use |
|
|
202
|
+
| `autoplay` | `boolean` | `true` | Auto-play animations on load |
|
|
203
|
+
| `fit` | `Fit` | `Fit.Contain` | How the animation fits in the canvas |
|
|
204
|
+
| `alignment` | `Alignment` | `Alignment.Center` | Alignment of the animation |
|
|
205
|
+
| `useOffscreenRenderer` | `boolean` | `false` | Use offscreen rendering |
|
|
206
|
+
| `shouldUseIntersectionObserver` | `boolean` | `true` | Auto-pause when off-screen |
|
|
207
|
+
| `shouldDisableRiveListeners` | `boolean` | `false` | Disable Rive event listeners |
|
|
208
|
+
| `automaticallyHandleEvents` | `boolean` | `false` | Auto-handle Rive events (e.g., OpenUrlEvent) |
|
|
209
|
+
|
|
210
|
+
#### Outputs
|
|
211
|
+
|
|
212
|
+
| Output | Type | Description |
|
|
213
|
+
|--------|------|-------------|
|
|
214
|
+
| `loaded` | `void` | Emitted when animation loads successfully |
|
|
215
|
+
| `loadError` | `Error` | Emitted when animation fails to load |
|
|
216
|
+
| `stateChange` | `RiveEvent` | Emitted on state machine state changes |
|
|
217
|
+
| `riveEvent` | `RiveEvent` | Emitted for custom Rive events |
|
|
218
|
+
| `riveReady` | `Rive` | Emitted when Rive instance is created |
|
|
219
|
+
|
|
220
|
+
#### Public Signals
|
|
221
|
+
|
|
222
|
+
| Signal | Type | Description |
|
|
223
|
+
|--------|------|-------------|
|
|
224
|
+
| `isPlaying` | `Signal<boolean>` | Whether animation is playing |
|
|
225
|
+
| `isPaused` | `Signal<boolean>` | Whether animation is paused |
|
|
226
|
+
| `isLoaded` | `Signal<boolean>` | Whether animation is loaded |
|
|
227
|
+
| `riveInstance` | `Signal<Rive \| null>` | Direct access to Rive instance |
|
|
228
|
+
|
|
229
|
+
#### Public Methods
|
|
230
|
+
|
|
231
|
+
| Method | Description |
|
|
232
|
+
|--------|-------------|
|
|
233
|
+
| `playAnimation(animations?: string \| string[])` | Play animation(s) |
|
|
234
|
+
| `pauseAnimation(animations?: string \| string[])` | Pause animation(s) |
|
|
235
|
+
| `stopAnimation(animations?: string \| string[])` | Stop animation(s) |
|
|
236
|
+
| `reset()` | Reset animation to beginning |
|
|
237
|
+
| `setInput(stateMachine: string, input: string, value: number \| boolean)` | Set state machine input value |
|
|
238
|
+
| `fireTrigger(stateMachine: string, trigger: string)` | Fire state machine trigger |
|
|
239
|
+
|
|
240
|
+
### RiveFileService
|
|
241
|
+
|
|
242
|
+
Service for preloading and caching .riv files.
|
|
243
|
+
|
|
244
|
+
#### Methods
|
|
245
|
+
|
|
246
|
+
| Method | Description |
|
|
247
|
+
|--------|-------------|
|
|
248
|
+
| `loadFile(params: RiveFileParams): Signal<RiveFileState>` | Load and cache a .riv file |
|
|
249
|
+
| `releaseFile(params: RiveFileParams): void` | Release cached file (decrements ref count) |
|
|
250
|
+
| `clearCache(): void` | Clear all cached files |
|
|
251
|
+
|
|
252
|
+
#### Types
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
interface RiveFileParams {
|
|
256
|
+
src?: string;
|
|
257
|
+
buffer?: ArrayBuffer;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
interface RiveFileState {
|
|
261
|
+
riveFile: RiveFile | null;
|
|
262
|
+
status: 'idle' | 'loading' | 'success' | 'failed';
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## SSR Support
|
|
267
|
+
|
|
268
|
+
The library is fully compatible with Angular Universal and server-side rendering:
|
|
269
|
+
|
|
270
|
+
- Canvas rendering is automatically disabled on the server
|
|
271
|
+
- IntersectionObserver and ResizeObserver use safe fallbacks
|
|
272
|
+
- No runtime errors in SSR environments
|
|
273
|
+
|
|
274
|
+
## Performance Tips
|
|
275
|
+
|
|
276
|
+
1. **Use IntersectionObserver**: Keep `shouldUseIntersectionObserver` enabled (default) to automatically pause animations when off-screen
|
|
277
|
+
2. **Preload files**: Use `RiveFileService` to preload and cache .riv files for instant display
|
|
278
|
+
3. **Disable unnecessary listeners**: Set `shouldDisableRiveListeners` to `true` for decorative animations without interactivity
|
|
279
|
+
4. **Use OnPush**: The component already uses `OnPush` change detection for optimal performance
|
|
280
|
+
|
|
281
|
+
## Troubleshooting
|
|
282
|
+
|
|
283
|
+
### Animation not loading
|
|
284
|
+
|
|
285
|
+
- Verify the .riv file path is correct
|
|
286
|
+
- Check browser console for errors
|
|
287
|
+
- Ensure `@rive-app/canvas` is installed
|
|
288
|
+
|
|
289
|
+
### State machine inputs not working
|
|
290
|
+
|
|
291
|
+
- Verify state machine and input names match your .riv file
|
|
292
|
+
- Check that the animation has loaded before calling `setInput` or `fireTrigger`
|
|
293
|
+
- Use the `loaded` output to ensure timing
|
|
294
|
+
|
|
295
|
+
### Memory leaks with RiveFileService
|
|
296
|
+
|
|
297
|
+
- Always call `releaseFile` when done with a file
|
|
298
|
+
- Use `DestroyRef.onDestroy` to auto-release files on component destroy
|
|
299
|
+
- The service uses reference counting - files are only cleaned up when ref count reaches 0
|
|
300
|
+
|
|
301
|
+
## Requirements
|
|
302
|
+
|
|
303
|
+
- Angular 18.0.0 or higher
|
|
304
|
+
- @rive-app/canvas 2.35.0 or higher
|
|
305
|
+
- TypeScript 5.4 or higher
|
|
306
|
+
|
|
307
|
+
## Contributing
|
|
308
|
+
|
|
309
|
+
Contributions are welcome! Please feel free to submit issues or pull requests.
|
|
310
|
+
|
|
311
|
+
## License
|
|
312
|
+
|
|
313
|
+
MIT
|
|
314
|
+
|
|
315
|
+
## Resources
|
|
316
|
+
|
|
317
|
+
- [Rive Homepage](https://rive.app)
|
|
318
|
+
- [Rive Documentation](https://help.rive.app)
|
|
319
|
+
- [Rive Community](https://rive.app/community)
|
|
320
|
+
- [Angular Documentation](https://angular.dev)
|
|
321
|
+
|
|
322
|
+
## Maintainer
|
|
323
|
+
|
|
324
|
+
This library is maintained by the community and is not officially supported by Rive. For official Rive support, please visit the [Rive Community](https://rive.app/community).
|