@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
+ [![npm version](https://img.shields.io/npm/v/@grandgular/rive-angular.svg)](https://www.npmjs.com/package/@grandgular/rive-angular)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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).