mirror of
https://github.com/rive-app/rive-react.git
synced 2026-03-13 08:22:30 +08:00
Compare commits
62 Commits
resizeExam
...
v3.0.46
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91025e6772 | ||
|
|
d3bc913bcf | ||
|
|
ce56321e1d | ||
|
|
4b6f5410fd | ||
|
|
6544874d3d | ||
|
|
ed90c7f7c9 | ||
|
|
e06fdd1c8b | ||
|
|
b8ffb6b53c | ||
|
|
2dc925ef70 | ||
|
|
639de79c9e | ||
|
|
a9961c821a | ||
|
|
fc082d1a03 | ||
|
|
87fa1ae2a5 | ||
|
|
62490a50f9 | ||
|
|
fcc1a16df4 | ||
|
|
29e0ceb797 | ||
|
|
3e6a951ca1 | ||
|
|
04910f78f1 | ||
|
|
c2977c705c | ||
|
|
4e2137422b | ||
|
|
0696417926 | ||
|
|
f11a433365 | ||
|
|
48a4726d1f | ||
|
|
c927f24b5a | ||
|
|
3cbbf99fec | ||
|
|
03f05f57b7 | ||
|
|
8b483b807b | ||
|
|
816fcfcdbc | ||
|
|
6c28a8795e | ||
|
|
6a57630ae6 | ||
|
|
acba24c4ed | ||
|
|
1e6eb5ec92 | ||
|
|
6871a81ebd | ||
|
|
33053b3b7f | ||
|
|
4040083d5a | ||
|
|
2153b81e6b | ||
|
|
6b1500e681 | ||
|
|
503702cf1f | ||
|
|
7be20b0a87 | ||
|
|
78491f5819 | ||
|
|
39edb88a19 | ||
|
|
fd1a1653b1 | ||
|
|
8d7f0ab28c | ||
|
|
2a2e532564 | ||
|
|
2b1aa01a87 | ||
|
|
06c4e2aea3 | ||
|
|
819bd51ea9 | ||
|
|
6b7f113296 | ||
|
|
a62e9b3a9a | ||
|
|
04685c0bcd | ||
|
|
48fd9f9d80 | ||
|
|
3c578b730f | ||
|
|
7a46886133 | ||
|
|
092049d20f | ||
|
|
f7aced03cd | ||
|
|
eb07281415 | ||
|
|
156b3bdfb5 | ||
|
|
24d8e0a907 | ||
|
|
a1a155849a | ||
|
|
59e67cec3d | ||
|
|
84b18cc3dd | ||
|
|
1092b44947 |
26
.github/workflows/add_to_project.yml
vendored
Normal file
26
.github/workflows/add_to_project.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Adds all new issues to project board
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add issue to project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.5.0
|
||||
with:
|
||||
project-url: https://github.com/orgs/rive-app/projects/12/views/1
|
||||
github-token: ${{ secrets.ADD_TO_PROJECT_ACTION }}
|
||||
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ["triage"]
|
||||
})
|
||||
144
CHANGELOG.md
144
CHANGELOG.md
@@ -4,9 +4,153 @@ 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.46](https://github.com/rive-app/rive-react/compare/v3.0.38...v3.0.46)
|
||||
|
||||
- Add prop to the standalone Rive component [`d3bc913`](https://github.com/rive-app/rive-react/commit/d3bc913bcf70e62df6cea5b5a08d0135c965f9ff)
|
||||
|
||||
#### [v3.0.38](https://github.com/rive-app/rive-react/compare/v3.0.37...v3.0.38)
|
||||
|
||||
> 3 March 2023
|
||||
|
||||
- chore: release 3.0.38 [`04910f7`](https://github.com/rive-app/rive-react/commit/04910f78f1779242626db0dfb74ab27ae95c75fa)
|
||||
- maint: bump wasm dependency for more speed on state updates [`c2977c7`](https://github.com/rive-app/rive-react/commit/c2977c705c5572480be3f253b7237bdc2d3eea35)
|
||||
|
||||
#### [v3.0.37](https://github.com/rive-app/rive-react/compare/v3.0.36...v3.0.37)
|
||||
|
||||
> 1 March 2023
|
||||
|
||||
- chore: release 3.0.37 [`4e21374`](https://github.com/rive-app/rive-react/commit/4e2137422b8c1d135a6501add3f2cc68140f1817)
|
||||
- maint: bump wasm dependency for more speed on state updates [`0696417`](https://github.com/rive-app/rive-react/commit/06964179262d4b79114f3609d79cc6560c30ef94)
|
||||
|
||||
#### [v3.0.36](https://github.com/rive-app/rive-react/compare/v3.0.35...v3.0.36)
|
||||
|
||||
> 22 February 2023
|
||||
|
||||
- chore: release 3.0.36 [`f11a433`](https://github.com/rive-app/rive-react/commit/f11a433365c34818976798106fc03a264e6ceaea)
|
||||
- Fix: matchMedia add event listener for safari 13 and older versions [`3cbbf99`](https://github.com/rive-app/rive-react/commit/3cbbf99fec90f58cdf410df861f272115a7704e6)
|
||||
- maint: bump wasm to support speed on states feature [`48a4726`](https://github.com/rive-app/rive-react/commit/48a4726d1f7a01088959d47580082e5ddfed1492)
|
||||
|
||||
#### [v3.0.35](https://github.com/rive-app/rive-react/compare/v3.0.34...v3.0.35)
|
||||
|
||||
> 17 January 2023
|
||||
|
||||
- chore: release 3.0.35 [`03f05f5`](https://github.com/rive-app/rive-react/commit/03f05f57b7f277d7d90701954b5ca09842bcaab6)
|
||||
- fix: update rive-wasm to try patching slow burn memory leak [`8b483b8`](https://github.com/rive-app/rive-react/commit/8b483b807b8207fe12c864e19f64592c0bedac46)
|
||||
|
||||
#### [v3.0.34](https://github.com/rive-app/rive-react/compare/v3.0.33...v3.0.34)
|
||||
|
||||
> 16 December 2022
|
||||
|
||||
- chore: release 3.0.34 [`816fcfc`](https://github.com/rive-app/rive-react/commit/816fcfcdbcb9f2d8f83cc7887f43aae011dee3be)
|
||||
- Bumping rive-app/canvas & webgl versions. [`6c28a87`](https://github.com/rive-app/rive-react/commit/6c28a8795eeabc10aa8bdcee99ee9754d924d0ae)
|
||||
|
||||
#### [v3.0.33](https://github.com/rive-app/rive-react/compare/v3.0.32...v3.0.33)
|
||||
|
||||
> 8 November 2022
|
||||
|
||||
- maint: bump wasm runtime and replace old skills rive file example [`acba24c`](https://github.com/rive-app/rive-react/commit/acba24c4ed4f703303615819678c7273c385286f)
|
||||
- chore: release 3.0.33 [`6a57630`](https://github.com/rive-app/rive-react/commit/6a57630ae67df057472a6d40f354fcfe53cb5521)
|
||||
|
||||
#### [v3.0.32](https://github.com/rive-app/rive-react/compare/v3.0.31...v3.0.32)
|
||||
|
||||
> 21 October 2022
|
||||
|
||||
- chore: release 3.0.32 [`1e6eb5e`](https://github.com/rive-app/rive-react/commit/1e6eb5ec927aa0bb10ee1e7659c4824c64702f58)
|
||||
- Bumping to latest Rive WASM to fix https://2dimensions.slack.com/archives/CLLCU09T6/p1666370141425879 [`6871a81`](https://github.com/rive-app/rive-react/commit/6871a81ebd17038426e7089ed836379d02a9c5fd)
|
||||
|
||||
#### [v3.0.31](https://github.com/rive-app/rive-react/compare/v3.0.30...v3.0.31)
|
||||
|
||||
> 21 October 2022
|
||||
|
||||
- chore: release 3.0.31 [`33053b3`](https://github.com/rive-app/rive-react/commit/33053b3b7f0ff6d99ce4383ecea1f031ccb587e0)
|
||||
- bump runtimes to incorporate speed fix for state machines [`4040083`](https://github.com/rive-app/rive-react/commit/4040083d5a89510356e45433e5026664baf92a49)
|
||||
|
||||
#### [v3.0.30](https://github.com/rive-app/rive-react/compare/v3.0.29...v3.0.30)
|
||||
|
||||
> 20 October 2022
|
||||
|
||||
- chore: release 3.0.30 [`2153b81`](https://github.com/rive-app/rive-react/commit/2153b81e6b74d33d1e2f709291fad338dcbb85da)
|
||||
- Bump dependencies to fix call to runtime cleanup [`6b1500e`](https://github.com/rive-app/rive-react/commit/6b1500e681ba8984d786eec4d414c4c8ad4b0268)
|
||||
|
||||
#### [v3.0.29](https://github.com/rive-app/rive-react/compare/v3.0.28...v3.0.29)
|
||||
|
||||
> 20 October 2022
|
||||
|
||||
- chore: release 3.0.29 [`503702c`](https://github.com/rive-app/rive-react/commit/503702cf1f963204e3519b9b3dbfea1bfa271d0a)
|
||||
- Call cleanup on unmount [`7be20b0`](https://github.com/rive-app/rive-react/commit/7be20b0a874afab7f6a29122521f42c71a22aa51)
|
||||
|
||||
#### [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)
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ Check out our Storybook instance that shows how to use the library in small exam
|
||||
- [Example page](https://rive-app.github.io/rive-react)
|
||||
- [Login screen w/ input tracking](https://rive-app.github.io/rive-use-cases/?path=/story/example-loginformcomponent--primary)
|
||||
- [Mouse tracking](https://codesandbox.io/s/rive-mouse-track-test-t0y965?file=/src/App.js)
|
||||
- [Accessibility concerns](https://blog.rive.app/accessible-web-animations-aria-live-regions/)
|
||||
- [Accessibility concerns](https://rive.app/blog/accesible-web-animations-aria-live-regions)
|
||||
|
||||
## Migration Guides
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import { useState } from 'react';
|
||||
|
||||
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
|
||||
|
||||
import RiveComponent, {useRive, useStateMachineInput} from '../../src';
|
||||
import {Button} from './components/Button';
|
||||
import RiveComponent, { useRive, useStateMachineInput } from '../../src';
|
||||
import { Button } from './components/Button';
|
||||
import './rive-overview.css';
|
||||
|
||||
<Meta title="React Runtime/State Machines" />
|
||||
@@ -15,6 +15,7 @@ import './rive-overview.css';
|
||||
Not familiar with Rive State Machines? Check out our [help docs](https://help.rive.app/editor/state-machine) on what these are first!
|
||||
|
||||
The `useStateMachineInput` hook is a helper that makes grabbing references to state machine inputs easier to setup. This hook should be used along with the `useRive` hook, as you need to pass in the `rive` instance returned from `useRive`. See each of the examples below to see usage of the hook creating instance of three types of inputs:
|
||||
|
||||
- Booleans
|
||||
- Numbers
|
||||
- Triggers
|
||||
@@ -90,8 +91,8 @@ Once you grab a reference to the state machine input, you can get/set the value
|
||||
<Canvas withSource="open">
|
||||
<Story name="Number input">
|
||||
{() => {
|
||||
const STATE_MACHINE_NAME = 'State Machine ';
|
||||
const INPUT_NAME = 'Level';
|
||||
const STATE_MACHINE_NAME = 'skill-controller';
|
||||
const INPUT_NAME = 'level';
|
||||
const { rive, RiveComponent: RiveComponentTouch } = useRive({
|
||||
src: 'skills.riv',
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
@@ -101,7 +102,11 @@ Once you grab a reference to the state machine input, you can get/set the value
|
||||
// levelInput is a number state machine input. To transition the state machine,
|
||||
// we need to set the value to a number. For this state machine input, we need
|
||||
// to set to 0, 1 or 2 for a state transition to occur.
|
||||
const levelInput = useStateMachineInput(rive, STATE_MACHINE_NAME, INPUT_NAME);
|
||||
const levelInput = useStateMachineInput(
|
||||
rive,
|
||||
STATE_MACHINE_NAME,
|
||||
INPUT_NAME
|
||||
);
|
||||
return (
|
||||
// The animation will fit to the parent element, so we set a large height
|
||||
// and width for this example.
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rive-react",
|
||||
"version": "3.0.19",
|
||||
"version": "3.0.46",
|
||||
"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.1.4",
|
||||
"@rive-app/webgl": "1.1.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
|
||||
@@ -28,7 +28,11 @@ export interface RiveProps {
|
||||
* For `@rive-app/react-webgl`, sets this property to maintain a single WebGL context for multiple canvases. **We recommend to keep the default value** when rendering multiple Rive instances on a page.
|
||||
*/
|
||||
useOffscreenRenderer?: boolean;
|
||||
};
|
||||
/**
|
||||
* Specify whether to disable Rive listeners on the canvas, thus preventing any event listeners to be attached to the canvas element
|
||||
*/
|
||||
shouldDisableRiveListeners?: boolean;
|
||||
}
|
||||
|
||||
const Rive = ({
|
||||
src,
|
||||
@@ -37,6 +41,7 @@ const Rive = ({
|
||||
stateMachines,
|
||||
layout,
|
||||
useOffscreenRenderer = true,
|
||||
shouldDisableRiveListeners = false,
|
||||
...rest
|
||||
}: RiveProps & ComponentProps<'canvas'>) => {
|
||||
const params = {
|
||||
@@ -46,6 +51,7 @@ const Rive = ({
|
||||
layout,
|
||||
stateMachines,
|
||||
autoplay: true,
|
||||
shouldDisableRiveListeners,
|
||||
};
|
||||
|
||||
const options = {
|
||||
|
||||
@@ -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.
|
||||
@@ -186,6 +215,9 @@ export default function useRive(
|
||||
// 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) {
|
||||
@@ -228,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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
93
src/utils.ts
93
src/utils.ts
@@ -3,14 +3,25 @@ import { Dimensions } from './types';
|
||||
|
||||
// There are polyfills for this, but they add hundreds of lines of code
|
||||
class FakeResizeObserver {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
observe() { }
|
||||
unobserve() { }
|
||||
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,90 @@ 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.hasOwnProperty('addEventListener')
|
||||
? mediaMatcher.addEventListener('change', updateDpr)
|
||||
: mediaMatcher.addListener(updateDpr);
|
||||
|
||||
return () => {
|
||||
mediaMatcher.hasOwnProperty('removeEventListener')
|
||||
? mediaMatcher.removeEventListener('change', updateDpr)
|
||||
: mediaMatcher.removeListener(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);
|
||||
}
|
||||
|
||||
@@ -102,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 = {
|
||||
...baseRiveMock,
|
||||
stop: stopMock,
|
||||
cleanup: cleanupMock,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
@@ -127,7 +127,7 @@ describe('useRive', () => {
|
||||
|
||||
unmount();
|
||||
|
||||
expect(stopMock).toBeCalled();
|
||||
expect(cleanupMock).toBeCalled();
|
||||
});
|
||||
|
||||
it('sets the a bounds with the devicePixelRatio by default', async () => {
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user