mirror of
https://github.com/rive-app/rive-react.git
synced 2026-03-13 08:22:30 +08:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a46886133 | ||
|
|
092049d20f | ||
|
|
f7aced03cd | ||
|
|
eb07281415 | ||
|
|
156b3bdfb5 | ||
|
|
24d8e0a907 | ||
|
|
a1a155849a | ||
|
|
59e67cec3d | ||
|
|
84b18cc3dd | ||
|
|
1092b44947 | ||
|
|
efe28aa5f3 | ||
|
|
16d836c959 | ||
|
|
838ed1abf8 | ||
|
|
d010a55cc0 | ||
|
|
fd1c00a995 | ||
|
|
45aec2db1c | ||
|
|
62b3a1d8dc | ||
|
|
5be9d2f874 | ||
|
|
49a6b1de11 | ||
|
|
ec61a6835d | ||
|
|
ac29fa30a7 | ||
|
|
e966316971 | ||
|
|
ae6efc14d4 | ||
|
|
8ce77f153c | ||
|
|
08638359bb | ||
|
|
7dbade4589 | ||
|
|
8175c4a4d4 | ||
|
|
795ee53340 |
35
.eslintrc.js
35
.eslintrc.js
@@ -1,29 +1,38 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true
|
||||
es2021: true,
|
||||
},
|
||||
extends: ['plugin:react/recommended', 'prettier', 'plugin:storybook/recommended'],
|
||||
extends: [
|
||||
'plugin:react/recommended',
|
||||
'prettier',
|
||||
'plugin:storybook/recommended',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
jsx: true,
|
||||
},
|
||||
ecmaVersion: 12,
|
||||
sourceType: 'module'
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'prettier'],
|
||||
plugins: ['@typescript-eslint', 'prettier', 'react-hooks'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'prefer-const': ['warn', {
|
||||
destructuring: 'all'
|
||||
}],
|
||||
'prefer-const': [
|
||||
'warn',
|
||||
{
|
||||
destructuring: 'all',
|
||||
},
|
||||
],
|
||||
'no-var': 'error',
|
||||
eqeqeq: ['error', 'smart']
|
||||
eqeqeq: ['error', 'smart'],
|
||||
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
||||
'react-hooks/exhaustive-deps': 'off', // Checks effect dependencies
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect'
|
||||
}
|
||||
}
|
||||
};
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
12
.github/workflows/storybook.yml
vendored
12
.github/workflows/storybook.yml
vendored
@@ -1,10 +1,12 @@
|
||||
name: Deploy Storybook
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- main
|
||||
paths: ['src', 'examples/stories/**'] # Trigger the action only when files change in the folders defined here
|
||||
# Testing to see if this job is causing the race condition
|
||||
workflow_dispatch:
|
||||
# pull_request:
|
||||
# types: [closed]
|
||||
# branches:
|
||||
# - main
|
||||
# paths: ['src', 'examples/stories/**'] # Trigger the action only when files change in the folders defined here
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
138
CHANGELOG.md
138
CHANGELOG.md
@@ -4,9 +4,143 @@ 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.14](https://github.com/rive-app/rive-react/compare/v3.0.1...v3.0.14)
|
||||
#### [v3.0.22](https://github.com/rive-app/rive-react/compare/v3.0.21...v3.0.22)
|
||||
|
||||
- Fix: Bump version because of race condition again [`1f0e511`](https://github.com/rive-app/rive-react/commit/1f0e511f6e047d05ca2783a56c2924a9c1af432a)
|
||||
- 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)
|
||||
|
||||
|
||||
74
CONTRIBUTING.md
Normal file
74
CONTRIBUTING.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Contributing
|
||||
|
||||
We love contributions! If you want to run the project locally to test out changes, run the examples, or just see how things work under the hood, read on below.
|
||||
|
||||
## Local development
|
||||
|
||||
This runtime consumes specific tied-down versions of the [JS/WASM runtime](https://github.com/rive-app/rive-wasm) to have better control over changes that occur in that downstream runtime.
|
||||
|
||||
### Installation
|
||||
|
||||
1. Clone the project down
|
||||
2. Run `npm i` in the shell/terminal at the base of the project to install the dependencies needed for the project
|
||||
|
||||
### Local dev server
|
||||
|
||||
To start the local dev server to reflect any changes made to the core `src/` files, run the following in a terminal tab:
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Running the example storybook locally
|
||||
|
||||
We use Storybook to deploy our examples out onto a public-facing page for folks to view and see code examples for. It also serves as the place we'll include any example suites. These story files are stored in `examples/**.stories.mdx`. `.mdx` is an extension Storybook supports to support both `jsx` (React files) and `md` (markdown) in one file.
|
||||
|
||||
To run Storybook, run the following command in the terminal:
|
||||
|
||||
```
|
||||
npm run storybook
|
||||
```
|
||||
|
||||
Any changes made in any files should reflect automatically, including the `.mdx` example files, and `src/` files.
|
||||
|
||||
### Testing
|
||||
|
||||
We also have a suite of unit tests against the high-level component and various hooks exported in the `test/` folder. When adding new components, changing the API, or underlying functionality, make sure to add a test here!
|
||||
|
||||
To run the test suite:
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
## Making changes
|
||||
|
||||
When you're ready to make changes, push up to a feature branch off of the `main` branch. Create a pull request to this repository in Github. When creating commit messages, please be as descriptive as possible to the changes being made.
|
||||
|
||||
For example, if the change is simply a bug fix or patch change:
|
||||
|
||||
```
|
||||
git commit -m "Fix: Fixing a return type from useRive"
|
||||
```
|
||||
|
||||
Or if it's simply a docs change:
|
||||
|
||||
```
|
||||
git commit -m "Docs: Adding a new link for another example page"
|
||||
```
|
||||
|
||||
For minor/major version releases, also ensure you preface commit messages with:
|
||||
|
||||
```
|
||||
git commit -m "Major: Restructuring the useRive API with new parameters"
|
||||
```
|
||||
|
||||
These messages help make the changelog clear as to what changes are made for future devs to see.
|
||||
|
||||
When pull requests are merged, the runtime will automatically deploy the next release version. By default, patch versions are published. If you want to set the next version as a minor/major version to be released, you have to manually update the `package.json` file at the root of the project to the verison you want it to.
|
||||
|
||||
You can find the deploy scripts in `.github/`
|
||||
|
||||
## Bumping the underlying JS/WASM runtime
|
||||
|
||||
Many times, fixes to the runtime and feature adds come from the underlying JS/WASM runtime. In these cases, just bump the `@rive-app/canvas` and `@rive-app/webgl` versions to the verison you need to incorporate the fix/feature. Run `npm i` and test out the change locally against the Storybook examples and run the test suite to make sure nothing breaks, and then submit a PR with just the `package.json` change if that's all that's needed.
|
||||
223
README.md
223
README.md
@@ -1,220 +1,67 @@
|
||||

|
||||
[](https://rive-app.github.io/rive-react)
|
||||

|
||||

|
||||
|
||||
# Rive React
|
||||
|
||||
React Runtime for [Rive](https://rive.app).
|
||||

|
||||
|
||||
A wrapper around [Rive.js](https://github.com/rive-app/rive-wasm), providing full control over the js runtime while making it super simple to use in React applications.
|
||||
A React runtime library for [Rive](https://rive.app).
|
||||
|
||||
Detailed runtime documentation can be found in [Rive's help center](https://help.rive.app/runtimes).
|
||||
This library is a wrapper around the [JS/Wasm runtime](https://github.com/rive-app/rive-wasm), giving full control over the js runtime while providing components and hooks for React applications.
|
||||
|
||||
## Create and ship interactive animations to any platform
|
||||
## Table of contents
|
||||
|
||||
[Rive](https://rive.app) is a real-time interactive design and animation tool. Use our collaborative editor to create motion graphics that respond to different states and user inputs. Then load your animations into apps, games, and websites with our lightweight open-source runtimes.
|
||||
- :star: [Rive Overview](#rive-overview)
|
||||
- 🚀 [Getting Started & API docs](#getting-started)
|
||||
- :mag: [Supported Versions](#supported-versions)
|
||||
- :books: [Examples](#examples)
|
||||
- :runner: [Migration Guides](#migration-guides)
|
||||
- 👨💻 [Contributing](#contributing)
|
||||
- :question: [Issues](#issues)
|
||||
|
||||
## Installation
|
||||
## Rive Overview
|
||||
|
||||
There are two main variants of the React runtime:
|
||||
[Rive](https://rive.app) is a real-time interactive design and animation tool that helps teams create and run interactive animations anywhere. Designers and developers use our collaborative editor to create motion graphics that respond to different states and user inputs. Our lightweight open-source runtime libraries allow them to load their animations into apps, games, and websites.
|
||||
|
||||
1. **Recommended** Using [Canvas2D](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
|
||||
:house_with_garden: [Homepage](https://rive.app/)
|
||||
|
||||
```
|
||||
npm i --save @rive-app/react-canvas
|
||||
```
|
||||
:blue_book: [General help docs](https://help.rive.app/)
|
||||
|
||||
2. Using [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API)
|
||||
🛠 [Resources for building in Rive](https://rive.app/resources/)
|
||||
|
||||
```
|
||||
npm i --save @rive-app/react-webgl
|
||||
```
|
||||
## Getting Started
|
||||
|
||||
Read more in our [web runtime docs](https://github.com/rive-app/rive-wasm/blob/master/WEB_RUNTIMES.md) around the differences. For most cases and smallest bundle size, we recommend using the `react-canvas` package.
|
||||
Follow along with the link below for a quick start in getting Rive React integrated into your React apps.
|
||||
|
||||
_Note: This library is using React hooks so the minimum version required for both react and react-dom is 16.8.0._
|
||||
[Getting Started with Rive in React](https://help.rive.app/runtimes/overview/react)
|
||||
|
||||
## Usage
|
||||
[API documentation](https://help.rive.app/runtimes/overview/react/parameters-and-return-values)
|
||||
|
||||
### Component
|
||||
## Supported Versions
|
||||
|
||||
Rive React provides a basic component as it's default import for displaying simple animations. Note that any animations or state machines instantiated through this component will autoplay.
|
||||
|
||||
```js
|
||||
import Rive from '@rive-app/react-canvas';
|
||||
|
||||
function Example() {
|
||||
return <Rive src="loader.riv" />;
|
||||
}
|
||||
|
||||
export default Example;
|
||||
```
|
||||
|
||||
#### Props
|
||||
|
||||
- `src`: File path or URL to the .riv file to display.
|
||||
- `artboard`: _(optional)_ Name to display.
|
||||
- `animations`: _(optional)_ Name or list of names of animtions to play.
|
||||
- `stateMachines`: _(optional)_ Name of state machine to play.
|
||||
- `layout`: _(optional)_ Layout object to define how animations are displayed on the canvas. See [Rive.js](https://github.com/rive-app/rive-wasm#layout) for more details.
|
||||
- _All attributes and eventHandlers that can be passed to a `canvas` element can also be passed to the `Rive` component and used in the same manner._
|
||||
|
||||
#### Styles and Classes
|
||||
|
||||
When rendering out a Rive component, in the DOM, it will show as a `<div>` element that contains the `<canvas>` element that powers the Rive animations. The purpose of the `<div>` element is to help control the sizing of the component. By default, the container has the following styles set on the `style` attribute:
|
||||
```css
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
```
|
||||
|
||||
If you decide to pass in a `className` to the Rive component, you will override these attributes, and you will need to either set these style attributes in your CSS associated with that `className`, or set your own sizing preferences.
|
||||
|
||||
### useRive Hook
|
||||
|
||||
For more advanced usage, the `useRive` hook is provided. The hook will return a component and a [Rive.js](https://github.com/rive-app/rive-wasm) `Rive` object which gives you control of the current rive file.
|
||||
|
||||
```js
|
||||
import { useRive } from '@rive-app/react-canvas';
|
||||
|
||||
function Example() {
|
||||
const params = {
|
||||
src: 'loader.riv',
|
||||
autoplay: false,
|
||||
};
|
||||
const { RiveComponent, rive } = useRive(params);
|
||||
|
||||
return (
|
||||
<RiveComponent
|
||||
onMouseEnter={() => rive && rive.play()}
|
||||
onMouseLeave={() => rive && rive.pause()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Example;
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `riveParams`: Set of parameters that are passed to the Rive.js `Rive` class constructor. `null` and `undefined` can be passed to conditionally display the .riv file.
|
||||
- `opts`: Rive React specific options.
|
||||
|
||||
#### Return Values
|
||||
|
||||
- `RiveComponent`: A Component that can be used to display your .riv file. This component accepts the same attributes and event handlers as a `canvas` element.
|
||||
- `rive`: A Rive.js `Rive` object. This will return as null until the .riv file has fully loaded.
|
||||
- `canvas`: HTMLCanvasElement object, on which the .riv file is rendering.
|
||||
- `setCanvasRef`: A callback ref that can be passed to your own canvas element, if you wish to have control over the rendering of the Canvas element.
|
||||
- `setContainerRef`: A callback ref that can be passed to a container element that wraps the canvas element, if you which to have control over the rendering of the container element.
|
||||
_For the vast majority of use cases, you can just the returned `RiveComponent` and don't need to worry about `setCanvasRef` and `setContainerRef`._
|
||||
|
||||
#### riveParams
|
||||
|
||||
- `src?`: _(optional)_ File path or URL to the .riv file to use. One of `src` or `buffer` must be provided.
|
||||
- `buffer?`: _(optional)_ ArrayBuffer containing the raw bytes from a .riv file. One of `src` or `buffer` must be provided.
|
||||
- `artboard?`: _(optional)_ Name of the artboard to use.
|
||||
- `animations?`: _(optional)_ Name or list of names of animations to play.
|
||||
- `stateMachines?`: _(optional)_ Name of list of names of state machines to load.
|
||||
- `layout?`: _(optional)_ Layout object to define how animations are displayed on the canvas. See [Rive.js](https://github.com/rive-app/rive-wasm#layout) for more details.
|
||||
- `autoplay?`: _(optional)_ If `true`, the animation will automatically start playing when loaded. Defaults to false.
|
||||
- `onLoad?`: _(optional)_ Callback that get's fired when the .rive file loads .
|
||||
- `onLoadError?`: _(optional)_ Callback that get's fired when an error occurs loading the .riv file.
|
||||
- `onPlay?`: _(optional)_ Callback that get's fired when the animation starts playing.
|
||||
- `onPause?`: _(optional)_ Callback that get's fired when the animation pauses.
|
||||
- `onStop?`: _(optional)_ Callback that get's fired when the animation stops playing.
|
||||
- `onLoop?`: _(optional)_ Callback that get's fired when the animation completes a loop.
|
||||
- `onStateChange?`: _(optional)_ Callback that get's fired when a state change occurs.
|
||||
|
||||
#### opts
|
||||
|
||||
- `useDevicePixelRatio`: _(optional)_ If `true`, the hook will scale the resolution of the animation based the [devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). Defaults to `true`. NOTE: Requires the `setContainerRef` ref callback to be passed to a element wrapping a canvas element. If you use the `RiveComponent`, then this will happen automatically.
|
||||
- `fitCanvasToArtboardHeight`: _(optional)_ If `true`, then the canvas will resize based on the height of the artboard. Defaults to `false`.
|
||||
- `useOffscreenRenderer`: _(optional)_ If `true`, the Rive instance will share (or create if one does not exist) an offscreen `WebGL` context. This allows you to display multiple Rive animations on one screen to work around some browser limitations regarding multiple concurrent WebGL contexts. If `false`, each Rive instance will have its own dedicated `WebGL` context, and you may need to be cautious of the browser limitations just mentioned. Defaults to `true`.
|
||||
|
||||
### useStateMachineInput Hook
|
||||
|
||||
The `useStateMachineInput` hook is provided to make it easier to interact with state machine inputs on a rive file.
|
||||
|
||||
```js
|
||||
import { useRive, useStateMachineInput } from '@rive-app/react-canvas';
|
||||
|
||||
function Example() {
|
||||
const STATE_MACHINE_NAME = 'button';
|
||||
const INPUT_NAME = 'onClick';
|
||||
|
||||
const { RiveComponent, rive } = useRive({
|
||||
src: 'button.riv',
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
autoplay: true,
|
||||
});
|
||||
|
||||
const onClickInput = useStateMachineInput(
|
||||
rive,
|
||||
STATE_MACHINE_NAME,
|
||||
INPUT_NAME
|
||||
);
|
||||
|
||||
// This example is using a state machine with a trigger input.
|
||||
return <RiveComponent onClick={() => onClickInput.fire()} />;
|
||||
}
|
||||
|
||||
export default Example;
|
||||
```
|
||||
|
||||
See our [examples](examples) folder for working examples of [Boolean](examples/state-machine-boolean-input) and [Number](examples/state-machine-number-input) inputs.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `rive`: A `Rive` object. This is returned by the `useRive` hook.
|
||||
- `stateMachineName`: Name of the state machine.
|
||||
- `inputName`: Name of the state machine input.
|
||||
- `initialValue`: Initial value to set on a state machine input when it's loaded in, for number or boolean inputs. **Note** that this may trigger any transitional animations between the initial state and any next states that depend on the input this `initialValue` is being set to. If this is problematic or conflicting for your case, we recommend setting the true initial value of the input on your state machine in the Rive editor.
|
||||
|
||||
#### Return Value
|
||||
|
||||
A Rive.js `stateMachineInput` object.
|
||||
This library supports React versions `^16.8.0` through `^18.0.0`.
|
||||
|
||||
## Examples
|
||||
|
||||
This project uses [Storybook](https://storybook.js.org/) to build examples and API documentation. Check it out at:
|
||||
https://rive-app.github.io/rive-react. To run locally, simply run `npm run storybook`.
|
||||
Check out our Storybook instance that shows how to use the library in small examples, along with code snippets! This includes examples using the basic component, as well as the convenient hooks exported to take advantage of state machines.
|
||||
|
||||
## Migration notes
|
||||
- [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/)
|
||||
|
||||
### Migrating from version 0.0.x to 1.x.x
|
||||
## Migration Guides
|
||||
|
||||
Starting in v 1.0.0, we've migrated from wrapping around the `@rive-app/canvas` runtime (which uses the `CanvasRendereringContext2D` renderer) to the `@rive-app/webgl` runtime (which uses the WebGL renderer). The high-level API doesn't require any change to upgrade, but there are some notes to consider about the backing renderer.
|
||||
Using an older version of the runtime and need to learn how to upgrade to the latest version? Check out the migration guides below in our help center that help guide you through version bumps; breaking changes and all!
|
||||
|
||||
The backing `WebGL` runtime allows for best performance across all devices, as well as support for some features that are not supported in the `canvas` renderer runtime. To allow the `react` runtime to support some of the newer features in Rive, we needed to switch the `rive-react` backing runtime to `@rive-app/webgl`.
|
||||
[Migration guides](https://help.rive.app/runtimes/overview/react/migrating-from-1.x.x-to-3.x.x)
|
||||
|
||||
One note about this switch is that some browsers may limit the number of concurrent WebGL contexts. For example, Chrome may only support up to 16 contexts concurrently. We pass a property called `useOffscreenRenderer` set to true to the backing runtime when instantiating Rive by default, which helps to manage the lifecycle of the `canvas` with a single offscreen `WebGL` context, even if there are many Rive animations on the screen (i.e 16+). If you need a single `WebGL` context per Rive animation/instance, pass in the `useOffscreenRenderer` property set to `false` in the `useRive` options, or as a prop in the default export component from this runtime. See below for an example:
|
||||
## Contributing
|
||||
|
||||
```js
|
||||
const {rive, RiveComponent} = useRive({
|
||||
src: 'foo.riv',
|
||||
}, {
|
||||
// Default (you don't need to set this)
|
||||
useOffscreenRenderer: true,
|
||||
// To override and use one context per Rive instance, uncomment and use the line below
|
||||
// useOffscreenRenderer: false,
|
||||
});
|
||||
We love contributions! Check out our [contributing docs](./CONTRIBUTING.md) to get more details into how to run this project, the examples, and more all locally.
|
||||
|
||||
// or you can override the flag in JSX via props
|
||||
return (
|
||||
<Rive src="foo.riv" useOffscreenRenderer={false} />
|
||||
);
|
||||
```
|
||||
## Issues
|
||||
|
||||
### Migrating from version 1.x.x to 2.x.x
|
||||
|
||||
#### Package split
|
||||
|
||||
In most cases, you may be able to migrate safely. We are mainly enabling the React runtime to work with both backing renderers `@rive-app/webgl` and `@rive-app/canvas`, such that you can use either `@rive-app/react-canvas` or `@rive-app/react-webgl` as the dependency in your React applications. Another change that is mostly internal is that by default, `rive-react` will now use `@rive-app/canvas` (as opposed to `@rive-app/webgl`) to wrap around, as it currently yields the fastest performance across devices. Therefore, **we recommend installing `@rive-app/react-canvas` in your applicaions**. However, if you need a WebGL backing renderer, you may want to use `@rive-app/react-webgl`.
|
||||
|
||||
#### Classes, styles, and component props
|
||||
Starting in v2.0, we introduce one breaking change where any non-style props set on the `RiveComponent` (i.e `aria-*`, `role`, etc.) will be set on the inner `<canvas>` element. Previously, all extra props would be set onto the containing `<div>` element. Both the `className` and `style` props will continue to be set on the `<div>` element that wraps the canvas, as this dictates the sizing of the Rive component.
|
||||
|
||||
|
||||
### Migrating to 3.0
|
||||
|
||||
There are no breaking changes here. If you have migrated to v2.x.x, you can safely migrate to 3.0.
|
||||
Have an issue with using the runtime, or want to suggest a feature/API to help make your development life better? Log an issue in our [issues](https://github.com/rive-app/rive-react/issues) tab! You can also browse older issues and discussion threads there to see solutions that may have worked for common problems.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rive-react",
|
||||
"version": "3.0.14",
|
||||
"version": "3.0.22",
|
||||
"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.70",
|
||||
"@rive-app/webgl": "1.0.67"
|
||||
"@rive-app/canvas": "1.0.81",
|
||||
"@rive-app/webgl": "1.0.77"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
@@ -65,6 +65,7 @@
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.27.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-storybook": "^0.5.12",
|
||||
"jest": "^27.0.4",
|
||||
"prettier": "^2.3.1",
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
RiveState,
|
||||
Dimensions,
|
||||
} from '../types';
|
||||
import { useWindowSize } from '../utils';
|
||||
import { useSize } from '../utils';
|
||||
|
||||
type RiveComponentProps = {
|
||||
setContainerRef: RefCallback<HTMLElement>;
|
||||
@@ -80,6 +80,7 @@ export default function useRive(
|
||||
): RiveState {
|
||||
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const containerRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
const [rive, setRive] = useState<Rive | null>(null);
|
||||
const [dimensions, setDimensions] = useState<Dimensions>({
|
||||
height: 0,
|
||||
@@ -88,7 +89,7 @@ export default function useRive(
|
||||
|
||||
// Listen to changes in the window sizes and update the bounds when changes
|
||||
// occur.
|
||||
const windowSize = useWindowSize();
|
||||
const size = useSize(containerRef);
|
||||
|
||||
const isParamsLoaded = Boolean(riveParams);
|
||||
const options = getOptions(opts);
|
||||
@@ -103,8 +104,12 @@ export default function useRive(
|
||||
* @returns Dimensions object.
|
||||
*/
|
||||
function getCanvasDimensions() {
|
||||
const { width, height } =
|
||||
containerRef.current?.getBoundingClientRect() ?? new DOMRect(0, 0, 0, 0);
|
||||
// getBoundingClientRect returns the scaled width and height
|
||||
// this will result in double scaling
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements
|
||||
|
||||
const width = containerRef.current?.clientWidth ?? 0;
|
||||
const height = containerRef.current?.clientHeight ?? 0;
|
||||
|
||||
if (rive && options.fitCanvasToArtboardHeight) {
|
||||
const { maxY, maxX } = rive.bounds;
|
||||
@@ -155,26 +160,34 @@ export default function useRive(
|
||||
/**
|
||||
* Listen to changes on the windowSize and the rive file being loaded
|
||||
* and update the canvas bounds as needed.
|
||||
*
|
||||
* ie does not support ResizeObservers, so we fallback to the window listener there
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (rive) {
|
||||
updateBounds();
|
||||
}
|
||||
}, [rive, windowSize]);
|
||||
}, [rive, size]);
|
||||
|
||||
/**
|
||||
* Ref callback called when the canvas element mounts and unmounts.
|
||||
*/
|
||||
const setCanvasRef: RefCallback<HTMLCanvasElement> = useCallback(
|
||||
(canvas: HTMLCanvasElement | null) => {
|
||||
if (canvas && riveParams) {
|
||||
if (canvas && riveParams && isParamsLoaded) {
|
||||
const { useOffscreenRenderer } = options;
|
||||
const r = new Rive({
|
||||
useOffscreenRenderer,
|
||||
...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 (canvas === null && canvasRef.current) {
|
||||
canvasRef.current.height = 0;
|
||||
canvasRef.current.width = 0;
|
||||
@@ -184,7 +197,6 @@ export default function useRive(
|
||||
},
|
||||
[isParamsLoaded]
|
||||
);
|
||||
|
||||
/**
|
||||
* Ref callback called when the container element mounts
|
||||
*/
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
70
src/utils.ts
70
src/utils.ts
@@ -1,24 +1,80 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Dimensions } from './types';
|
||||
|
||||
export function useWindowSize() {
|
||||
const [windowSize, setWindowSize] = useState<Dimensions>({
|
||||
// There are polyfills for this, but they add hundreds of lines of code
|
||||
class FakeResizeObserver {
|
||||
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>
|
||||
) {
|
||||
const [size, setSize] = useState<Dimensions>({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
// internet explorer does not support ResizeObservers.
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const handleResize = () => {
|
||||
setWindowSize({
|
||||
setSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
};
|
||||
window.addEventListener('resize', handleResize);
|
||||
handleResize();
|
||||
|
||||
if (useWindowListener) {
|
||||
// only pay attention to window size changes when we do not have the resizeObserver (IE only)
|
||||
handleResize();
|
||||
window.addEventListener('resize', handleResize);
|
||||
}
|
||||
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}
|
||||
}, []);
|
||||
return windowSize;
|
||||
const observer = useRef(
|
||||
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 && useResizeObserver) {
|
||||
current.observe(containerRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
current.disconnect();
|
||||
if (containerRef.current && useResizeObserver) {
|
||||
current.unobserve(containerRef.current);
|
||||
}
|
||||
};
|
||||
}, [containerRef, observer]);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import RiveComponent from '../src/components/Rive';
|
||||
import {render} from '@testing-library/react'
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
jest.mock('@rive-app/canvas', () => ({
|
||||
Rive: jest.fn().mockImplementation(() => ({
|
||||
@@ -26,7 +26,13 @@ jest.mock('@rive-app/canvas', () => ({
|
||||
|
||||
describe('Rive Component', () => {
|
||||
it('renders the component as a canvas and a div wrapper', () => {
|
||||
const {container, getByLabelText} = render(<RiveComponent src="foo.riv" className="container-styles" aria-label="Foo label" />);
|
||||
const { container, getByLabelText } = render(
|
||||
<RiveComponent
|
||||
src="foo.riv"
|
||||
className="container-styles"
|
||||
aria-label="Foo label"
|
||||
/>
|
||||
);
|
||||
expect(container.firstChild).toHaveClass('container-styles');
|
||||
expect(getByLabelText('Foo label').tagName).toEqual('CANVAS');
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
@@ -100,7 +110,7 @@ describe('useRive', () => {
|
||||
const stopMock = jest.fn();
|
||||
|
||||
const riveMock = {
|
||||
on: (_: string, cb: () => void) => cb(),
|
||||
...baseRiveMock,
|
||||
stop: stopMock,
|
||||
};
|
||||
|
||||
@@ -112,6 +122,7 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
unmount();
|
||||
@@ -126,26 +137,20 @@ 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');
|
||||
containerSpy.getBoundingClientRect = jest.fn().mockImplementation(() => ({
|
||||
width: 100,
|
||||
height: 100,
|
||||
}));
|
||||
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();
|
||||
});
|
||||
|
||||
// Height and width should be 2* the width and height returned from containers
|
||||
@@ -166,26 +171,20 @@ 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');
|
||||
containerSpy.getBoundingClientRect = jest.fn().mockImplementation(() => ({
|
||||
width: 100,
|
||||
height: 100,
|
||||
}));
|
||||
jest.spyOn(containerSpy, 'clientWidth', 'get').mockReturnValue(100);
|
||||
jest.spyOn(containerSpy, 'clientHeight', 'get').mockReturnValue(100);
|
||||
|
||||
const { result } = renderHook(() => useRive(params, opts));
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
result.current.setContainerRef(containerSpy);
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
// Height and width should be same as containers bounding rect
|
||||
@@ -203,8 +202,7 @@ describe('useRive', () => {
|
||||
};
|
||||
|
||||
const riveMock = {
|
||||
on: (_: string, cb: () => void) => cb(),
|
||||
stop: jest.fn(),
|
||||
...baseRiveMock,
|
||||
bounds: {
|
||||
maxX: 100,
|
||||
maxY: 50,
|
||||
@@ -216,16 +214,15 @@ describe('useRive', () => {
|
||||
|
||||
const canvasSpy = document.createElement('canvas');
|
||||
const containerSpy = document.createElement('div');
|
||||
containerSpy.getBoundingClientRect = jest.fn().mockImplementation(() => ({
|
||||
width: 100,
|
||||
height: 100,
|
||||
}));
|
||||
jest.spyOn(containerSpy, 'clientWidth', 'get').mockReturnValue(100);
|
||||
jest.spyOn(containerSpy, 'clientHeight', 'get').mockReturnValue(100);
|
||||
|
||||
const { result } = renderHook(() => useRive(params, opts));
|
||||
|
||||
await act(async () => {
|
||||
result.current.setContainerRef(containerSpy);
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
// Height and width should be same as containers bounding rect
|
||||
@@ -249,8 +246,7 @@ describe('useRive', () => {
|
||||
}));
|
||||
|
||||
const riveMock = {
|
||||
on: (_: string, cb: () => void) => cb(),
|
||||
stop: jest.fn(),
|
||||
...baseRiveMock,
|
||||
bounds: {
|
||||
maxX: 100,
|
||||
maxY: 50,
|
||||
@@ -266,6 +262,7 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
expect(observeMock).toBeCalledWith(canvasSpy);
|
||||
@@ -283,7 +280,7 @@ describe('useRive', () => {
|
||||
const stopMock = jest.fn();
|
||||
|
||||
const riveMock = {
|
||||
on: (_: string, cb: () => void) => cb(),
|
||||
...baseRiveMock,
|
||||
stop: stopMock,
|
||||
play: playMock,
|
||||
animationNames: ['light'],
|
||||
@@ -301,6 +298,7 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
rerender({
|
||||
@@ -323,7 +321,7 @@ describe('useRive', () => {
|
||||
const stopMock = jest.fn();
|
||||
|
||||
const riveMock = {
|
||||
on: (_: string, cb: () => void) => cb(),
|
||||
...baseRiveMock,
|
||||
stop: stopMock,
|
||||
play: playMock,
|
||||
pause: pauseMock,
|
||||
@@ -343,6 +341,7 @@ describe('useRive', () => {
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
controlledRiveloadCb();
|
||||
});
|
||||
|
||||
rerender({
|
||||
@@ -360,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;
|
||||
|
||||
@@ -2,12 +2,13 @@ import { mocked } from 'jest-mock';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
import useStateMachineInput from '../src/hooks/useStateMachineInput';
|
||||
import {Rive, StateMachineInput} from '@rive-app/canvas';
|
||||
import { Rive, StateMachineInput } from '@rive-app/canvas';
|
||||
|
||||
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,29 +49,33 @@ describe('useStateMachineInput', () => {
|
||||
});
|
||||
|
||||
it('returns null if there is no state machine name', () => {
|
||||
const riveMock = {};
|
||||
mocked(Rive).mockImplementation(() => riveMock as Rive);
|
||||
const riveMock = getRiveMock();
|
||||
|
||||
const { result } = renderHook(() => useStateMachineInput(riveMock as Rive, '', 'testInput'));
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
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', ''));
|
||||
const { result } = renderHook(() =>
|
||||
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: [] });
|
||||
|
||||
const { result } = renderHook(() => useStateMachineInput(riveMock as Rive, 'smName', ''));
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock as Rive, 'smName', '')
|
||||
);
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
|
||||
@@ -63,13 +83,13 @@ 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] });
|
||||
|
||||
const { result } = renderHook(() => useStateMachineInput(riveMock as Rive, 'smName', 'numInput'));
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock, 'smName', 'numInput')
|
||||
);
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
|
||||
@@ -77,13 +97,13 @@ 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] });
|
||||
|
||||
const { result } = renderHook(() => useStateMachineInput(riveMock as Rive, 'smName', 'boolInput'));
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock, 'smName', 'boolInput')
|
||||
);
|
||||
expect(result.current).toBe(smInput);
|
||||
});
|
||||
|
||||
@@ -92,13 +112,12 @@ 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));
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock, 'smName', 'boolInput', true)
|
||||
);
|
||||
expect(result.current).toStrictEqual({
|
||||
...smInput,
|
||||
value: true,
|
||||
|
||||
Reference in New Issue
Block a user