Compare commits

...

30 Commits

Author SHA1 Message Date
avivian
2153b81e6b chore: release 3.0.30 2022-10-20 18:34:05 +00:00
Arthur Vivian
6b1500e681 Bump dependencies to fix call to runtime cleanup 2022-10-20 19:31:16 +01:00
avivian
503702cf1f chore: release 3.0.29 2022-10-20 17:06:43 +00:00
Arthur Vivian
7be20b0a87 Call cleanup on unmount 2022-10-20 18:03:30 +01:00
avivian
78491f5819 chore: release 3.0.28 2022-10-20 09:44:48 +00:00
Arthur Vivian
39edb88a19 Bump canvas and webgl dependencies to fix alignment memory leaks 2022-10-20 10:42:06 +01:00
zplata
fd1a1653b1 chore: release 3.0.27 2022-10-04 17:56:49 +00:00
Zach Plata
8d7f0ab28c Fix: Bump wasm to accomodate clipping bug on nested artboards 2022-10-04 12:53:17 -05:00
zplata
2a2e532564 chore: release 3.0.26 2022-09-22 17:15:22 +00:00
Zach Plata
2b1aa01a87 fix: Adjust canvas size if devicePixelRatio changes for any reaason 2022-09-22 12:12:32 -05:00
zplata
06c4e2aea3 chore: release 3.0.25 2022-09-21 14:23:55 +00:00
Zach Plata
819bd51ea9 patch: bump js runtime dependency to address content security policy issue in WASM build 2022-09-21 09:20:55 -05:00
zplata
6b7f113296 chore: release 3.0.24 2022-09-15 15:56:54 +00:00
Zach Plata
a62e9b3a9a add tests 2022-09-15 10:53:36 -05:00
Zach Plata
04685c0bcd fix: make a canvas of size 0 until we calculate the bounds appropriately 2022-09-15 10:53:36 -05:00
avivian
48fd9f9d80 chore: release 3.0.23 2022-08-31 10:27:17 +00:00
Arthur Vivian
3c578b730f Bump runtime version to fix broken version 2022-08-31 11:24:05 +01:00
avivian
7a46886133 chore: release 3.0.22 2022-08-30 17:09:24 +00:00
Arthur Vivian
092049d20f Update @rive-app/canvas and @rive-app/webgl dependencies to support non node builds 2022-08-30 18:06:18 +01:00
mjtalbot
f7aced03cd chore: release 3.0.21 2022-07-22 13:34:00 +00:00
Maxwell Talbot
eb07281415 rev rive-wasm dependencies & update render delay to be 0ms 2022-07-22 14:30:04 +01:00
mjtalbot
156b3bdfb5 chore: release 3.0.20 2022-07-22 12:56:26 +00:00
Maxwell Talbot
24d8e0a907 update tests 2022-07-22 13:52:42 +01:00
Maxwell Talbot
a1a155849a use window.settimeout and clear out some consts 2022-07-22 13:52:42 +01:00
Maxwell Talbot
59e67cec3d update name 2022-07-22 13:52:42 +01:00
Maxwell Talbot
84b18cc3dd ensure we re evaluate state machine inputs when we play is triggered, looks like there maybe additional situations where we are going to need this. 2022-07-22 13:52:42 +01:00
Maxwell Talbot
1092b44947 update resize behaviour to throttle, add parameters to enable switching modes 2022-07-22 13:52:42 +01:00
zplata
efe28aa5f3 chore: release 3.0.19 2022-07-19 23:48:34 +00:00
Zach Plata
16d836c959 fix tests that were automatically calling the rive load callback to be more controlled 2022-07-19 18:44:20 -05:00
Zach Plata
838ed1abf8 Fix: Add check before setting Rive as state variable on Rive instance load 2022-07-19 18:44:20 -05:00
7 changed files with 498 additions and 118 deletions

View File

