mirror of
https://github.com/rive-app/rive-react.git
synced 2026-03-13 08:22:30 +08:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4fcef529e | ||
|
|
f51fe6ee2a | ||
|
|
0f558e66e1 | ||
|
|
7a67fb14f7 | ||
|
|
da3fc317ff | ||
|
|
9b40a1a02e | ||
|
|
aa92c01329 | ||
|
|
99a8a42a15 | ||
|
|
f0ea7add89 | ||
|
|
ac9f322ccb | ||
|
|
e1c2d108e9 | ||
|
|
3aaee0bcf8 | ||
|
|
e3739f1a1f | ||
|
|
0e0a2bd972 | ||
|
|
5c47a411f2 | ||
|
|
16dc257b2f | ||
|
|
4678ea9ecf | ||
|
|
d58963e29d | ||
|
|
c32433284a | ||
|
|
8bb5652df0 | ||
|
|
9ecacf37fe | ||
|
|
f837fbe0d4 | ||
|
|
8e181d6ae2 | ||
|
|
a3a41dca40 | ||
|
|
b78c9715d7 | ||
|
|
520e5bb51d | ||
|
|
586ee4c002 | ||
|
|
3d6c7ed499 | ||
|
|
c3900b7845 | ||
|
|
d369817113 | ||
|
|
d68302ccb3 | ||
|
|
bbd4cc7af6 | ||
|
|
3936277f65 | ||
|
|
aa2a783d1c | ||
|
|
d8d7d64749 | ||
|
|
af3edad2c2 | ||
|
|
5326f800f7 | ||
|
|
b44f9ad9e1 | ||
|
|
b2495300b7 | ||
|
|
eb436263d7 | ||
|
|
0e6385288e | ||
|
|
78f75434fc | ||
|
|
a9c2950419 | ||
|
|
e799f64554 | ||
|
|
95a1daa4ef | ||
|
|
8a5b88c591 | ||
|
|
d3b29cf7d7 |
122
CHANGELOG.md
122
CHANGELOG.md
@@ -4,11 +4,131 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [v4.11.3](https://github.com/rive-app/rive-react/compare/v4.11.2...v4.11.3)
|
||||
|
||||
- update rive canvas to 2.17.3 [`f51fe6e`](https://github.com/rive-app/rive-react/commit/f51fe6ee2aafef72f9272e49535d4f2c89b9b44f)
|
||||
|
||||
#### [v4.11.2](https://github.com/rive-app/rive-react/compare/v4.11.1...v4.11.2)
|
||||
|
||||
> 7 June 2024
|
||||
|
||||
- chore: release 4.11.2 [`0f558e6`](https://github.com/rive-app/rive-react/commit/0f558e66e1a78aae923bd79c39b25c9725eb76bd)
|
||||
- bump rive [`7a67fb1`](https://github.com/rive-app/rive-react/commit/7a67fb14f7cb581e2fa823e9d968fc506ce2834e)
|
||||
|
||||
#### [v4.11.1](https://github.com/rive-app/rive-react/compare/v4.11.0...v4.11.1)
|
||||
|
||||
> 6 June 2024
|
||||
|
||||
- chore: release 4.11.1 [`da3fc31`](https://github.com/rive-app/rive-react/commit/da3fc317ff9cc275a30d5a42adad3d0532947f9a)
|
||||
- chore: release 4.11.0 [`aa92c01`](https://github.com/rive-app/rive-react/commit/aa92c013296bfd4c848e1c41b0c52ad48b1f6ab7)
|
||||
- update rive version [`9b40a1a`](https://github.com/rive-app/rive-react/commit/9b40a1a02e1877445b3e3dc35b96a4b7e3a54780)
|
||||
|
||||
#### [v4.11.0](https://github.com/rive-app/rive-react/compare/v4.10.0...v4.11.0)
|
||||
|
||||
> 6 June 2024
|
||||
|
||||
- chore: release 4.11.0 [`2142210`](https://github.com/rive-app/rive-react/commit/21422104c9d502887352c40a6e185c69098f438d)
|
||||
- chore: bump web to v2.17.0 [`99a8a42`](https://github.com/rive-app/rive-react/commit/99a8a42a15969e70c96cc90460ec9fcba514ed4c)
|
||||
|
||||
#### [v4.10.0](https://github.com/rive-app/rive-react/compare/v4.9.5...v4.10.0)
|
||||
|
||||
> 28 May 2024
|
||||
|
||||
- fix observer and use single instance [`c324332`](https://github.com/rive-app/rive-react/commit/c32433284ad4116170ab016139ecba7678e6c21e)
|
||||
- feat: create useRiveFile hook [`16dc257`](https://github.com/rive-app/rive-react/commit/16dc257b2f904d51101535002b9fb34640a65842)
|
||||
- change hooks lifecycle to account for component reloading [`8e181d6`](https://github.com/rive-app/rive-react/commit/8e181d6ae2dff82875c60be789dcd63bedaba883)
|
||||
|
||||
#### [v4.9.5](https://github.com/rive-app/rive-react/compare/v4.9.4...v4.9.5)
|
||||
|
||||
> 10 May 2024
|
||||
|
||||
- chore: release 4.9.5 [`a3a41dc`](https://github.com/rive-app/rive-react/commit/a3a41dca404193a900f021a2358d85d4ab44fb26)
|
||||
- bump canvas to 2.15.6 [`b78c971`](https://github.com/rive-app/rive-react/commit/b78c9715d713031d0dbdacb8c4517be5d5a411b8)
|
||||
|
||||
#### [v4.9.4](https://github.com/rive-app/rive-react/compare/v4.9.3...v4.9.4)
|
||||
|
||||
> 9 May 2024
|
||||
|
||||
- chore: release 4.9.4 [`520e5bb`](https://github.com/rive-app/rive-react/commit/520e5bb51d8c31b769149a6daee2c84ec0a1a3d6)
|
||||
- bump version to 2.15.5 [`586ee4c`](https://github.com/rive-app/rive-react/commit/586ee4c002a13251d8f13adce11bc6f9cda3a904)
|
||||
|
||||
#### [v4.9.3](https://github.com/rive-app/rive-react/compare/v4.9.2...v4.9.3)
|
||||
|
||||
> 8 May 2024
|
||||
|
||||
- chore: release 4.9.3 [`3d6c7ed`](https://github.com/rive-app/rive-react/commit/3d6c7ed4991da4b729b11e02ea2cda3d3f6b078c)
|
||||
- bump version to 2.15.4 [`c3900b7`](https://github.com/rive-app/rive-react/commit/c3900b7845a63862b7da0d64d16294dd015e876e)
|
||||
|
||||
#### [v4.9.2](https://github.com/rive-app/rive-react/compare/v4.9.1...v4.9.2)
|
||||
|
||||
> 30 April 2024
|
||||
|
||||
- chore: release 4.9.2 [`d369817`](https://github.com/rive-app/rive-react/commit/d3698171138887e4a27180b31c40750090fb0863)
|
||||
- bump rive canvas to 2.15.2 [`d68302c`](https://github.com/rive-app/rive-react/commit/d68302ccb3f5756b753f29897033da2c0aa015d5)
|
||||
|
||||
#### [v4.9.1](https://github.com/rive-app/rive-react/compare/v4.9.0...v4.9.1)
|
||||
|
||||
> 30 April 2024
|
||||
|
||||
- chore: release 4.9.1 [`bbd4cc7`](https://github.com/rive-app/rive-react/commit/bbd4cc7af651df5fb5f28313b7e7e55e294a0282)
|
||||
- update to version 2.15.1 [`3936277`](https://github.com/rive-app/rive-react/commit/3936277f658981f6928bf0ef77ea01bb60ce27c5)
|
||||
|
||||
#### [v4.9.0](https://github.com/rive-app/rive-react/compare/v4.8.10...v4.9.0)
|
||||
|
||||
> 24 April 2024
|
||||
|
||||
- chore: release 4.9.0 [`aa2a783`](https://github.com/rive-app/rive-react/commit/aa2a783d1c5e6560f59fc8299858d68464a28308)
|
||||
- chore: bump rive wasm to v2.15.0 [`d8d7d64`](https://github.com/rive-app/rive-react/commit/d8d7d64749ff019edd6d50ea98dea9000967071a)
|
||||
|
||||
#### [v4.8.10](https://github.com/rive-app/rive-react/compare/v4.8.9...v4.8.10)
|
||||
|
||||
> 23 April 2024
|
||||
|
||||
- chore: release 4.8.10 [`af3edad`](https://github.com/rive-app/rive-react/commit/af3edad2c20e820569544318bd68bb3c56b9e180)
|
||||
- bump rive library dependencies to 2.14.4 [`5326f80`](https://github.com/rive-app/rive-react/commit/5326f800f7a99eafa9c2081e22966509f89008da)
|
||||
|
||||
#### [v4.8.9](https://github.com/rive-app/rive-react/compare/v4.8.8...v4.8.9)
|
||||
|
||||
> 18 April 2024
|
||||
|
||||
- chore: release 4.8.9 [`b44f9ad`](https://github.com/rive-app/rive-react/commit/b44f9ad9e157702af5b5a946c8a476f4842392ce)
|
||||
- Bump rive js libraries to 2.14.3 [`b249530`](https://github.com/rive-app/rive-react/commit/b2495300b7a7badcdfb6ced96c2eabb8779c36f0)
|
||||
|
||||
#### [v4.8.8](https://github.com/rive-app/rive-react/compare/v4.8.7...v4.8.8)
|
||||
|
||||
> 17 April 2024
|
||||
|
||||
- chore: release 4.8.8 [`eb43626`](https://github.com/rive-app/rive-react/commit/eb436263d786ffdee20d39dea0ce16b8aec101a9)
|
||||
- bump canvas and webgl to 2.14.2 [`0e63852`](https://github.com/rive-app/rive-react/commit/0e6385288ec85c40c5e7f4bc6f15b98a02ec6b88)
|
||||
|
||||
#### [v4.8.7](https://github.com/rive-app/rive-react/compare/v4.8.6...v4.8.7)
|
||||
|
||||
> 11 April 2024
|
||||
|
||||
- chore: release 4.8.7 [`78f7543`](https://github.com/rive-app/rive-react/commit/78f75434fc179326918f6e837058a74fa4a8dbd6)
|
||||
- bump react canvas to 2.14.1 [`a9c2950`](https://github.com/rive-app/rive-react/commit/a9c29504191d929b7e96587100d375ba6787deb2)
|
||||
|
||||
#### [v4.8.6](https://github.com/rive-app/rive-react/compare/v4.8.5...v4.8.6)
|
||||
|
||||
> 9 April 2024
|
||||
|
||||
- chore: release 4.8.6 [`e799f64`](https://github.com/rive-app/rive-react/commit/e799f64554e05de8c3b0666aa7091859576d37ba)
|
||||
- Bump rive-canvas to 2.13.2 [`95a1daa`](https://github.com/rive-app/rive-react/commit/95a1daa4efbb3835e886317ad16161fe8ac843a5)
|
||||
|
||||
#### [v4.8.5](https://github.com/rive-app/rive-react/compare/v4.8.4...v4.8.5)
|
||||
|
||||
> 8 April 2024
|
||||
|
||||
- chore: release 4.8.5 [`8a5b88c`](https://github.com/rive-app/rive-react/commit/8a5b88c59132e5d04fdcce1db903397fd15b5a18)
|
||||
- bump version to 2.13.0 [`d3b29cf`](https://github.com/rive-app/rive-react/commit/d3b29cf7d7c6fe99e4e12f4579ef0876ef875b35)
|
||||
|
||||
#### [v4.8.4](https://github.com/rive-app/rive-react/compare/v4.8.3...v4.8.4)
|
||||
|
||||
> 29 March 2024
|
||||
|
||||
- feat: add new webgl2 package for new Rive Renderer [`a7875b2`](https://github.com/rive-app/rive-react/commit/a7875b26a43342f6380a5009b25b831acfcfb610)
|
||||
- chore: release 4.8.4 [`611522b`](https://github.com/rive-app/rive-react/commit/611522b3e06c30923b4aefa737310c376dab861c)
|
||||
- Update README.md [`4653b8b`](https://github.com/rive-app/rive-react/commit/4653b8bea1169408c94962e80fe3f2c34c8b221f)
|
||||
- bump version to 2.12.0 [`89d3597`](https://github.com/rive-app/rive-react/commit/89d35976d4c8c91eddeb35b9dda5e3073ef45851)
|
||||
|
||||
#### [v4.8.3](https://github.com/rive-app/rive-react/compare/v4.8.2...v4.8.3)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rive-app/react-canvas-lite",
|
||||
"version": "4.8.4",
|
||||
"version": "4.11.3",
|
||||
"description": "React wrapper around the @rive-app/canvas-lite library",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/types/index.d.ts",
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/rive-app/rive-react#readme",
|
||||
"dependencies": {
|
||||
"@rive-app/canvas-lite": "2.12.0"
|
||||
"@rive-app/canvas-lite": "2.17.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rive-app/react-canvas",
|
||||
"version": "4.8.4",
|
||||
"version": "4.11.3",
|
||||
"description": "React wrapper around the @rive-app/canvas library",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/types/index.d.ts",
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/rive-app/rive-react#readme",
|
||||
"dependencies": {
|
||||
"@rive-app/canvas": "2.12.0"
|
||||
"@rive-app/canvas": "2.17.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rive-app/react-webgl",
|
||||
"version": "4.8.4",
|
||||
"version": "4.11.3",
|
||||
"description": "React wrapper around the @rive-app/webgl library",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/types/index.d.ts",
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/rive-app/rive-react#readme",
|
||||
"dependencies": {
|
||||
"@rive-app/webgl": "2.12.0"
|
||||
"@rive-app/webgl": "2.17.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rive-app/react-webgl2",
|
||||
"version": "4.8.4",
|
||||
"version": "4.11.3",
|
||||
"description": "React wrapper around the @rive-app/webgl2 library",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/types/index.d.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rive-react",
|
||||
"version": "4.8.4",
|
||||
"version": "4.11.3",
|
||||
"description": "React wrapper around the rive-js library",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/types/index.d.ts",
|
||||
@@ -36,9 +36,9 @@
|
||||
},
|
||||
"homepage": "https://github.com/rive-app/rive-react#readme",
|
||||
"dependencies": {
|
||||
"@rive-app/canvas": "2.12.0",
|
||||
"@rive-app/canvas-lite": "2.12.0",
|
||||
"@rive-app/webgl": "2.12.0"
|
||||
"@rive-app/canvas": "2.17.3",
|
||||
"@rive-app/canvas-lite": "2.17.3",
|
||||
"@rive-app/webgl": "2.17.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
|
||||
38
src/hooks/elementObserver.ts
Normal file
38
src/hooks/elementObserver.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
class FakeIntersectionObserver {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
const MyIntersectionObserver =
|
||||
globalThis.IntersectionObserver || FakeIntersectionObserver;
|
||||
|
||||
class ElementObserver {
|
||||
private observer;
|
||||
|
||||
private elementsMap: Map<Element, Function> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.observer = new MyIntersectionObserver(this.onObserved);
|
||||
}
|
||||
public onObserved = (entries: IntersectionObserverEntry[]) => {
|
||||
entries.forEach((entry) => {
|
||||
const elementCallback = this.elementsMap.get(entry.target as Element);
|
||||
if (elementCallback) {
|
||||
elementCallback(entry);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
public registerCallback(element: Element, callback: Function) {
|
||||
this.observer.observe(element);
|
||||
this.elementsMap.set(element, callback);
|
||||
}
|
||||
|
||||
public removeCallback(element: Element) {
|
||||
this.observer.unobserve(element);
|
||||
this.elementsMap.delete(element);
|
||||
}
|
||||
}
|
||||
|
||||
export default ElementObserver;
|
||||
32
src/hooks/useIntersectionObserver.ts
Normal file
32
src/hooks/useIntersectionObserver.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { useCallback } from 'react';
|
||||
import ElementObserver from './elementObserver';
|
||||
|
||||
let observer: ElementObserver;
|
||||
const getObserver = () => {
|
||||
if(!observer) {
|
||||
observer = new ElementObserver();
|
||||
}
|
||||
return observer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to observe elements when they are intersecting with the viewport
|
||||
*
|
||||
* @returns - API to observer and unobserve elements
|
||||
*/
|
||||
export default function useIntersectionObserver() {
|
||||
const observe = useCallback((element: Element, callback: Function) => {
|
||||
const observer = getObserver();
|
||||
observer.registerCallback(element, callback);
|
||||
}, []);
|
||||
|
||||
const unobserve = useCallback((element: Element) => {
|
||||
const observer = getObserver();
|
||||
observer.removeCallback(element);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
observe,
|
||||
unobserve,
|
||||
};
|
||||
}
|
||||
@@ -13,7 +13,7 @@ interface UseResizeCanvasProps {
|
||||
/**
|
||||
* Ref to the canvas element
|
||||
*/
|
||||
canvasRef: MutableRefObject<HTMLCanvasElement | null>;
|
||||
canvasElem: HTMLCanvasElement | null;
|
||||
/**
|
||||
* Ref to the container element of the canvas
|
||||
*/
|
||||
@@ -55,7 +55,7 @@ interface UseResizeCanvasProps {
|
||||
*/
|
||||
export default function useResizeCanvas({
|
||||
riveLoaded = false,
|
||||
canvasRef,
|
||||
canvasElem,
|
||||
containerRef,
|
||||
options = {},
|
||||
onCanvasHasResized,
|
||||
@@ -120,7 +120,7 @@ export default function useResizeCanvas({
|
||||
|
||||
const { width, height } = getContainerDimensions();
|
||||
let hasResized = false;
|
||||
if (canvasRef.current) {
|
||||
if (canvasElem) {
|
||||
// Check if the canvas parent container bounds have changed and set
|
||||
// new values accordingly
|
||||
const boundsChanged =
|
||||
@@ -138,10 +138,10 @@ export default function useResizeCanvas({
|
||||
if (boundsChanged || canvasSizeChanged) {
|
||||
const newCanvasWidthProp = currentDevicePixelRatio * width;
|
||||
const newCanvasHeightProp = currentDevicePixelRatio * height;
|
||||
canvasRef.current.width = newCanvasWidthProp;
|
||||
canvasRef.current.height = newCanvasHeightProp;
|
||||
canvasRef.current.style.width = width + 'px';
|
||||
canvasRef.current.style.height = height + 'px';
|
||||
canvasElem.width = newCanvasWidthProp;
|
||||
canvasElem.height = newCanvasHeightProp;
|
||||
canvasElem.style.width = width + 'px';
|
||||
canvasElem.style.height = height + 'px';
|
||||
setLastCanvasSize({
|
||||
width: newCanvasWidthProp,
|
||||
height: newCanvasHeightProp,
|
||||
@@ -149,8 +149,8 @@ export default function useResizeCanvas({
|
||||
hasResized = true;
|
||||
}
|
||||
} else if (boundsChanged) {
|
||||
canvasRef.current.width = width;
|
||||
canvasRef.current.height = height;
|
||||
canvasElem.width = width;
|
||||
canvasElem.height = height;
|
||||
setLastCanvasSize({
|
||||
width: width,
|
||||
height: height,
|
||||
@@ -167,7 +167,7 @@ export default function useResizeCanvas({
|
||||
}
|
||||
isFirstSizing && setIsFirstSizing(false);
|
||||
}, [
|
||||
canvasRef,
|
||||
canvasElem,
|
||||
containerRef,
|
||||
containerSize,
|
||||
currentDevicePixelRatio,
|
||||
@@ -184,4 +184,12 @@ export default function useResizeCanvas({
|
||||
shouldUseDevicePixelRatio,
|
||||
riveLoaded,
|
||||
]);
|
||||
|
||||
// Reset width and height values when the canvas changes
|
||||
useEffect(() => {
|
||||
setLastCanvasSize({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
}, [canvasElem]);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Rive, EventType } from '@rive-app/canvas';
|
||||
import { UseRiveParameters, UseRiveOptions, RiveState } from '../types';
|
||||
import useResizeCanvas from './useResizeCanvas';
|
||||
import { getOptions } from '../utils';
|
||||
import useIntersectionObserver from './useIntersectionObserver';
|
||||
|
||||
type RiveComponentProps = {
|
||||
setContainerRef: RefCallback<HTMLElement>;
|
||||
@@ -65,7 +66,7 @@ export default function useRive(
|
||||
riveParams?: UseRiveParameters,
|
||||
opts: Partial<UseRiveOptions> = {}
|
||||
): RiveState {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const [canvasElem, setCanvasElem] = useState<HTMLCanvasElement | null>(null);
|
||||
const containerRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
const [rive, setRive] = useState<Rive | null>(null);
|
||||
@@ -87,7 +88,7 @@ export default function useRive(
|
||||
// Watch the canvas parent container resize and size the canvas to match
|
||||
useResizeCanvas({
|
||||
riveLoaded: !!rive,
|
||||
canvasRef,
|
||||
canvasElem,
|
||||
containerRef,
|
||||
options,
|
||||
onCanvasHasResized,
|
||||
@@ -99,32 +100,39 @@ export default function useRive(
|
||||
*/
|
||||
const setCanvasRef: RefCallback<HTMLCanvasElement> = useCallback(
|
||||
(canvas: HTMLCanvasElement | null) => {
|
||||
if (canvas && riveParams && isParamsLoaded) {
|
||||
const { useOffscreenRenderer } = options;
|
||||
const r = new Rive({
|
||||
useOffscreenRenderer,
|
||||
...riveParams,
|
||||
canvas,
|
||||
});
|
||||
r.on(EventType.Load, () => {
|
||||
// Check if the component/canvas is mounted before setting state to avoid setState
|
||||
// on an unmounted component in some rare cases
|
||||
if (canvasRef.current) {
|
||||
setRive(r);
|
||||
} else {
|
||||
// If unmounted, cleanup the rive object immediately
|
||||
r.cleanup();
|
||||
}
|
||||
});
|
||||
} else if (canvas === null && canvasRef.current) {
|
||||
canvasRef.current.height = 0;
|
||||
canvasRef.current.width = 0;
|
||||
if (canvas === null && canvasElem) {
|
||||
canvasElem.height = 0;
|
||||
canvasElem.width = 0;
|
||||
}
|
||||
|
||||
canvasRef.current = canvas;
|
||||
setCanvasElem(canvas);
|
||||
},
|
||||
[isParamsLoaded]
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvasElem || !riveParams) {
|
||||
return;
|
||||
}
|
||||
if (rive == null) {
|
||||
const { useOffscreenRenderer } = options;
|
||||
const r = new Rive({
|
||||
useOffscreenRenderer,
|
||||
...riveParams,
|
||||
canvas: canvasElem,
|
||||
});
|
||||
r.on(EventType.Load, () => {
|
||||
// Check if the component/canvas is mounted before setting state to avoid setState
|
||||
// on an unmounted component in some rare cases
|
||||
if (canvasElem) {
|
||||
setRive(r);
|
||||
} else {
|
||||
// If unmounted, cleanup the rive object immediately
|
||||
r.cleanup();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [canvasElem, isParamsLoaded, rive]);
|
||||
/**
|
||||
* Ref callback called when the container element mounts
|
||||
*/
|
||||
@@ -139,21 +147,62 @@ export default function useRive(
|
||||
* Set up IntersectionObserver to stop rendering if the animation is not in
|
||||
* view.
|
||||
*/
|
||||
const { observe, unobserve } = useIntersectionObserver();
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(([entry]) => {
|
||||
let timeoutId: ReturnType<typeof setTimeout>;
|
||||
let isPaused = false;
|
||||
// This is a workaround to retest whether an element is offscreen or not.
|
||||
// There seems to be a bug in Chrome that triggers an intersection change when an element
|
||||
// is moved within the DOM using insertBefore.
|
||||
// For some reason, when this is called whithin the context of a React application, the
|
||||
// intersection callback is called only once reporting isIntersecting as false but never
|
||||
// triggered back with isIntersecting as true.
|
||||
// For this reason we retest after 10 millisecond whether the element is actually off the
|
||||
// viewport or not.
|
||||
const retestIntersection = () => {
|
||||
if (canvasElem && isPaused) {
|
||||
const size = canvasElem.getBoundingClientRect();
|
||||
const isIntersecting =
|
||||
size.width > 0 &&
|
||||
size.height > 0 &&
|
||||
size.top <
|
||||
(window.innerHeight || document.documentElement.clientHeight) &&
|
||||
size.bottom > 0 &&
|
||||
size.left <
|
||||
(window.innerWidth || document.documentElement.clientWidth) &&
|
||||
size.right > 0;
|
||||
if (isIntersecting) {
|
||||
rive?.startRendering();
|
||||
isPaused = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
const onChange = (entry: IntersectionObserverEntry) => {
|
||||
entry.isIntersecting
|
||||
? rive && rive.startRendering()
|
||||
: rive && rive.stopRendering();
|
||||
});
|
||||
|
||||
if (canvasRef.current) {
|
||||
observer.observe(canvasRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
isPaused = !entry.isIntersecting;
|
||||
clearTimeout(timeoutId);
|
||||
if (!entry.isIntersecting && entry.boundingClientRect.width === 0) {
|
||||
timeoutId = setTimeout(retestIntersection, 10);
|
||||
}
|
||||
};
|
||||
}, [rive]);
|
||||
if (canvasElem && options.shouldUseIntersectionObserver !== false) {
|
||||
observe(canvasElem, onChange);
|
||||
}
|
||||
return () => {
|
||||
if (canvasElem) {
|
||||
unobserve(canvasElem);
|
||||
}
|
||||
};
|
||||
}, [
|
||||
observe,
|
||||
unobserve,
|
||||
rive,
|
||||
canvasElem,
|
||||
options.shouldUseIntersectionObserver,
|
||||
]);
|
||||
|
||||
/**
|
||||
* On unmount, call cleanup to cleanup any WASM generated objects that need
|
||||
@@ -166,7 +215,7 @@ export default function useRive(
|
||||
setRive(null);
|
||||
}
|
||||
};
|
||||
}, [rive]);
|
||||
}, [rive, canvasElem]);
|
||||
|
||||
/**
|
||||
* Listen for changes in the animations params
|
||||
@@ -198,7 +247,7 @@ export default function useRive(
|
||||
);
|
||||
|
||||
return {
|
||||
canvas: canvasRef.current,
|
||||
canvas: canvasElem,
|
||||
container: containerRef.current,
|
||||
setCanvasRef,
|
||||
setContainerRef,
|
||||
|
||||
52
src/hooks/useRiveFile.ts
Normal file
52
src/hooks/useRiveFile.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import type {
|
||||
UseRiveFileParameters,
|
||||
RiveFileState,
|
||||
FileStatus,
|
||||
} from '../types';
|
||||
import { EventType, RiveFile } from '@rive-app/canvas';
|
||||
|
||||
/**
|
||||
* Custom hook for initializing and managing a RiveFile instance within a component.
|
||||
* It sets up a RiveFile based on provided source parameters (URL or ArrayBuffer) and ensures
|
||||
* proper cleanup to avoid memory leaks when the component unmounts or inputs change.
|
||||
*
|
||||
* @param params - Object containing parameters accepted by the Rive file in the @rive-app/canvas runtime,
|
||||
*
|
||||
* @returns {RiveFileState} Contains the active RiveFile instance (`riveFile`) and the loading status.
|
||||
*/
|
||||
function useRiveFile(params: UseRiveFileParameters): RiveFileState {
|
||||
const [riveFile, setRiveFile] = useState<RiveFile | null>(null);
|
||||
const [status, setStatus] = useState<FileStatus>('idle');
|
||||
|
||||
useEffect(() => {
|
||||
let file: RiveFile | null = null;
|
||||
|
||||
const loadRiveFile = async () => {
|
||||
setStatus('loading');
|
||||
file = new RiveFile(params);
|
||||
file.init();
|
||||
file.on(EventType.Load, () => {
|
||||
// We request an instance to add +1 to the referencesCount so it doesn't get destroyed
|
||||
// while this hook is active
|
||||
file?.getInstance();
|
||||
setRiveFile(file);
|
||||
setStatus('success');
|
||||
});
|
||||
file.on(EventType.LoadError, () => {
|
||||
setStatus('failed');
|
||||
});
|
||||
setRiveFile(file);
|
||||
};
|
||||
|
||||
loadRiveFile();
|
||||
|
||||
return () => {
|
||||
file?.cleanup();
|
||||
};
|
||||
}, [params.src, params.buffer]);
|
||||
|
||||
return { riveFile, status };
|
||||
}
|
||||
|
||||
export default useRiveFile;
|
||||
@@ -2,8 +2,9 @@ import Rive, { RiveProps } from './components/Rive';
|
||||
import useRive from './hooks/useRive';
|
||||
import useStateMachineInput from './hooks/useStateMachineInput';
|
||||
import useResizeCanvas from './hooks/useResizeCanvas';
|
||||
import useRiveFile from './hooks/useRiveFile';
|
||||
|
||||
export default Rive;
|
||||
export { useRive, useStateMachineInput, useResizeCanvas, RiveProps };
|
||||
export { RiveState, UseRiveParameters, UseRiveOptions } from './types';
|
||||
export { useRive, useStateMachineInput, useResizeCanvas, useRiveFile , RiveProps };
|
||||
export { RiveState, UseRiveParameters, UseRiveFileParameters, UseRiveOptions } from './types';
|
||||
export * from '@rive-app/canvas';
|
||||
|
||||
32
src/types.ts
32
src/types.ts
@@ -1,5 +1,10 @@
|
||||
import { RefCallback, ComponentProps } from 'react';
|
||||
import { Rive, RiveParameters } from '@rive-app/canvas';
|
||||
import {
|
||||
Rive,
|
||||
RiveFile,
|
||||
RiveFileParameters,
|
||||
RiveParameters,
|
||||
} from '@rive-app/canvas';
|
||||
import { ComponentProps, RefCallback } from 'react';
|
||||
|
||||
export type UseRiveParameters = Partial<Omit<RiveParameters, 'canvas'>> | null;
|
||||
|
||||
@@ -9,6 +14,7 @@ export type UseRiveOptions = {
|
||||
fitCanvasToArtboardHeight: boolean;
|
||||
useOffscreenRenderer: boolean;
|
||||
shouldResizeCanvasToContainer: boolean;
|
||||
shouldUseIntersectionObserver?: boolean;
|
||||
};
|
||||
|
||||
export type Dimensions = {
|
||||
@@ -21,9 +27,9 @@ export type Dimensions = {
|
||||
* @property canvas - Canvas element the Rive Animation is attached to.
|
||||
* @property container - Container element of the canvas.
|
||||
* @property setCanvasRef - Ref callback to be passed to the canvas element.
|
||||
* @property setContainerRef - Ref callback to be passed to the container element
|
||||
* of the canvas. This is optional, however if not used then the hook will
|
||||
* not take care of automatically resizing the canvas to it's outer
|
||||
* @property setContainerRef - Ref callback to be passed to the container
|
||||
* element of the canvas. This is optional, however if not used then the hook
|
||||
* will not take care of automatically resizing the canvas to it's outer
|
||||
* container if the window resizes.
|
||||
* @property rive - The loaded Rive Animation
|
||||
*/
|
||||
@@ -35,3 +41,19 @@ export type RiveState = {
|
||||
rive: Rive | null;
|
||||
RiveComponent: (props: ComponentProps<'canvas'>) => JSX.Element;
|
||||
};
|
||||
|
||||
export type UseRiveFileParameters = Partial<
|
||||
Omit<RiveFileParameters, 'onLoad' | 'onLoadError'>
|
||||
>;
|
||||
|
||||
export type FileStatus = 'idle' | 'loading' | 'failed' | 'success';
|
||||
|
||||
/**
|
||||
* @typedef RiveFileState
|
||||
* @property data - The RiveFile instance
|
||||
* @property status - The status of the file
|
||||
*/
|
||||
export type RiveFileState = {
|
||||
riveFile: RiveFile | null;
|
||||
status: FileStatus;
|
||||
};
|
||||
|
||||
39
test/elementObserver.test.tsx
Normal file
39
test/elementObserver.test.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
// TODO move this
|
||||
const observe = jest.fn();
|
||||
const unobserve = jest.fn();
|
||||
const disconnect = jest.fn();
|
||||
|
||||
jest.spyOn(globalThis, 'IntersectionObserver').mockImplementation(() => {
|
||||
return {
|
||||
observe,
|
||||
unobserve,
|
||||
disconnect,
|
||||
root: null,
|
||||
thresholds: [],
|
||||
rootMargin: '',
|
||||
takeRecords: () => [],
|
||||
};
|
||||
});
|
||||
|
||||
import ElementObserver from '../src/hooks/elementObserver';
|
||||
|
||||
describe('elementObserver', () => {
|
||||
it('registers a callback and observes the element', () => {
|
||||
const observer = new ElementObserver();
|
||||
const element = document.createElement('li');
|
||||
observer.registerCallback(element, ()=>{});
|
||||
expect(observe).toHaveBeenCalled();
|
||||
expect(observe).toHaveBeenCalledWith(element);
|
||||
});
|
||||
|
||||
it('unregisters a callback and unobserves the element', () => {
|
||||
const observer = new ElementObserver();
|
||||
const element = document.createElement('li');
|
||||
observer.removeCallback(element);
|
||||
expect(unobserve).toHaveBeenCalled();
|
||||
expect(unobserve).toHaveBeenCalledWith(element);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
jest.clearAllMocks();
|
||||
42
test/useIntersectionObserver.test.tsx
Normal file
42
test/useIntersectionObserver.test.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import ElementObserver from '../src/hooks/elementObserver';
|
||||
jest.mock('../src/hooks/elementObserver');
|
||||
|
||||
import useIntersectionObserver from '../src/hooks/useIntersectionObserver';
|
||||
|
||||
describe('useIntersectionObserver', () => {
|
||||
it('returns an object on initialization', () => {
|
||||
const { result } = renderHook(() => useIntersectionObserver());
|
||||
expect(result.current).toBeDefined();
|
||||
});
|
||||
|
||||
it('registers a callback', () => {
|
||||
const { result } = renderHook(() => useIntersectionObserver());
|
||||
const element = document.createElement('li');
|
||||
const callback = () => {};
|
||||
act(() => {
|
||||
result.current.observe(element, callback);
|
||||
});
|
||||
const mockElementObserver = (ElementObserver as jest.Mock).mock
|
||||
.instances[0];
|
||||
const registerCallback = mockElementObserver.registerCallback;
|
||||
expect(registerCallback.mock.calls.length).toBe(1);
|
||||
expect(registerCallback.mock.calls[0].length).toBe(2);
|
||||
expect(registerCallback.mock.calls[0][0]).toBe(element);
|
||||
expect(registerCallback.mock.calls[0][1]).toBe(callback);
|
||||
});
|
||||
|
||||
it('unregisters a callback', () => {
|
||||
const { result } = renderHook(() => useIntersectionObserver());
|
||||
const element = document.createElement('li');
|
||||
act(() => {
|
||||
result.current.unobserve(element);
|
||||
});
|
||||
const mockElementObserver = (ElementObserver as jest.Mock).mock
|
||||
.instances[0];
|
||||
const removeCallback = mockElementObserver.removeCallback;
|
||||
expect(removeCallback.mock.calls.length).toBe(1);
|
||||
expect(removeCallback.mock.calls[0].length).toBe(1);
|
||||
expect(removeCallback.mock.calls[0][0]).toBe(element);
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import { renderHook, act } from '@testing-library/react-hooks';
|
||||
|
||||
import useRive from '../src/hooks/useRive';
|
||||
import * as rive from '@rive-app/canvas';
|
||||
import { render } from '@testing-library/react';
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
|
||||
jest.mock('@rive-app/canvas', () => ({
|
||||
Rive: jest.fn().mockImplementation(() => ({
|
||||
@@ -65,6 +65,11 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
expect(result.current.rive).toBe(baseRiveMock);
|
||||
@@ -93,7 +98,14 @@ describe('useRive', () => {
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
result.current.setContainerRef(containerSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
await act(async () => {
|
||||
jest.spyOn(containerSpy, 'clientWidth', 'get').mockReturnValue(500);
|
||||
jest.spyOn(containerSpy, 'clientHeight', 'get').mockReturnValue(500);
|
||||
containerSpy.dispatchEvent(new Event('resize'));
|
||||
@@ -125,6 +137,11 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -153,6 +170,11 @@ describe('useRive', () => {
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
result.current.setContainerRef(containerSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -188,6 +210,11 @@ describe('useRive', () => {
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
result.current.setContainerRef(containerSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -222,6 +249,11 @@ describe('useRive', () => {
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
result.current.setContainerRef(containerSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -260,6 +292,11 @@ describe('useRive', () => {
|
||||
await act(async () => {
|
||||
result.current.setContainerRef(containerSpy);
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -271,43 +308,6 @@ describe('useRive', () => {
|
||||
expect(containerSpy).toHaveAttribute('style', 'height: 50px;');
|
||||
});
|
||||
|
||||
it('configures a IntersectionObserver on mounting', async () => {
|
||||
const params = {
|
||||
src: 'file-src',
|
||||
};
|
||||
|
||||
const observeMock = jest.fn();
|
||||
|
||||
const restore = global.IntersectionObserver;
|
||||
global.IntersectionObserver = jest.fn().mockImplementation(() => ({
|
||||
observe: observeMock,
|
||||
}));
|
||||
|
||||
const riveMock = {
|
||||
...baseRiveMock,
|
||||
bounds: {
|
||||
maxX: 100,
|
||||
maxY: 50,
|
||||
},
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
mocked(rive.Rive).mockImplementation(() => riveMock);
|
||||
|
||||
const canvasSpy = document.createElement('canvas');
|
||||
|
||||
const { result } = renderHook(() => useRive(params));
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
expect(observeMock).toBeCalledWith(canvasSpy);
|
||||
|
||||
global.IntersectionObserver = restore;
|
||||
});
|
||||
|
||||
it('updates the playing animations when the animations param changes', async () => {
|
||||
const params = {
|
||||
src: 'file-src',
|
||||
@@ -336,6 +336,11 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -379,6 +384,11 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -405,6 +415,11 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -428,6 +443,11 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -456,6 +476,11 @@ describe('useRive', () => {
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
result.current.setContainerRef(containerSpy);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
@@ -483,9 +508,17 @@ describe('useRive', () => {
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
result.current.setContainerRef(containerSpy);
|
||||
controlledRiveloadCb();
|
||||
jest.spyOn(containerSpy, 'clientWidth', 'get').mockReturnValue(200);
|
||||
jest.spyOn(containerSpy, 'clientHeight', 'get').mockReturnValue(200);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
containerSpy.dispatchEvent(new Event('resize'));
|
||||
});
|
||||
|
||||
@@ -516,7 +549,16 @@ describe('useRive', () => {
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
result.current.setContainerRef(containerSpy);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.canvas).toBe(canvasSpy);
|
||||
});
|
||||
await act(async () => {
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
jest.spyOn(containerSpy, 'clientWidth', 'get').mockReturnValue(500);
|
||||
jest.spyOn(containerSpy, 'clientHeight', 'get').mockReturnValue(500);
|
||||
containerSpy.dispatchEvent(new Event('resize'));
|
||||
|
||||
115
test/useRiveFile.test.tsx
Normal file
115
test/useRiveFile.test.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { mocked } from 'jest-mock';
|
||||
|
||||
import useRiveFile from '../src/hooks/useRiveFile';
|
||||
import { RiveFile } from '@rive-app/canvas';
|
||||
|
||||
jest.mock('@rive-app/canvas', () => ({
|
||||
RiveFile: jest.fn().mockImplementation(() => ({
|
||||
cleanup: jest.fn(),
|
||||
on: jest.fn(),
|
||||
init: jest.fn(),
|
||||
getInstance: jest.fn(),
|
||||
})),
|
||||
EventType: {
|
||||
Load: 'load',
|
||||
loadError: 'loadError',
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
describe('useRiveFile', () => {
|
||||
beforeEach(() => {
|
||||
mocked(RiveFile).mockClear();
|
||||
});
|
||||
|
||||
it('initializes RiveFile with provided parameters', async () => {
|
||||
const params = {
|
||||
src: 'file-src',
|
||||
enableRiveAssetCDN: false
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useRiveFile(params));
|
||||
|
||||
expect(RiveFile).toHaveBeenCalledWith(params);
|
||||
expect(result.current.riveFile).toBeDefined();
|
||||
});
|
||||
|
||||
it('cleans up RiveFile on unmount', async () => {
|
||||
const params = {
|
||||
src: 'file-src',
|
||||
enableRiveAssetCDN: false
|
||||
};
|
||||
|
||||
const { result, unmount } = renderHook(() => useRiveFile(params));
|
||||
|
||||
const riveInstance = result.current.riveFile;
|
||||
expect(riveInstance).toBeDefined();
|
||||
|
||||
unmount();
|
||||
|
||||
expect(riveInstance?.cleanup).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not reinitialize RiveFile if src has not changed', async () => {
|
||||
const params = {
|
||||
src: 'file-src',
|
||||
enableRiveAssetCDN: false
|
||||
};
|
||||
|
||||
const { rerender } = renderHook(() => useRiveFile(params));
|
||||
|
||||
rerender();
|
||||
|
||||
expect(RiveFile).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not reinitialize RiveFile if buffer has not changed', async () => {
|
||||
const params = {
|
||||
buffer: new ArrayBuffer(10),
|
||||
enableRiveAssetCDN: false
|
||||
};
|
||||
|
||||
const { rerender } = renderHook(() => useRiveFile(params));
|
||||
|
||||
rerender();
|
||||
|
||||
expect(RiveFile).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('reinitializes RiveFile if src changes', async () => {
|
||||
let params = {
|
||||
src: 'file-src',
|
||||
enableRiveAssetCDN: false
|
||||
};
|
||||
|
||||
const { rerender } = renderHook(() => useRiveFile(params));
|
||||
|
||||
params = {
|
||||
src: 'new-file-src',
|
||||
enableRiveAssetCDN: false
|
||||
};
|
||||
|
||||
rerender();
|
||||
|
||||
expect(RiveFile).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('reinitializes RiveFile if buffer changes', async () => {
|
||||
let params = {
|
||||
buffer: new ArrayBuffer(10),
|
||||
enableRiveAssetCDN: false
|
||||
};
|
||||
|
||||
const { rerender } = renderHook(() => useRiveFile(params));
|
||||
|
||||
params = {
|
||||
buffer: new ArrayBuffer(20),
|
||||
enableRiveAssetCDN: false
|
||||
};
|
||||
|
||||
rerender();
|
||||
|
||||
expect(RiveFile).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user