@@ -4,9 +4,193 @@ 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).
#### [v3.0.18](https://github.com/rive-app/rive-react/compare/v3.0.1...v3.0.18)
#### [v3.0.30](https://github.com/rive-app/rive-react/compare/v3.0.28...v3.0.30)
- Bump dependencies to fix call to runtime cleanup [`6b1500e`](https://github.com/rive-app/rive-react/commit/6b1500e681ba8984d786eec4d414c4c8ad4b0268)
#### [v3.0.28](https://github.com/rive-app/rive-react/compare/v3.0.27...v3.0.28)
> 20 October 2022
- chore: release 3.0.28 [`78491f5`](https://github.com/rive-app/rive-react/commit/78491f5819b78b2e7435a2509e89fd3c672f3126)
- Bump canvas and webgl dependencies to fix alignment memory leaks [`39edb88`](https://github.com/rive-app/rive-react/commit/39edb88a19aaff478e4e9cbeeef58414f28fdb60)
#### [v3.0.27](https://github.com/rive-app/rive-react/compare/v3.0.26...v3.0.27)
> 4 October 2022
- chore: release 3.0.27 [`fd1a165`](https://github.com/rive-app/rive-react/commit/fd1a1653b13c2fd15f2862e6c372a27fd49acd00)
- Fix: Bump wasm to accomodate clipping bug on nested artboards [`8d7f0ab`](https://github.com/rive-app/rive-react/commit/8d7f0ab28c8aa6af3abe5269e7b8980cc1a8dbe7)
#### [v3.0.26](https://github.com/rive-app/rive-react/compare/v3.0.25...v3.0.26)
> 22 September 2022
- fix: Adjust canvas size if devicePixelRatio changes for any reaason [`2b1aa01`](https://github.com/rive-app/rive-react/commit/2b1aa01a87c14f71b980d160f6607edc12d3eed6)
- chore: release 3.0.26 [`2a2e532`](https://github.com/rive-app/rive-react/commit/2a2e53256401b6d7137db4b73c5901d587a888af)
#### [v3.0.25](https://github.com/rive-app/rive-react/compare/v3.0.24...v3.0.25)
> 21 September 2022
- chore: release 3.0.25 [`06c4e2a`](https://github.com/rive-app/rive-react/commit/06c4e2aea39d8e08ee12760663bd612ca77f3e9c)
- patch: bump js runtime dependency to address content security policy issue in WASM build [`819bd51`](https://github.com/rive-app/rive-react/commit/819bd51ea976bbea0c218379cd88304bd1738323)
#### [v3.0.24](https://github.com/rive-app/rive-react/compare/v3.0.23...v3.0.24)
> 15 September 2022
- add tests [`a62e9b3`](https://github.com/rive-app/rive-react/commit/a62e9b3a9aeb51b71c441ea1560eea6431704ee7)
- chore: release 3.0.24 [`6b7f113`](https://github.com/rive-app/rive-react/commit/6b7f1132964dafe9783f0a79782a2ba20638c7ce)
- fix: make a canvas of size 0 until we calculate the bounds appropriately [`04685c0`](https://github.com/rive-app/rive-react/commit/04685c0bcda26ee0451ab8cc9fc09436e94d04f6)
#### [v3.0.23](https://github.com/rive-app/rive-react/compare/v3.0.22...v3.0.23)
> 31 August 2022
- chore: release 3.0.23 [`48fd9f9`](https://github.com/rive-app/rive-react/commit/48fd9f9d8048ef098ed7550e6da0d3a4eb69148e)
- Bump runtime version to fix broken version [`3c578b7`](https://github.com/rive-app/rive-react/commit/3c578b730f82059469393522722316f2ad3a61d3)
#### [v3.0.22](https://github.com/rive-app/rive-react/compare/v3.0.21...v3.0.22)
> 30 August 2022
- chore: release 3.0.22 [`7a46886`](https://github.com/rive-app/rive-react/commit/7a468861336861361f4fceae45616f001bf4b448)
- Update @rive-app/canvas and @rive-app/webgl dependencies to support non node builds [`092049d`](https://github.com/rive-app/rive-react/commit/092049d20f6f955a0528831d2b5e15087328bc75)
#### [v3.0.21](https://github.com/rive-app/rive-react/compare/v3.0.20...v3.0.21)
> 22 July 2022
- chore: release 3.0.21 [`f7aced0`](https://github.com/rive-app/rive-react/commit/f7aced03cd3c39d039cc53af54947e328fe18e83)
- rev rive-wasm dependencies & update render delay to be 0ms [`eb07281`](https://github.com/rive-app/rive-react/commit/eb072814155bb803f6faa831caa0e0292b8f6f28)
#### [v3.0.20](https://github.com/rive-app/rive-react/compare/v3.0.19...v3.0.20)
> 22 July 2022
- update tests [`24d8e0a`](https://github.com/rive-app/rive-react/commit/24d8e0a90795f650806064d53ae1b362e3fd332f)
- update resize behaviour to throttle, add parameters to enable switching modes [`1092b44`](https://github.com/rive-app/rive-react/commit/1092b44947e2ac07dd38d21e8b45445256c0a59d)
- ensure we re evaluate state machine inputs when we play is triggered, looks like there maybe additional situations where we are going to need this. [`84b18cc`](https://github.com/rive-app/rive-react/commit/84b18cc3ddf86e55b6741956ea8f86d6d21f4078)
#### [v3.0.19](https://github.com/rive-app/rive-react/compare/v3.0.18...v3.0.19)
> 19 July 2022
- chore: release 3.0.19 [`efe28aa`](https://github.com/rive-app/rive-react/commit/efe28aa5f35f5ddde3e89085c34016ce87bb5cbb)
- fix tests that were automatically calling the rive load callback to be more controlled [`16d836c`](https://github.com/rive-app/rive-react/commit/16d836c95928e4294b565ecb444d517653c4988b)
- Fix: Add check before setting Rive as state variable on Rive instance load [`838ed1a`](https://github.com/rive-app/rive-react/commit/838ed1abf8aeec86ca63bfef07953424ba9cce90)
#### [v3.0.18](https://github.com/rive-app/rive-react/compare/v3.0.17...v3.0.18)
> 14 July 2022
- update canvas dimensions to use clientWidth and Height as opposed to BoundingClient, to avoid getting scaled information [`fd1c00a`](https://github.com/rive-app/rive-react/commit/fd1c00a995374634ec8552d20a0f7094fcb04e25)
- chore: release 3.0.18 [`d010a55`](https://github.com/rive-app/rive-react/commit/d010a55cc0c065c353dd5424a6fef8a58f416c61)
#### [v3.0.17](https://github.com/rive-app/rive-react/compare/v3.0.16...v3.0.17)
> 14 July 2022
- add resizeObserver to replace window listeners for all but IE [`e966316`](https://github.com/rive-app/rive-react/commit/e966316971d88a7242651a0b1fa3a1eaff48d276)
- refactored the IE check into `useSize` [`5be9d2f`](https://github.com/rive-app/rive-react/commit/5be9d2f8741224ed7cd291898b1abe88668b3fed)
- Fix useEffects so they're not in conditional statements [`ec61a68`](https://github.com/rive-app/rive-react/commit/ec61a6835d9ca6158538f5d1ac5b6b861c58ac57)
#### [v3.0.16](https://github.com/rive-app/rive-react/compare/v3.0.15...v3.0.16)
> 12 July 2022
- Docs: Condense down README and add CONTRIBUTING guide [`0863835`](https://github.com/rive-app/rive-react/commit/08638359bb817213fb861950a20cae7e7b27111f)
- staged work [`7dbade4`](https://github.com/rive-app/rive-react/commit/7dbade4589ca0524b58f9abbdcc38afa3e3b1866)
- chore: release 3.0.16 [`ae6efc1`](https://github.com/rive-app/rive-react/commit/ae6efc14d46c33b90fe89ee067347296daf865e7)
#### [v3.0.15](https://github.com/rive-app/rive-react/compare/v3.0.14...v3.0.15)
> 28 June 2022
- chore: release 3.0.15 [`8175c4a`](https://github.com/rive-app/rive-react/commit/8175c4a4d406ac80703a6df346f3b5562d2e9311)
- Patch: Bump js runtime dependencies for nested artboard display patch [`795ee53`](https://github.com/rive-app/rive-react/commit/795ee533405ec98457db074d11730849e1be5c88)
#### [v3.0.14](https://github.com/rive-app/rive-react/compare/v3.0.12...v3.0.14)
> 28 June 2022
- Deploying to main from @ 3477afdef166251f35f1778a3143ff6c6efecc58 🚀 [`7aee5cf`](https://github.com/rive-app/rive-react/commit/7aee5cfab4eaca1fc9369742639507a770c4f756)
- Fix: Intake JS runtime patches for starting animation frames [`3477afd`](https://github.com/rive-app/rive-react/commit/3477afdef166251f35f1778a3143ff6c6efecc58)
- chore: release 3.0.14 [`04353db`](https://github.com/rive-app/rive-react/commit/04353db43266f6dcf40f4ef7f3be23afa13c2e0d)
#### [v3.0.12](https://github.com/rive-app/rive-react/compare/v3.0.11...v3.0.12)
> 22 June 2022
- chore: release 3.0.12 [`8b43a82`](https://github.com/rive-app/rive-react/commit/8b43a82c5f56cbb5b1fe7dacfa7ca8457fc6d413)
- Fix: Bump cpp to get nested artboard opacity fix and fill rule patch [`bd49e6a`](https://github.com/rive-app/rive-react/commit/bd49e6a4ee66c68005b60a670700ef69b5322656)
- Bump @rive-app/canvas to take the fillRule bug fix [`1dbb9cd`](https://github.com/rive-app/rive-react/commit/1dbb9cd38d41393b9f354cdf81e88c702aa3ae64)
#### [v3.0.11](https://github.com/rive-app/rive-react/compare/v3.0.10...v3.0.11)
> 22 June 2022
- chore: release 3.0.11 [`aee7407`](https://github.com/rive-app/rive-react/commit/aee7407f7921c515f3c1d9aabf87387baddb4064)
- Docs: Code snippets update to use new React package structure [`b48de9d`](https://github.com/rive-app/rive-react/commit/b48de9db8496be35f29bea87273a7a9fceefdafc)
#### [v3.0.10](https://github.com/rive-app/rive-react/compare/v3.0.8...v3.0.10)
> 20 June 2022
- Deploying to main from @ 5ad5a957a6e8f10abedc23f46033d4792e29dfe5 🚀 [`802648e`](https://github.com/rive-app/rive-react/commit/802648eda8fa0e5a0a35c66af06e476eac59fe9e)
- chore: release 3.0.10 [`6772f16`](https://github.com/rive-app/rive-react/commit/6772f166b7f3e4747ae508a54e2533bb3ea49878)
- Maint: Update docs for storybook link [`5ad5a95`](https://github.com/rive-app/rive-react/commit/5ad5a957a6e8f10abedc23f46033d4792e29dfe5)
#### [v3.0.8](https://github.com/rive-app/rive-react/compare/v3.0.7...v3.0.8)
> 9 June 2022
- Maint: Add GH workflow for deploying storybook to Github Pages [`38625a0`](https://github.com/rive-app/rive-react/commit/38625a00c313192d0edbe1c3a855bea1ec56bd2b)
- chore: release 3.0.8 [`414d6f8`](https://github.com/rive-app/rive-react/commit/414d6f895ac2184876dec90959c17c2b22f6843f)
#### [v3.0.7](https://github.com/rive-app/rive-react/compare/v3.0.6...v3.0.7)
> 8 June 2022
- Feat: Move existing examples into Storybook and add documentation [`ec230fa`](https://github.com/rive-app/rive-react/commit/ec230faa738202cedad14cc866e30c4c03efffd7)
- chore: release 3.0.7 [`bad688d`](https://github.com/rive-app/rive-react/commit/bad688dfa3841ec07e30fa07609a6cb7bb7c1688)
#### [v3.0.6](https://github.com/rive-app/rive-react/compare/v3.0.5...v3.0.6)
> 6 June 2022
- chore: release 3.0.6 [`90c6d1e`](https://github.com/rive-app/rive-react/commit/90c6d1edb1d4bef6250dd4a5101a7cfe04ff9ce9)
- Maint: Roll canvas and webgl dependencies forward to support nested state machines [`0480dc9`](https://github.com/rive-app/rive-react/commit/0480dc92c842265d601d08b60fb49392969cfd9e)
#### [v3.0.5](https://github.com/rive-app/rive-react/compare/v3.0.4...v3.0.5)
> 26 May 2022
- chore: release 3.0.5 [`de24fa5`](https://github.com/rive-app/rive-react/commit/de24fa564117d4acbe60b4cf734abd9e951b30f1)
- Feat: Add stateMachines param to the default Rive component [`84d9730`](https://github.com/rive-app/rive-react/commit/84d9730767a62c63e743d5a04bba5b3d480ea38d)
- Maint: Bump wasm for another listener patch [`805afd5`](https://github.com/rive-app/rive-react/commit/805afd5dff2888294926c32ec07f5e24db804d09)
#### [v3.0.4](https://github.com/rive-app/rive-react/compare/v3.0.3...v3.0.4)
> 23 May 2022
- chore: release 3.0.4 [`9abee34`](https://github.com/rive-app/rive-react/commit/9abee34d12641f845b93febf438df0f77f72153f)
- Maint: Bump rive-wasm dependency for listener patches [`12801b1`](https://github.com/rive-app/rive-react/commit/12801b10cc8980339e5856d71d96da3c612cb291)
#### [v3.0.3](https://github.com/rive-app/rive-react/compare/v3.0.2...v3.0.3)
> 17 May 2022
- Feat: Bump wasm and add examples to support touch feature [`3902948`](https://github.com/rive-app/rive-react/commit/3902948a2ef8af6955ef12124207edee29eb0be8)
- chore: release 3.0.3 [`da11387`](https://github.com/rive-app/rive-react/commit/da1138755861aadb9e7c6cb0028f2120d610a6c5)
#### [v3.0.2](https://github.com/rive-app/rive-react/compare/v3.0.1...v3.0.2)
> 17 May 2022
- chore: release 3.0.2 [`21a17ed`](https://github.com/rive-app/rive-react/commit/21a17ed40ee51263c666dde48b6c55e958eceeb8)
- Maint: Bump wasm dependencies [`f0e7092`](https://github.com/rive-app/rive-react/commit/f0e70924ec9849f45ecddda801ad63e1d87b1bdb)
#### [v3.0.1](https://github.com/rive-app/rive-react/compare/v3.0.0...v3.0.1)

View File

@@ -1,6 +1,6 @@
{
"name": "rive-react",
"version": "3.0.18",
"version": "3.0.30",
"description": "React wrapper around the rive-js library",
"main": "dist/index.js",
"typings": "dist/types/index.d.ts",
@@ -29,8 +29,8 @@
},
"homepage": "https://github.com/rive-app/rive-react#readme",
"dependencies": {
"@rive-app/canvas": "1.0.71",
"@rive-app/webgl": "1.0.68"
"@rive-app/canvas": "1.0.91",
"@rive-app/webgl": "1.0.87"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"

View File

@@ -13,7 +13,7 @@ import {
RiveState,
Dimensions,
} from '../types';
import { useSize } from '../utils';
import { useSize, useDevicePixelRatio } from '../utils';
type RiveComponentProps = {
setContainerRef: RefCallback<HTMLElement>;
@@ -39,7 +39,11 @@ function RiveComponent({
className={className}
{...(!className && { style: containerStyle })}
>
<canvas ref={setCanvasRef} style={{ verticalAlign: 'top' }} {...rest} />
<canvas
ref={setCanvasRef}
style={{ verticalAlign: 'top', width: 0, height: 0 }}
{...rest}
/>
</div>
);
}
@@ -82,7 +86,12 @@ export default function useRive(
const containerRef = useRef<HTMLElement | null>(null);
const [rive, setRive] = useState<Rive | null>(null);
const [dimensions, setDimensions] = useState<Dimensions>({
const [lastContainerDimensions, setLastContainerDimensions] =
useState<Dimensions>({
height: 0,
width: 0,
});
const [lastCanvasSize, setLastCanvasSize] = useState<Dimensions>({
height: 0,
width: 0,
});
@@ -90,6 +99,7 @@ export default function useRive(
// Listen to changes in the window sizes and update the bounds when changes
// occur.
const size = useSize(containerRef);
const currentDevicePixelRatio = useDevicePixelRatio();
const isParamsLoaded = Boolean(riveParams);
const options = getOptions(opts);
@@ -127,23 +137,42 @@ export default function useRive(
}
const { width, height } = getCanvasDimensions();
const boundsChanged =
width !== dimensions.width || height !== dimensions.height;
if (canvasRef.current && rive && boundsChanged) {
if (options.fitCanvasToArtboardHeight) {
if (canvasRef.current && rive) {
// Check if the canvas parent container bounds have changed and set
// new values accordingly
const boundsChanged =
width !== lastContainerDimensions.width ||
height !== lastContainerDimensions.height;
if (options.fitCanvasToArtboardHeight && boundsChanged) {
containerRef.current.style.height = height + 'px';
}
if (options.useDevicePixelRatio) {
const dpr = window.devicePixelRatio || 1;
canvasRef.current.width = dpr * width;
canvasRef.current.height = dpr * height;
canvasRef.current.style.width = width + 'px';
canvasRef.current.style.height = height + 'px';
} else {
// Check if devicePixelRatio may have changed and get new canvas
// width/height values to set the size
const canvasSizeChanged =
width * currentDevicePixelRatio !== lastCanvasSize.width ||
height * currentDevicePixelRatio !== lastCanvasSize.height;
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';
setLastCanvasSize({
width: newCanvasWidthProp,
height: newCanvasHeightProp,
});
}
} else if (boundsChanged) {
canvasRef.current.width = width;
canvasRef.current.height = height;
setLastCanvasSize({
width: width,
height: height,
});
}
setDimensions({ width, height });
setLastContainerDimensions({ width, height });
// Updating the canvas width or height will clear the canvas, so call
// startRendering() to redraw the current frame as the animation might
@@ -167,7 +196,7 @@ export default function useRive(
if (rive) {
updateBounds();
}
}, [rive, size]);
}, [rive, size, currentDevicePixelRatio]);
/**
* Ref callback called when the canvas element mounts and unmounts.
@@ -181,7 +210,16 @@ export default function useRive(
...riveParams,
canvas,
});
r.on(EventType.Load, () => setRive(r));
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;
@@ -222,12 +260,13 @@ export default function useRive(
}, [rive]);
/**
* On unmount, stop rive from rendering.
* On unmount, call cleanup to cleanup any WASM generated objects that need
* to be manually destroyed.
*/
useEffect(() => {
return () => {
if (rive) {
rive.stop();
rive.cleanup();
setRive(null);
}
};

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { Rive, StateMachineInput } from '@rive-app/canvas';
import { EventType, Rive, StateMachineInput } from '@rive-app/canvas';
/**
* Custom hook for fetching a stateMachine input from a rive file.
@@ -18,21 +18,33 @@ export default function useStateMachineInput(
const [input, setInput] = useState<StateMachineInput | null>(null);
useEffect(() => {
if (!rive || !stateMachineName || !inputName) {
setInput(null);
}
if (rive && stateMachineName && inputName) {
const inputs = rive.stateMachineInputs(stateMachineName);
if (inputs) {
const selectedInput = inputs.find((input) => input.name === inputName);
if (initialValue !== undefined && selectedInput) {
selectedInput.value = initialValue;
}
setInput(selectedInput || null);
function setStateMachineInput() {
if (!rive || !stateMachineName || !inputName) {
setInput(null);
}
} else {
setInput(null);
if (rive && stateMachineName && inputName) {
const inputs = rive.stateMachineInputs(stateMachineName);
if (inputs) {
const selectedInput = inputs.find(
(input) => input.name === inputName
);
if (initialValue !== undefined && selectedInput) {
selectedInput.value = initialValue;
}
setInput(selectedInput || null);
}
} else {
setInput(null);
}
}
setStateMachineInput();
if (rive) {
rive.on(EventType.Play, () => {
// Check if the component/canvas is mounted before setting state to avoid setState
// on an unmounted component in some rare cases
setStateMachineInput();
});
}
}, [rive]);

View File

@@ -8,9 +8,20 @@ class FakeResizeObserver {
disconnect() {}
}
function throttle(f: Function, delay: number) {
let timer = 0;
return function (this: Function, ...args: any) {
clearTimeout(timer);
timer = window.setTimeout(() => f.apply(this, args), delay);
};
}
const MyResizeObserver = globalThis.ResizeObserver || FakeResizeObserver;
const hasResizeObserver = globalThis.ResizeObserver !== undefined;
const useResizeObserver = hasResizeObserver;
const useWindowListener = !useResizeObserver;
export function useSize(
containerRef: React.MutableRefObject<HTMLElement | null>
) {
@@ -29,34 +40,86 @@ export function useSize(
});
};
if (!hasResizeObserver) {
if (useWindowListener) {
// only pay attention to window size changes when we do not have the resizeObserver (IE only)
window.addEventListener('resize', handleResize);
handleResize();
window.addEventListener('resize', handleResize);
}
return () => window.removeEventListener('resize', handleResize);
}
}, []);
const observer = useRef(
new MyResizeObserver((entries) => {
setSize({
width: entries[entries.length - 1].contentRect.width,
height: entries[entries.length - 1].contentRect.height,
});
})
new MyResizeObserver(
throttle((entries: any) => {
if (useResizeObserver) {
setSize({
width: entries[entries.length - 1].contentRect.width,
height: entries[entries.length - 1].contentRect.height,
});
}
}, 0)
)
);
useEffect(() => {
const current = observer.current;
if (containerRef.current) {
if (containerRef.current && useResizeObserver) {
current.observe(containerRef.current);
}
return () => {
current.disconnect();
if (containerRef.current && useResizeObserver) {
current.unobserve(containerRef.current);
}
};
}, [containerRef, observer]);
return size;
}
/**
* Listen for devicePixelRatio changes and set the new value accordingly. This could
* happen for reasons such as:
* - User moves window from retina screen display to a separate monitor
* - User controls zoom settings on the browser
*
* Source: https://github.com/rexxars/use-device-pixel-ratio/blob/main/index.ts
*
* @returns dpr: Number - Device pixel ratio; ratio of physical px to resolution in CSS pixels for current device
*/
export function useDevicePixelRatio() {
const dpr = getDevicePixelRatio();
const [currentDpr, setCurrentDpr] = useState(dpr);
useEffect(() => {
const canListen = typeof window !== 'undefined' && 'matchMedia' in window;
if (!canListen) {
return;
}
const updateDpr = () => {
const newDpr = getDevicePixelRatio();
setCurrentDpr(newDpr);
};
const mediaMatcher = window.matchMedia(
`screen and (resolution: ${currentDpr}dppx)`
);
mediaMatcher.addEventListener('change', updateDpr);
return () => {
mediaMatcher.removeEventListener('change', updateDpr);
};
}, [currentDpr]);
return currentDpr;
}
export function getDevicePixelRatio(): number {
const hasDprProp =
typeof window !== 'undefined' &&
typeof window.devicePixelRatio === 'number';
const dpr = hasDprProp ? window.devicePixelRatio : 1;
return Math.min(Math.max(1, dpr), 3);
}

View File

@@ -29,6 +29,23 @@ jest.mock('@rive-app/canvas', () => ({
}));
describe('useRive', () => {
let controlledRiveloadCb: () => void;
let baseRiveMock: Partial<rive.Rive>;
beforeEach(() => {
baseRiveMock = {
on: (_: rive.EventType, cb: rive.EventCallback) =>
((controlledRiveloadCb as rive.EventCallback) = cb),
stop: jest.fn(),
stopRendering: jest.fn(),
startRendering: jest.fn(),
};
});
afterEach(() => {
controlledRiveloadCb = () => {};
});
it('returns rive as null if no params are passed', () => {
const { result } = renderHook(() => useRive());
expect(result.current.rive).toBe(null);
@@ -40,23 +57,17 @@ describe('useRive', () => {
src: 'file-src',
};
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: jest.fn(),
stopRendering: jest.fn(),
};
// @ts-ignore
mocked(rive.Rive).mockImplementation(() => riveMock);
mocked(rive.Rive).mockImplementation(() => baseRiveMock);
const canvasSpy = document.createElement('canvas');
const { result } = renderHook(() => useRive(params));
await act(async () => {
result.current.setCanvasRef(canvasSpy);
controlledRiveloadCb();
});
expect(result.current.rive).toBe(riveMock);
expect(result.current.rive).toBe(baseRiveMock);
expect(result.current.canvas).toBe(canvasSpy);
});
@@ -68,9 +79,7 @@ describe('useRive', () => {
const resizeToCanvasMock = jest.fn();
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: jest.fn(),
stopRendering: jest.fn(),
...baseRiveMock,
resizeToCanvas: resizeToCanvasMock,
};
@@ -84,6 +93,7 @@ describe('useRive', () => {
await act(async () => {
result.current.setCanvasRef(canvasSpy);
result.current.setContainerRef(containerSpy);
controlledRiveloadCb();
});
expect(result.current.rive).toBe(riveMock);
@@ -92,16 +102,16 @@ describe('useRive', () => {
expect(resizeToCanvasMock).toBeCalled();
});
it('stops the rive object on unmount', async () => {
it('calls cleanup on the rive object on unmount', async () => {
const params = {
src: 'file-src',
};
const stopMock = jest.fn();
const cleanupMock = jest.fn();
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: stopMock,
...baseRiveMock,
cleanup: cleanupMock,
};
// @ts-ignore
@@ -112,11 +122,12 @@ describe('useRive', () => {
await act(async () => {
result.current.setCanvasRef(canvasSpy);
controlledRiveloadCb();
});
unmount();
expect(stopMock).toBeCalled();
expect(cleanupMock).toBeCalled();
});
it('sets the a bounds with the devicePixelRatio by default', async () => {
@@ -126,13 +137,8 @@ describe('useRive', () => {
global.devicePixelRatio = 2;
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: jest.fn(),
};
// @ts-ignore
mocked(rive.Rive).mockImplementation(() => riveMock);
mocked(rive.Rive).mockImplementation(() => baseRiveMock);
const canvasSpy = document.createElement('canvas');
const containerSpy = document.createElement('div');
@@ -144,6 +150,7 @@ describe('useRive', () => {
await act(async () => {
result.current.setCanvasRef(canvasSpy);
result.current.setContainerRef(containerSpy);
controlledRiveloadCb();
});
// Height and width should be 2* the width and height returned from containers
@@ -164,13 +171,8 @@ describe('useRive', () => {
useDevicePixelRatio: false,
};
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: jest.fn(),
};
// @ts-ignore
mocked(rive.Rive).mockImplementation(() => riveMock);
mocked(rive.Rive).mockImplementation(() => baseRiveMock);
const canvasSpy = document.createElement('canvas');
const containerSpy = document.createElement('div');
@@ -182,6 +184,7 @@ describe('useRive', () => {
await act(async () => {
result.current.setCanvasRef(canvasSpy);
result.current.setContainerRef(containerSpy);
controlledRiveloadCb();
});
// Height and width should be same as containers bounding rect
@@ -199,8 +202,7 @@ describe('useRive', () => {
};
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: jest.fn(),
...baseRiveMock,
bounds: {
maxX: 100,
maxY: 50,
@@ -220,6 +222,7 @@ describe('useRive', () => {
await act(async () => {
result.current.setContainerRef(containerSpy);
result.current.setCanvasRef(canvasSpy);
controlledRiveloadCb();
});
// Height and width should be same as containers bounding rect
@@ -243,8 +246,7 @@ describe('useRive', () => {
}));
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: jest.fn(),
...baseRiveMock,
bounds: {
maxX: 100,
maxY: 50,
@@ -260,6 +262,7 @@ describe('useRive', () => {
await act(async () => {
result.current.setCanvasRef(canvasSpy);
controlledRiveloadCb();
});
expect(observeMock).toBeCalledWith(canvasSpy);
@@ -277,7 +280,7 @@ describe('useRive', () => {
const stopMock = jest.fn();
const riveMock = {
on: (_: string, cb: () => void) => cb(),
...baseRiveMock,
stop: stopMock,
play: playMock,
animationNames: ['light'],
@@ -295,6 +298,7 @@ describe('useRive', () => {
await act(async () => {
result.current.setCanvasRef(canvasSpy);
controlledRiveloadCb();
});
rerender({
@@ -317,7 +321,7 @@ describe('useRive', () => {
const stopMock = jest.fn();
const riveMock = {
on: (_: string, cb: () => void) => cb(),
...baseRiveMock,
stop: stopMock,
play: playMock,
pause: pauseMock,
@@ -337,6 +341,7 @@ describe('useRive', () => {
await act(async () => {
result.current.setCanvasRef(canvasSpy);
controlledRiveloadCb();
});
rerender({
@@ -354,20 +359,15 @@ describe('useRive', () => {
src: 'file-src',
};
const riveMock = {
on: (_: string, cb: () => void) => cb(),
stop: jest.fn(),
stopRendering: jest.fn(),
};
// @ts-ignore
mocked(rive.Rive).mockImplementation(() => riveMock);
mocked(rive.Rive).mockImplementation(() => baseRiveMock);
const canvasSpy = document.createElement('canvas');
const { result } = renderHook(() => useRive(params));
await act(async () => {
result.current.setCanvasRef(canvasSpy);
controlledRiveloadCb();
});
const { RiveComponent: RiveTestComponent } = result.current;
@@ -376,4 +376,79 @@ describe('useRive', () => {
);
expect(container.firstChild).not.toHaveStyle('width: 50%');
});
it('has a canvas size of 0 by default', async () => {
const params = {
src: 'file-src',
};
// @ts-ignore
mocked(rive.Rive).mockImplementation(() => baseRiveMock);
const canvasSpy = document.createElement('canvas');
const { result } = renderHook(() => useRive(params));
await act(async () => {
result.current.setCanvasRef(canvasSpy);
controlledRiveloadCb();
});
const { RiveComponent: RiveTestComponent } = result.current;
const { container } = render(<RiveTestComponent />);
expect(container.querySelector('canvas')).toHaveStyle('width: 0');
});
it('sets the canvas width and height after calculating the container size', async () => {
const params = {
src: 'file-src',
};
global.devicePixelRatio = 2;
// @ts-ignore
mocked(rive.Rive).mockImplementation(() => baseRiveMock);
const canvasSpy = document.createElement('canvas');
const containerSpy = document.createElement('div');
jest.spyOn(containerSpy, 'clientWidth', 'get').mockReturnValue(100);
jest.spyOn(containerSpy, 'clientHeight', 'get').mockReturnValue(100);
const { result } = renderHook(() => useRive(params));
await act(async () => {
result.current.setCanvasRef(canvasSpy);
result.current.setContainerRef(containerSpy);
controlledRiveloadCb();
});
expect(canvasSpy).toHaveStyle('height: 100px');
expect(canvasSpy).toHaveStyle('width: 100px');
});
it('updates the canvas dimensions and size if there is a new canvas size calculation', async () => {
const params = {
src: 'file-src',
};
window.devicePixelRatio = 2;
// @ts-ignore
mocked(rive.Rive).mockImplementation(() => baseRiveMock);
const canvasSpy = document.createElement('canvas');
const containerSpy = document.createElement('div');
jest.spyOn(containerSpy, 'clientWidth', 'get').mockReturnValue(100);
jest.spyOn(containerSpy, 'clientHeight', 'get').mockReturnValue(100);
const { result } = renderHook(() => useRive(params));
await act(async () => {
result.current.setCanvasRef(canvasSpy);
result.current.setContainerRef(containerSpy);
controlledRiveloadCb();
});
expect(canvasSpy).toHaveAttribute('width', '200');
expect(canvasSpy).toHaveAttribute('height', '200');
});
});

View File

@@ -8,6 +8,7 @@ jest.mock('@rive-app/canvas', () => ({
Rive: jest.fn().mockImplementation(() => ({
on: jest.fn(),
stop: jest.fn(),
stateMachineInputs: jest.fn(),
})),
Layout: jest.fn(),
Fit: {
@@ -26,6 +27,21 @@ jest.mock('@rive-app/canvas', () => ({
},
}));
function getRiveMock({
smiInputs,
}: {
smiInputs?: null | StateMachineInput[];
} = {}) {
const riveMock = new Rive({
canvas: undefined as unknown as HTMLCanvasElement,
});
if (smiInputs) {
riveMock.stateMachineInputs = jest.fn().mockReturnValue(smiInputs);
}
return riveMock;
}
describe('useStateMachineInput', () => {
it('returns null if there is null rive object passed', () => {
const { result } = renderHook(() => useStateMachineInput(null));
@@ -33,31 +49,29 @@ describe('useStateMachineInput', () => {
});
it('returns null if there is no state machine name', () => {
const riveMock = {};
mocked(Rive).mockImplementation(() => riveMock as Rive);
const riveMock = getRiveMock();
mocked(Rive).mockImplementation(() => riveMock);
const { result } = renderHook(() =>
useStateMachineInput(riveMock as Rive, '', 'testInput')
useStateMachineInput(riveMock, '', 'testInput')
);
expect(result.current).toBeNull();
});
it('returns null if there is no state machine input name', () => {
const riveMock = {};
mocked(Rive).mockImplementation(() => riveMock as Rive);
const riveMock = getRiveMock();
const { result } = renderHook(() =>
useStateMachineInput(riveMock as Rive, 'smName', '')
useStateMachineInput(riveMock, 'smName', '')
);
expect(result.current).toBeNull();
});
it('returns null if there are no inputs for the state machine', () => {
const riveMock = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
stateMachineInputs: (_: string) => [] as StateMachineInput[],
};
mocked(Rive).mockImplementation(() => riveMock as Rive);
const riveMock = getRiveMock({ smiInputs: [] });
mocked(Rive).mockImplementation(() => riveMock);
const { result } = renderHook(() =>
useStateMachineInput(riveMock as Rive, 'smName', '')
@@ -69,14 +83,12 @@ describe('useStateMachineInput', () => {
const smInput = {
name: 'boolInput',
} as StateMachineInput;
const riveMock = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
stateMachineInputs: (_: string) => [smInput] as StateMachineInput[],
};
mocked(Rive).mockImplementation(() => riveMock as Rive);
const riveMock = getRiveMock({ smiInputs: [smInput] });
mocked(Rive).mockImplementation(() => riveMock);
const { result } = renderHook(() =>
useStateMachineInput(riveMock as Rive, 'smName', 'numInput')
useStateMachineInput(riveMock, 'smName', 'numInput')
);
expect(result.current).toBeNull();
});
@@ -85,14 +97,12 @@ describe('useStateMachineInput', () => {
const smInput = {
name: 'boolInput',
} as StateMachineInput;
const riveMock = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
stateMachineInputs: (_: string) => [smInput] as StateMachineInput[],
};
mocked(Rive).mockImplementation(() => riveMock as Rive);
const riveMock = getRiveMock({ smiInputs: [smInput] });
mocked(Rive).mockImplementation(() => riveMock);
const { result } = renderHook(() =>
useStateMachineInput(riveMock as Rive, 'smName', 'boolInput')
useStateMachineInput(riveMock, 'smName', 'boolInput')
);
expect(result.current).toBe(smInput);
});
@@ -102,14 +112,11 @@ describe('useStateMachineInput', () => {
name: 'boolInput',
value: false,
} as StateMachineInput;
const riveMock = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
stateMachineInputs: (_: string) => [smInput] as StateMachineInput[],
};
mocked(Rive).mockImplementation(() => riveMock as Rive);
const riveMock = getRiveMock({ smiInputs: [smInput] });
mocked(Rive).mockImplementation(() => riveMock);
const { result } = renderHook(() =>
useStateMachineInput(riveMock as Rive, 'smName', 'boolInput', true)
useStateMachineInput(riveMock, 'smName', 'boolInput', true)
);
expect(result.current).toStrictEqual({
...smInput,