mirror of
https://github.com/rive-app/rive-react.git
synced 2026-03-13 08:22:30 +08:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19092189a7 | ||
|
|
5ad5a957a6 | ||
|
|
414d6f895a | ||
|
|
38625a00c3 | ||
|
|
bad688dfa3 | ||
|
|
ec230faa73 | ||
|
|
90c6d1edb1 | ||
|
|
0480dc92c8 | ||
|
|
de24fa5641 | ||
|
|
1a49e89c33 | ||
|
|
805afd5dff | ||
|
|
84d9730767 | ||
|
|
9abee34d12 | ||
|
|
12801b10cc | ||
|
|
da11387558 | ||
|
|
3902948a2e | ||
|
|
21a17ed40e | ||
|
|
f0e70924ec | ||
|
|
f4eccbe2ce | ||
|
|
83c81b49c5 | ||
|
|
5de40fad5b | ||
|
|
aab811b975 | ||
|
|
965507ac34 | ||
|
|
74b791ffa4 | ||
|
|
2853851d52 | ||
|
|
4f37bbdb76 | ||
|
|
39f1990eac | ||
|
|
bb7d3e4e6c | ||
|
|
7f2bd76eb3 | ||
|
|
5c0b9cd613 | ||
|
|
92dd811948 | ||
|
|
4429be44f2 | ||
|
|
9398e0d74e | ||
|
|
563dca3608 | ||
|
|
1da73aac05 | ||
|
|
5c4336b84f |
22
.eslintrc.js
22
.eslintrc.js
@@ -1,27 +1,29 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
es2021: true
|
||||
},
|
||||
extends: ['plugin:react/recommended', 'prettier'],
|
||||
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'],
|
||||
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']
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
};
|
||||
version: 'detect'
|
||||
}
|
||||
}
|
||||
};
|
||||
52
.github/workflows/publish.yml
vendored
52
.github/workflows/publish.yml
vendored
@@ -5,8 +5,37 @@ on:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
merge_job:
|
||||
determine_version:
|
||||
name: Determine the next build version
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.echo_version.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
working-directory: ./
|
||||
- name: Git config
|
||||
run: |
|
||||
git config --local user.email 'hello@rive.app'
|
||||
git config --local user.name ${{ github.actor }}
|
||||
- name: Authenticate with registry
|
||||
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
|
||||
- id: determine_version
|
||||
name: Get Version
|
||||
run: npm run release -- --ci --release-version | tail -n 1 > RELEASE_VERSION
|
||||
working-directory: ./
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
|
||||
- id: echo_version
|
||||
run: echo "::set-output name=version::$(cat ./RELEASE_VERSION)"
|
||||
|
||||
merge_job:
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
needs: [determine_version]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
@@ -21,15 +50,30 @@ jobs:
|
||||
run: npm run lint
|
||||
- name: Run Tests
|
||||
run: npm test
|
||||
- name: Build
|
||||
- name: Build main dist
|
||||
run: npm run build
|
||||
- name: Copy separated builds
|
||||
run: ./scripts/build.sh
|
||||
- name: Copy package jsons to separate react outputs
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: ./scripts/setup_all_packages.sh
|
||||
- name: Bump Versions of react outputs
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
RELEASE_VERSION: ${{ needs.determine_version.outputs.version }}
|
||||
run: ./scripts/bump_all_versions.sh
|
||||
- name: Git config
|
||||
run: |
|
||||
git config --local user.email 'hello@rive.app'
|
||||
git config --local user.name ${{ github.actor }}
|
||||
- name: Authenticate with registry
|
||||
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
|
||||
- name: Release
|
||||
- name: Release rive-react
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
|
||||
run: npm run release
|
||||
run: npm run release -- --ci
|
||||
- name: Release @rive-app/react-* variants
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
|
||||
run: ./scripts/publish_all.sh
|
||||
|
||||
28
.github/workflows/storybook.yml
vendored
Normal file
28
.github/workflows/storybook.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
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
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install and Build 🔧
|
||||
run: | # Install npm packages and build the Storybook files
|
||||
npm install
|
||||
npm run build-storybook
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@3.6.2
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BRANCH: main # The branch the action should deploy to.
|
||||
FOLDER: docs-build # The folder that the build-storybook script generates files.
|
||||
CLEAN: true # Automatically remove deleted files from the deploy branch
|
||||
TARGET_FOLDER: docs # The folder that we serve our Storybook files from
|
||||
13
.storybook/main.js
Normal file
13
.storybook/main.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
"stories": [
|
||||
"../examples/stories/*.stories.mdx",
|
||||
"../examples/stories/*.stories.@(js|jsx|ts|tsx)"
|
||||
],
|
||||
"addons": [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions"
|
||||
],
|
||||
"framework": "@storybook/react",
|
||||
staticDirs: ['../examples/stories/assets'],
|
||||
}
|
||||
17
.storybook/preview-head.html
Normal file
17
.storybook/preview-head.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
data-href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
data-href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap"
|
||||
/>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
39
.storybook/preview.js
Normal file
39
.storybook/preview.js
Normal file
@@ -0,0 +1,39 @@
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
options: {
|
||||
storySort: {
|
||||
method: 'alphabetical',
|
||||
order: ['Overview', 'Playback Controls', 'State Machines'],
|
||||
},
|
||||
},
|
||||
viewMode: 'docs',
|
||||
};
|
||||
|
||||
// The below function helps to default to the docs page, which contains all the documentation and examples
|
||||
function clickDocsButtonOnFirstLoad() {
|
||||
window.removeEventListener("load", clickDocsButtonOnFirstLoad);
|
||||
|
||||
try {
|
||||
const docsButtonSelector = window.parent.document.evaluate(
|
||||
"//button[contains(., 'Docs')]",
|
||||
window.parent.document,
|
||||
null,
|
||||
XPathResult.ANY_TYPE,
|
||||
null
|
||||
);
|
||||
|
||||
const button = docsButtonSelector.iterateNext();
|
||||
|
||||
button.click();
|
||||
} catch (error) {
|
||||
// Do nothing if it wasn't able to click on Docs button.
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", clickDocsButtonOnFirstLoad);
|
||||
29
CHANGELOG.md
29
CHANGELOG.md
@@ -4,8 +4,37 @@ 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.9](https://github.com/rive-app/rive-react/compare/v3.0.1...v3.0.9)
|
||||
|
||||
- Maint: Update docs for storybook link [`5ad5a95`](https://github.com/rive-app/rive-react/commit/5ad5a957a6e8f10abedc23f46033d4792e29dfe5)
|
||||
|
||||
#### [v3.0.1](https://github.com/rive-app/rive-react/compare/v3.0.0...v3.0.1)
|
||||
|
||||
> 26 April 2022
|
||||
|
||||
- Maint: Bump rive wasm to include fps counter API and take off major tag [`83c81b4`](https://github.com/rive-app/rive-react/commit/83c81b49c5a53c5ec9092b83607a78b6e4a69768)
|
||||
- chore: release 3.0.1 [`f4eccbe`](https://github.com/rive-app/rive-react/commit/f4eccbe2ce8795f55a6bed7d8b17c2ac59c94c30)
|
||||
|
||||
### [v3.0.0](https://github.com/rive-app/rive-react/compare/v2.0.0...v3.0.0)
|
||||
|
||||
> 26 April 2022
|
||||
|
||||
- Fix animation playing with autoplay false when animation name passed [`aab811b`](https://github.com/rive-app/rive-react/commit/aab811b975d8832c2be14779675392a17b1db5ff)
|
||||
- chore: release 3.0.0 [`5de40fa`](https://github.com/rive-app/rive-react/commit/5de40fad5b716be7dcec59c15422e9d41a4962b1)
|
||||
|
||||
### [v2.0.0](https://github.com/rive-app/rive-react/compare/v1.0.7...v2.0.0)
|
||||
|
||||
> 22 April 2022
|
||||
|
||||
- Breaking: Spread non-style props onto canvas element instead of containing div [`5c0b9cd`](https://github.com/rive-app/rive-react/commit/5c0b9cd613873ee565b4ec40d71042c3850a502c)
|
||||
- Update: Adding build scripts to prepare for split of rive-react into 2 package derivatives for canvas and webgl [`5c4336b`](https://github.com/rive-app/rive-react/commit/5c4336b84fe55afea1ec9d5e42f530cf29d6d176)
|
||||
- Change rive-react-* references to be the shortened namespace-named convention [`4429be4`](https://github.com/rive-app/rive-react/commit/4429be44f2af2b91d9d5e26a65b70e59d9647979)
|
||||
|
||||
#### [v1.0.7](https://github.com/rive-app/rive-react/compare/v1.0.6...v1.0.7)
|
||||
|
||||
> 20 April 2022
|
||||
|
||||
- chore: release 1.0.7 [`6e72ed5`](https://github.com/rive-app/rive-react/commit/6e72ed5271976e886a576f5a6c0d7162eedc81d8)
|
||||
- Maint: Allow peer dep React to use v18.0+ [`464fa97`](https://github.com/rive-app/rive-react/commit/464fa97c207e780d31866ecad0437dc16aefd1f2)
|
||||
|
||||
#### [v1.0.6](https://github.com/rive-app/rive-react/compare/v1.0.5...v1.0.6)
|
||||
|
||||
84
README.md
84
README.md
@@ -22,35 +22,11 @@ npm i --save rive-react
|
||||
|
||||
_Note: This library is using React hooks so the minimum version required for both react and react-dom is 16.8.0._
|
||||
|
||||
### Migrating from v 0.0.x to 1.x.x
|
||||
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
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:
|
||||
|
||||
```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,
|
||||
});
|
||||
|
||||
// or you can override the flag in JSX via props
|
||||
return (
|
||||
<Rive src="foo.riv" useOffscreenRenderer={false} />
|
||||
);
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Component
|
||||
|
||||
Rive React provides a basic component as it's default import for displaying simple animations.
|
||||
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-react';
|
||||
@@ -67,8 +43,19 @@ export default Example;
|
||||
- `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 `div` element can also be passed to the `Rive` component and used in the same manner._
|
||||
- _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
|
||||
|
||||
@@ -102,7 +89,7 @@ export default Example;
|
||||
|
||||
#### 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 `div` element.
|
||||
- `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.
|
||||
@@ -177,4 +164,45 @@ A Rive.js `stateMachineInput` object.
|
||||
|
||||
## Examples
|
||||
|
||||
The [examples](examples) shows a number of different ways to use Rive React. See the instructions for each example to run locally.
|
||||
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`.
|
||||
|
||||
## Migration notes
|
||||
|
||||
### Migrating from version 0.0.x to 1.x.x
|
||||
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
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:
|
||||
|
||||
```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,
|
||||
});
|
||||
|
||||
// or you can override the flag in JSX via props
|
||||
return (
|
||||
<Rive src="foo.riv" useOffscreenRenderer={false} />
|
||||
);
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
0
docs/.gitignore
vendored
Normal file
0
docs/.gitignore
vendored
Normal file
23
examples/basic-typescript/.gitignore
vendored
23
examples/basic-typescript/.gitignore
vendored
@@ -1,23 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -1,14 +0,0 @@
|
||||
# Basic Typescript
|
||||
|
||||
This is an example app that uses the useRive hook with typescript.
|
||||
|
||||
## To Run
|
||||
|
||||
This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).
|
||||
|
||||
To install and run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "basic-typescript",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/node": "^12.20.14",
|
||||
"@types/react": "^17.0.9",
|
||||
"@types/react-dom": "^17.0.6",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"rive-react": "latest",
|
||||
"typescript": "^4.3.2",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "SKIP_PREFLIGHT_CHECK=true react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>React App - Basic Typescript</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
import { useRive, UseRiveParameters } from 'rive-react';
|
||||
|
||||
function App() {
|
||||
const params: UseRiveParameters = {
|
||||
src: 'poison-loader.riv',
|
||||
autoplay: true,
|
||||
};
|
||||
|
||||
const { RiveComponent } = useRive(params);
|
||||
|
||||
return (
|
||||
// The animation will fit to the parent element.
|
||||
<div style={{ height: '500px', width: '500px' }}>
|
||||
<RiveComponent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
/// <reference types="react-scripts" />
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
# Basic with hook
|
||||
|
||||
This is a very basic example of using the useRive to autoplay a simple looping animation.
|
||||
|
||||
## To Run
|
||||
|
||||
This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).
|
||||
|
||||
To install and run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "basic-with-hook",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"rive-react": "latest",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "SKIP_PREFLIGHT_CHECK=true react-scripts start"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>Rive React - Basic with Hook</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
import { useRive } from 'rive-react';
|
||||
|
||||
function App() {
|
||||
const params = {
|
||||
src: 'poison-loader.riv',
|
||||
autoplay: true,
|
||||
};
|
||||
|
||||
const { RiveComponent } = useRive(params);
|
||||
|
||||
return (
|
||||
// The animation will fit to the parent element.
|
||||
<div style={{ height: '500px', width: '500px' }}>
|
||||
<RiveComponent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
# Basic
|
||||
|
||||
This is a very basic example of using the Rive component to autoplay a simple looping animation.
|
||||
|
||||
## To Run
|
||||
|
||||
This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).
|
||||
|
||||
To install and run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "basic-example",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"react": "file:../../node_modules/react",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"rive-react": "file:../..",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "SKIP_PREFLIGHT_CHECK=true react-scripts start"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>Rive React - Basic</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -1,12 +0,0 @@
|
||||
import Rive from 'rive-react';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
// The animation will fit to the parent element.
|
||||
<div style={{ height: '500px', width: '500px' }}>
|
||||
<Rive src="poison-loader.riv" autoplay />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
# Play on hover
|
||||
|
||||
This provides an example of how to play/pause a Rive animation when the mouse is hovered over it.
|
||||
|
||||
## To Run
|
||||
|
||||
This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).
|
||||
|
||||
To install and run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "play-on-hover",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"rive-react": "latest",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "SKIP_PREFLIGHT_CHECK=true react-scripts start"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>Rive React - Play on hover</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -1,32 +0,0 @@
|
||||
import { useRive } from 'rive-react';
|
||||
|
||||
function App() {
|
||||
const params = {
|
||||
src: 'poison-loader.riv',
|
||||
autoplay: false,
|
||||
};
|
||||
|
||||
const { RiveComponent, rive } = useRive(params);
|
||||
|
||||
function onMouseEnter() {
|
||||
// `rive` will return as null until the file as fully loaded, so we include this
|
||||
// guard to prevent any unwanted errors.
|
||||
if (rive) {
|
||||
rive.play();
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
if (rive) {
|
||||
rive.pause();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ height: '600px', width: '600px' }}>
|
||||
<RiveComponent onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
# Play/Pause Buttons
|
||||
|
||||
This example shows how we can play/pause the Rive animation on a button click and update the text of the button based on events happening in the runtime.
|
||||
|
||||
## To Run
|
||||
|
||||
This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).
|
||||
|
||||
To install and run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "play-pause-button",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"rive-react": "latest",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "SKIP_PREFLIGHT_CHECK=true react-scripts start"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>Rive React - Play/Pause Button</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -1,50 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRive } from 'rive-react';
|
||||
|
||||
function App() {
|
||||
const [buttonText, setButtonText] = useState('Pause');
|
||||
const { RiveComponent, rive } = useRive({
|
||||
src: 'poison-loader.riv',
|
||||
autoplay: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (rive) {
|
||||
// "play" event is fired when the animation starts to play, so we update
|
||||
// button text on this event.
|
||||
rive.on('play', () => {
|
||||
setButtonText('Pause');
|
||||
});
|
||||
|
||||
// As above, the "pause" event is fired when the animation pauses.
|
||||
rive.on('pause', () => {
|
||||
setButtonText('Play');
|
||||
});
|
||||
}
|
||||
// We listen for changes to the rive object. The rive object will be null
|
||||
// until the rive file has loaded.
|
||||
}, [rive]);
|
||||
|
||||
function onButtonClick() {
|
||||
// `rive` will return as null until the file as fully loaded, so we include this
|
||||
// guard to prevent any unwanted errors.
|
||||
if (rive) {
|
||||
if (rive.isPlaying) {
|
||||
rive.pause();
|
||||
} else {
|
||||
rive.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
// The animation will fit to the parent element, so we set a large height
|
||||
// and width for this example.
|
||||
<div style={{ height: '500px', width: '500px' }}>
|
||||
<RiveComponent />
|
||||
<button onClick={onButtonClick}>{buttonText}</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
# State Machine Boolean Input
|
||||
|
||||
This example shows how to interact with a state machine, using various onMouse\* callbacks to trigger transations.
|
||||
|
||||
## To Run
|
||||
|
||||
This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).
|
||||
|
||||
To install and run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "state-machine-boolean-input",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"rive-react": "latest",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "SKIP_PREFLIGHT_CHECK=true react-scripts start"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>Rive React - State Machine Boolean Input</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,58 +0,0 @@
|
||||
import { useRive, useStateMachineInput } from 'rive-react';
|
||||
|
||||
function App() {
|
||||
const STATE_MACHINE_NAME = 'State Machine 1';
|
||||
const ON_HOVER_INPUT_NAME = 'Hover';
|
||||
const ON_PRESSED_INPUT_NAME = 'Pressed';
|
||||
|
||||
const { RiveComponent, rive } = useRive({
|
||||
src: 'like.riv',
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
artboard: 'New Artboard',
|
||||
autoplay: true,
|
||||
});
|
||||
|
||||
// Both onHoverInput and onPressedInput are boolean inputs. To transition
|
||||
// states we need to set the value property to true or false.
|
||||
const onHoverInput = useStateMachineInput(
|
||||
rive,
|
||||
STATE_MACHINE_NAME,
|
||||
ON_HOVER_INPUT_NAME
|
||||
);
|
||||
const onPressedInput = useStateMachineInput(
|
||||
rive,
|
||||
STATE_MACHINE_NAME,
|
||||
ON_PRESSED_INPUT_NAME
|
||||
);
|
||||
|
||||
function onMouseEnter() {
|
||||
onHoverInput.value = true;
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
onHoverInput.value = false;
|
||||
}
|
||||
|
||||
function onMouseDown() {
|
||||
onPressedInput.value = true;
|
||||
}
|
||||
|
||||
function onMouseUp() {
|
||||
onPressedInput.value = false;
|
||||
}
|
||||
|
||||
return (
|
||||
// The animation will fit to the parent element, so we set a large height
|
||||
// and width for this example.
|
||||
<div style={{ height: '500px', width: '500px' }}>
|
||||
<RiveComponent
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseUp={onMouseUp}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
# State Machine Number Input
|
||||
|
||||
This example shows how to interact with a state machine with number inputs. We trigger the transistions by changing the value of a number input.
|
||||
|
||||
## To Run
|
||||
|
||||
This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).
|
||||
|
||||
To install and run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "state-machine-number-input",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"rive-react": "latest",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "SKIP_PREFLIGHT_CHECK=true react-scripts start"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>Rive React - State Machine Number Input</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,34 +0,0 @@
|
||||
import { useRive, useStateMachineInput } from 'rive-react';
|
||||
|
||||
function App() {
|
||||
const STATE_MACHINE_NAME = 'State Machine ';
|
||||
const INPUT_NAME = 'Level';
|
||||
|
||||
const { RiveComponent, rive } = useRive({
|
||||
src: 'skills.riv',
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
artboard: 'New Artboard',
|
||||
autoplay: true,
|
||||
});
|
||||
|
||||
// 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);
|
||||
|
||||
return (
|
||||
// The animation will fit to the parent element, so we set a large height
|
||||
// and width for this example.
|
||||
<div style={{ height: '500px', width: '500px' }}>
|
||||
<RiveComponent />
|
||||
<div>
|
||||
Level:
|
||||
<button onClick={() => (levelInput.value = 0)}>0</button>
|
||||
<button onClick={() => (levelInput.value = 1)}>1</button>
|
||||
<button onClick={() => (levelInput.value = 2)}>2</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
# State Machine Trigger Input
|
||||
|
||||
This example shows how to interact with a state machine with a trigger input. We call the `fire()` function on the state machine input to transition the state when the animation is clicked.
|
||||
|
||||
## To Run
|
||||
|
||||
This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).
|
||||
|
||||
To install and run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "state-machine-trigger-input",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"rive-react": "latest",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "SKIP_PREFLIGHT_CHECK=true react-scripts start"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>Rive React - State Machine Trigger Input</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,31 +0,0 @@
|
||||
import { useRive, useStateMachineInput } from 'rive-react';
|
||||
|
||||
function App() {
|
||||
const STATE_MACHINE_NAME = 'State Machine 1';
|
||||
const INPUT_NAME = 'Pressed';
|
||||
|
||||
const { RiveComponent, rive } = useRive({
|
||||
src: 'piggy-bank.riv',
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
artboard: 'New Artboard',
|
||||
autoplay: true,
|
||||
});
|
||||
|
||||
// pressedInput in a trigger state machine input. To transition the state
|
||||
// we need to call the `fire()` method on the input.
|
||||
const pressedInput = 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.
|
||||
<div style={{ height: '500px', width: '500px' }}>
|
||||
<RiveComponent onClick={() => pressedInput.fire()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
99
examples/stories/AnimationPlayback.stories.mdx
Normal file
99
examples/stories/AnimationPlayback.stories.mdx
Normal file
@@ -0,0 +1,99 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
|
||||
|
||||
import {useRive, useStateMachineInput} from '../../src';
|
||||
import {Button} from './components/Button';
|
||||
import './rive-overview.css';
|
||||
|
||||
<Meta title="React Runtime/Playback Controls" />
|
||||
|
||||
# Animation Playback
|
||||
|
||||
When rendering Rives, you may want to control animation playback for certain scenarios. Animation playback allows you to programatically pause, stop, play, reset, and scrub animations as needed. You may find this useful for coordinating certain user interaction or other programatic cases to Rive animations.
|
||||
|
||||
**Note:** Just like the Rive web runtime, you invoke playback methods on a `rive` instance. Because of this, you will need to use the `useRive` method to render Rives to your React applications, as it returns a `rive` instance for you to invoke controls on.
|
||||
|
||||
## User event-driven playback
|
||||
|
||||
You can control Rive animation playback with user interaction directly on the canvas, or even outside the canvas, as you'll see below.
|
||||
|
||||
### Play/pause with hover
|
||||
|
||||
The example below shows how to start with a Rive instance that does not autoplay initially, but plays whenever the cursor is hovered over the canvas, and returns to a paused state when the mouse leaves the canvas.
|
||||
|
||||
<Canvas withSource="open">
|
||||
<Story name="Play/pause with hover">
|
||||
{() => {
|
||||
const { rive, RiveComponent } = useRive({
|
||||
src: 'poison-loader.riv',
|
||||
autoplay: false,
|
||||
});
|
||||
function onMouseEnter() {
|
||||
// rive will return as null until the file as fully loaded, so we include this
|
||||
// guard to prevent any unwanted errors.
|
||||
if (rive) {
|
||||
rive.play();
|
||||
}
|
||||
}
|
||||
function onMouseLeave() {
|
||||
if (rive) {
|
||||
rive.pause();
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="center">
|
||||
<RiveComponent
|
||||
className="base-canvas-size"
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
## Play/pause with external elements
|
||||
|
||||
This example shows how you can control Rive elements via user interaction outside of the canvas, such as other buttons. Here, the play/pause button will toggle whether or not to play or pause the Rive animation.
|
||||
|
||||
<Canvas withSource="open">
|
||||
<Story name="Play/pause with external elements">
|
||||
{() => {
|
||||
const [isPlaying, setIsPlaying] = useState(true);
|
||||
const { rive, RiveComponent } = useRive({
|
||||
src: 'truck.riv',
|
||||
stateMachines: "drive",
|
||||
artboard: 'Truck',
|
||||
autoplay: true,
|
||||
});
|
||||
const togglePlaying = () => {
|
||||
if (isPlaying) {
|
||||
rive.pause();
|
||||
setIsPlaying(false);
|
||||
} else {
|
||||
rive.play();
|
||||
setIsPlaying(true);
|
||||
}
|
||||
};
|
||||
return ((
|
||||
<>
|
||||
<div className="center">
|
||||
<RiveComponent className="base-canvas-size" />
|
||||
<Button onClick={togglePlaying}>{isPlaying ? 'Pause' : 'Play'}</Button>
|
||||
</div>
|
||||
</>
|
||||
));
|
||||
}}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
## Additional ways to control playback
|
||||
|
||||
While user interaction is a common way to control animation playback for Rives, there are other ways to achieve the same means as well.
|
||||
|
||||
### API-driven playback
|
||||
|
||||
Another common way to control animation playback is through API responses. Imagine an API that you poll for loading progress of a task. Based on that progress response, you may play or stop a given animation. As long as you hold a reference to the `rive` instance returned from the `useRive` hook, you can invoke control methods on that instance in places such as callbacks from API responses.
|
||||
|
||||
112
examples/stories/RiveOverview.stories.mdx
Normal file
112
examples/stories/RiveOverview.stories.mdx
Normal file
@@ -0,0 +1,112 @@
|
||||
<!-- RiveTestHook.stories.mdx -->
|
||||
|
||||
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 './rive-overview.css';
|
||||
|
||||
<Meta title="React Runtime/Overview" />
|
||||
|
||||
# Rive React Runtime
|
||||
|
||||
This is an examples/docs page for the official React runtime for Rive. Check out the various pages for how to use this SDK to incorporate Rive into your React-based web applications.
|
||||
|
||||
Want to follow along with these examples? Check out the `examples/` folder in the [rive-react](https://github.com/rive-app/rive-react) project to find all these examples and `.riv` assets you can inspect in the Rive editor by dragging and dropping them into your Rive editor file browser.
|
||||
|
||||
## What is Rive?
|
||||
|
||||
<img className="rive-logo" src="rive_logo_black_2x.png" alt="Rive logo" />
|
||||
|
||||
[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.
|
||||
|
||||
## How to use Rive at runtime
|
||||
|
||||
There's multiple ways to render Rive using the React runtime. See the associated code snippets that follow each example.
|
||||
|
||||
### Rive component
|
||||
|
||||
```tsx
|
||||
import RiveComponent from '@rive-app/react-canvas';
|
||||
```
|
||||
The React runtime exports a default React component you can insert as JSX. Under the hood, it renders a `<canvas>` element that runs the animation, and a wrapping `<div>` element that handles sizing of the canvas based on the parent that wraps the component.
|
||||
|
||||
**When to use this**: Use this for simple rendering cases where you don't need to control playback or setup state machine inputs to advance state machines. It will simply autoplay the first animation it finds in the `.riv`, the animation name you provide it, or the state machine name if you provide one.
|
||||
|
||||
**Note:** Style-specific props set onto the component will pass down to the wrapping `<div>` element, while most other props will be set onto the `<canvas>` element itself.
|
||||
|
||||
<Canvas withSource="open">
|
||||
<Story name="Rive Component">
|
||||
{() => (
|
||||
<div className="center">
|
||||
<RiveComponent src="poison-loader.riv" className="base-canvas-size" />
|
||||
</div>
|
||||
)}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
#### Props
|
||||
|
||||
In addition to the props laid out below, the component accepts other props that can be set on the `<canvas>` element.
|
||||
|
||||
<ArgsTable of={RiveComponent} />
|
||||
|
||||
### useRive Hook
|
||||
|
||||
```tsx
|
||||
import {useRive} from '@rive-app/react-canvas';
|
||||
```
|
||||
|
||||
The runtime also exports a named `useRive` hook that allows for more control at Rive instantiation, since it passes back a `rive` object you can use to manipulate state machines, control playback, and more.
|
||||
|
||||
**When to use this:** When you need to control your Rive animation in any aspect, such as controlling playback, using state machine inputs to advance state machines, add adding callbacks on certain Rive-specific events such as `onStateChange`, `onPause`, etc.
|
||||
<Canvas withSource="open">
|
||||
<Story name="useRive Hook">
|
||||
{() => {
|
||||
const [isPlaying, setIsPlaying] = useState(true);
|
||||
const [animationText, setAnimationText] = useState('');
|
||||
const { rive, RiveComponent: RiveComponentPlayback } = useRive({
|
||||
src: 'truck.riv',
|
||||
stateMachines: "drive",
|
||||
artboard: 'Truck',
|
||||
autoplay: true,
|
||||
onPause: () => {
|
||||
setAnimationText('Animation paused!');
|
||||
},
|
||||
onPlay: () => {
|
||||
setAnimationText('Animation is playing..');
|
||||
},
|
||||
});
|
||||
const togglePlaying = () => {
|
||||
if (isPlaying) {
|
||||
rive.pause();
|
||||
setIsPlaying(false);
|
||||
} else {
|
||||
rive.play();
|
||||
setIsPlaying(true);
|
||||
}
|
||||
};
|
||||
return ((
|
||||
<>
|
||||
<div className="center">
|
||||
<RiveComponentPlayback className="base-canvas-size" />
|
||||
<p>{animationText}</p>
|
||||
<Button onClick={togglePlaying}>{isPlaying ? 'Pause' : 'Play'}</Button>
|
||||
</div>
|
||||
</>
|
||||
));
|
||||
}}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
#### useRive parameters
|
||||
|
||||
```tsx
|
||||
useRive(params: UseRiveParameters, opts?: UseRiveOptions);
|
||||
```
|
||||
|
||||
The parameters available to set on `useRive` can be found [here](https://github.com/rive-app/rive-wasm/blob/master/js/src/rive.ts#L843). These pass down to the web runtime `rive` instance created.
|
||||
|
||||
Additionally, there are other options to set on `useRive` that can be found [here](https://github.com/rive-app/rive-react/blob/main/src/types.ts#L6).
|
||||
157
examples/stories/StateMachineDocs.stories.mdx
Normal file
157
examples/stories/StateMachineDocs.stories.mdx
Normal file
@@ -0,0 +1,157 @@
|
||||
<!-- RiveTestHook.stories.mdx -->
|
||||
|
||||
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 './rive-overview.css';
|
||||
|
||||
<Meta title="React Runtime/State Machines" />
|
||||
|
||||
# State Machine Usage
|
||||
|
||||
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
|
||||
|
||||
## Boolean inputs
|
||||
|
||||
Once you grab a reference to the state machine input, you can get/set the value of the input via the `.value` property.
|
||||
|
||||
**Note:** The input instance value itself is not a stateful React variable, therefore, any logic in the component dependent on an input value changing will not trigger a re-render like a React stateful variable change would. You can achieve this effect by keeping reference to the state machine input value inside a React state variable. See the example below for this pattern.
|
||||
|
||||
<Canvas withSource="open">
|
||||
<Story name="Boolean input">
|
||||
{() => {
|
||||
const STATE_MACHINE_NAME = 'State Machine 1';
|
||||
const ON_HOVER_INPUT_NAME = 'Hover';
|
||||
const ON_PRESSED_INPUT_NAME = 'Pressed';
|
||||
const { rive, RiveComponent: RiveComponentTouch } = useRive({
|
||||
src: 'like.riv',
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
artboard: 'New Artboard',
|
||||
autoplay: true,
|
||||
});
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
// Both onHoverInput and onPressedInput are boolean inputs. To transition
|
||||
// states we need to set the value property to true or false.
|
||||
const onPressedInput = useStateMachineInput(
|
||||
rive,
|
||||
STATE_MACHINE_NAME,
|
||||
ON_PRESSED_INPUT_NAME
|
||||
);
|
||||
const onHoverInput = useStateMachineInput(
|
||||
rive,
|
||||
STATE_MACHINE_NAME,
|
||||
ON_HOVER_INPUT_NAME
|
||||
);
|
||||
function onMouseDown() {
|
||||
onPressedInput.value = true;
|
||||
}
|
||||
function onMouseUp() {
|
||||
onPressedInput.value = false;
|
||||
}
|
||||
function onMouseEnter() {
|
||||
onHoverInput.value = true;
|
||||
setIsHovered(true);
|
||||
}
|
||||
function onMouseLeave() {
|
||||
onHoverInput.value = false;
|
||||
setIsHovered(false);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="center">
|
||||
<RiveComponentTouch
|
||||
className="base-canvas-size"
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseUp={onMouseUp}
|
||||
/>
|
||||
<p>Hover and click on the canvas</p>
|
||||
<p>Is cursor hovering? {isHovered ? 'Yes' : 'No'}</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
## Number inputs
|
||||
|
||||
Once you grab a reference to the state machine input, you can get/set the value of the input via the `.value` property.
|
||||
|
||||
<Canvas withSource="open">
|
||||
<Story name="Number input">
|
||||
{() => {
|
||||
const STATE_MACHINE_NAME = 'State Machine ';
|
||||
const INPUT_NAME = 'Level';
|
||||
const { rive, RiveComponent: RiveComponentTouch } = useRive({
|
||||
src: 'skills.riv',
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
artboard: 'New Artboard',
|
||||
autoplay: true,
|
||||
});
|
||||
// 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);
|
||||
return (
|
||||
// The animation will fit to the parent element, so we set a large height
|
||||
// and width for this example.
|
||||
<div className="center">
|
||||
<RiveComponentTouch className="large-canvas-size" />
|
||||
<div className="btn-group">
|
||||
Choose a level:
|
||||
<Button onClick={() => (levelInput.value = 0)}>0</Button>
|
||||
<Button onClick={() => (levelInput.value = 1)}>1</Button>
|
||||
<Button onClick={() => (levelInput.value = 2)}>2</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
## Trigger inputs
|
||||
|
||||
Unlike the boolean and number inputs, you invoke the `.fire()` method on a trigger input.
|
||||
|
||||
<Canvas withSource="open">
|
||||
<Story name="Trigger input">
|
||||
{() => {
|
||||
const STATE_MACHINE_NAME = 'State Machine 1';
|
||||
const INPUT_NAME = 'Pressed';
|
||||
const { rive, RiveComponent: RiveComponentTouch } = useRive({
|
||||
src: 'piggy-bank.riv',
|
||||
stateMachines: STATE_MACHINE_NAME,
|
||||
artboard: 'New Artboard',
|
||||
autoplay: true,
|
||||
});
|
||||
// pressedInput in a trigger state machine input. To transition the state
|
||||
// we need to call the `fire()` method on the input.
|
||||
const pressedInput = useStateMachineInput(
|
||||
rive,
|
||||
STATE_MACHINE_NAME,
|
||||
INPUT_NAME
|
||||
);
|
||||
// The animation will fit to the parent element, so we set a large height
|
||||
// and width for this example.
|
||||
return (
|
||||
<div className="center">
|
||||
<RiveComponentTouch
|
||||
className="base-canvas-size"
|
||||
onClick={() => pressedInput.fire()}
|
||||
/>
|
||||
<p>Click on the canvas</p>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Story>
|
||||
</Canvas>
|
||||
BIN
examples/stories/assets/rive_logo_black_2x.png
Normal file
BIN
examples/stories/assets/rive_logo_black_2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
examples/stories/assets/truck.riv
Normal file
BIN
examples/stories/assets/truck.riv
Normal file
Binary file not shown.
7
examples/stories/components/Button.tsx
Normal file
7
examples/stories/components/Button.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
export const Button = ({onClick, children}) => {
|
||||
return (
|
||||
<button className="btn" onClick={onClick}>{children}</button>
|
||||
);
|
||||
};
|
||||
36
examples/stories/rive-overview.css
Normal file
36
examples/stories/rive-overview.css
Normal file
@@ -0,0 +1,36 @@
|
||||
.center {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.base-canvas-size {
|
||||
height: 300px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.large-canvas-size {
|
||||
height: 600px;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
background: #0069ed;
|
||||
border-radius: 2px;
|
||||
height: 32px;
|
||||
margin-top: 16px;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.rive-logo {
|
||||
display: flex;
|
||||
margin: 16px auto;
|
||||
}
|
||||
3
npm/react-canvas/README.md
Normal file
3
npm/react-canvas/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# @rive-app/react-canvas
|
||||
|
||||
Output for `rive-react` using the backing `@rive-app/canvas` JS runtime
|
||||
3
npm/react-webgl/README.md
Normal file
3
npm/react-webgl/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# @rive-app/react-webgl
|
||||
|
||||
Output for `rive-react` using the backing `@rive-app/webgl` JS runtime
|
||||
22
package.json
22
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rive-react",
|
||||
"version": "1.0.7",
|
||||
"version": "3.0.9",
|
||||
"description": "React wrapper around the rive-js library",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/types/index.d.ts",
|
||||
@@ -14,7 +14,9 @@
|
||||
"lint": "eslint -c .eslintrc.js 'src/**/*{.ts,.tsx}'",
|
||||
"format": "prettier --write src",
|
||||
"types:check": "tsc --noEmit",
|
||||
"release": "release-it"
|
||||
"release": "release-it",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook -o docs-build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -27,12 +29,22 @@
|
||||
},
|
||||
"homepage": "https://github.com/rive-app/rive-react#readme",
|
||||
"dependencies": {
|
||||
"@rive-app/webgl": "1.0.39"
|
||||
"@rive-app/canvas": "1.0.65",
|
||||
"@rive-app/webgl": "1.0.62"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.0",
|
||||
"@storybook/addon-actions": "^6.5.3",
|
||||
"@storybook/addon-essentials": "^6.5.3",
|
||||
"@storybook/addon-interactions": "^6.5.3",
|
||||
"@storybook/addon-links": "^6.5.3",
|
||||
"@storybook/builder-webpack4": "^6.5.3",
|
||||
"@storybook/manager-webpack4": "^6.5.3",
|
||||
"@storybook/react": "^6.5.3",
|
||||
"@storybook/testing-library": "^0.0.11",
|
||||
"@testing-library/jest-dom": "^5.13.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/react-hooks": "^7.0.0",
|
||||
@@ -43,7 +55,8 @@
|
||||
"@typescript-eslint/eslint-plugin": "^5.7.0",
|
||||
"@typescript-eslint/parser": "^5.7.0",
|
||||
"auto-changelog": "^2.3.0",
|
||||
"bunchee": "^1.7.3",
|
||||
"babel-loader": "^8.2.5",
|
||||
"bunchee": "1.8.5",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
@@ -52,6 +65,7 @@
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.27.1",
|
||||
"eslint-plugin-storybook": "^0.5.12",
|
||||
"jest": "^27.0.4",
|
||||
"prettier": "^2.3.1",
|
||||
"react": "^17.0.2",
|
||||
|
||||
18
scripts/build.sh
Executable file
18
scripts/build.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Copy the build to each react-variant build for npm release
|
||||
cp -r ./dist ./npm/react-webgl
|
||||
cp -r ./dist ./npm/react-canvas
|
||||
|
||||
echo "Replacing the canvas with webgl references in react-webgl"
|
||||
pushd ./npm/react-webgl/dist
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
find . -type f -name "*.ts" -print0 | xargs -0 sed -i '' -e 's/@rive-app\/canvas/@rive-app\/webgl/g'
|
||||
find . -type f -name "*.js" -print0 | xargs -0 sed -i '' -e 's/@rive-app\/canvas/@rive-app\/webgl/g'
|
||||
else
|
||||
find . -type f -name "*.ts" -print0 | xargs -0 sed -i -e 's/@rive-app\/canvas/@rive-app\/webgl/g'
|
||||
find . -type f -name "*.js" -print0 | xargs -0 sed -i -e 's/@rive-app\/canvas/@rive-app\/webgl/g'
|
||||
fi
|
||||
popd
|
||||
11
scripts/bump_all_versions.sh
Executable file
11
scripts/bump_all_versions.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Bump the version number of every npm module in the npm folder.
|
||||
for dir in ./npm/*; do
|
||||
pushd $dir > /dev/null
|
||||
repo_name=`echo $dir | sed 's:.*/::' | sed 's/_/-/g'`
|
||||
echo Bumping version of $repo_name
|
||||
../../scripts/bump_version.sh $repo_name $RELEASE_VERSION
|
||||
popd > /dev/null
|
||||
done
|
||||
12
scripts/bump_version.sh
Executable file
12
scripts/bump_version.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Bumps the version of a single npm module found in the current working
|
||||
# directory. Call bump_version.sh from the path with package.json in it.
|
||||
|
||||
set -e
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
# RELEASE_VERSION will come from an env variable passed in from the GH action workflow
|
||||
node $SCRIPT_DIR/nextVersion.js "$RELEASE_VERSION" `pwd`
|
||||
|
||||
# Replace the dist package json with the newly trimmed one
|
||||
cp -f ./package.json ./dist/package.json
|
||||
33
scripts/nextVersion.js
Normal file
33
scripts/nextVersion.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const fs = require('fs');
|
||||
const path = process.argv[3];
|
||||
const package = require(path + '/package.json');
|
||||
const currentVersion = package.version;
|
||||
const nextVersion = process.argv[2].trim().replace(/\'/g, '"');
|
||||
|
||||
if (!nextVersion || nextVersion === currentVersion) {
|
||||
throw new Error('Next version is not defined or is a version that already exists');
|
||||
}
|
||||
|
||||
// Returns -1 if first is less than second, 1 if first is greater than second, otherwise 0 if equal.
|
||||
function compareVersion(first, second) {
|
||||
// Assumption: only numbers in our versions.
|
||||
const firstParts = first.split('.').map((value) => parseInt(value));
|
||||
const secondParts = second.split('.').map((value) => parseInt(value));
|
||||
|
||||
for (let i = 0; i < Math.max(firstParts.length, secondParts.length); i++) {
|
||||
const first = i < firstParts.length ? firstParts[i] : 0;
|
||||
const second = i < secondParts.length ? secondParts[i] : 0;
|
||||
if (first < second) {
|
||||
return -1;
|
||||
}
|
||||
if (second < first) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (compareVersion(currentVersion, nextVersion) <= 0) {
|
||||
package.version = nextVersion;
|
||||
fs.writeFileSync(path + '/package.json', JSON.stringify(package, null, 2));
|
||||
}
|
||||
10
scripts/publish_all.sh
Executable file
10
scripts/publish_all.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Bump the version number of every npm module in the npm folder.
|
||||
for dir in ./npm/*; do
|
||||
pushd $dir > /dev/null
|
||||
echo Publishing `echo $dir | sed 's:.*/::'`
|
||||
npm publish --access public
|
||||
popd > /dev/null
|
||||
done
|
||||
21
scripts/setup_all_packages.sh
Executable file
21
scripts/setup_all_packages.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Copying package.json to rive-react npm package folders"
|
||||
|
||||
cp package.json npm/react-canvas
|
||||
cp package.json npm/react-webgl
|
||||
|
||||
# Bump the version number of every npm module in the npm folder.
|
||||
for dir in ./npm/*; do
|
||||
echo $dir
|
||||
pushd $dir > /dev/null
|
||||
echo $dir
|
||||
repo_name=`echo $dir | sed 's:.*/::' | sed 's/_/-/g'`
|
||||
echo Setting package.json on npm packages
|
||||
echo $repo_name
|
||||
../../scripts/setup_package.sh $repo_name
|
||||
echo Finished setting up package.json
|
||||
popd > /dev/null
|
||||
done
|
||||
|
||||
6
scripts/setup_package.sh
Executable file
6
scripts/setup_package.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Setup the package.json for a given npm module
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
node $SCRIPT_DIR/trimPackageJson.js `pwd` "$1"
|
||||
22
scripts/trimPackageJson.js
Normal file
22
scripts/trimPackageJson.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const fs = require('fs');
|
||||
const path = process.argv[2];
|
||||
const npmPackageSplit = process.argv[3].split('-');
|
||||
const renderer = npmPackageSplit[npmPackageSplit.length - 1];
|
||||
const package = require(path + '/package.json');
|
||||
|
||||
function trimNpmPackage() {
|
||||
package.name = `@rive-app/react-${renderer}`;
|
||||
package.description = `React wrapper around the @rive-app/${renderer} library`;
|
||||
const webDependencyName = `@rive-app/${renderer}`;
|
||||
const canvasDep = package.dependencies[webDependencyName];
|
||||
package.dependencies = {
|
||||
[webDependencyName]: canvasDep,
|
||||
};
|
||||
delete package.devDependencies;
|
||||
delete package.scripts;
|
||||
fs.writeFileSync(path + '/package.json', JSON.stringify(package, null, 2));
|
||||
}
|
||||
|
||||
if (renderer) {
|
||||
trimNpmPackage();
|
||||
}
|
||||
@@ -24,7 +24,7 @@ window.IntersectionObserver = class IntersectionObserver {
|
||||
unobserve() {}
|
||||
};
|
||||
|
||||
jest.mock('@rive-app/webgl', () => ({
|
||||
jest.mock('@rive-app/canvas', () => ({
|
||||
Rive: jest.fn().mockImplementation(() => ({
|
||||
on: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
import { Layout } from '@rive-app/webgl';
|
||||
import { Layout } from '@rive-app/canvas';
|
||||
import React, { ComponentProps } from 'react';
|
||||
import useRive from '../hooks/useRive';
|
||||
|
||||
export type RiveProps = {
|
||||
export interface RiveProps {
|
||||
/**
|
||||
* URL of the Rive asset, or path to where the public asset is stored.
|
||||
*/
|
||||
src: string;
|
||||
/**
|
||||
* Artboard to render from the Rive asset.
|
||||
* Defaults to the first artboard created.
|
||||
*/
|
||||
artboard?: string;
|
||||
/**
|
||||
* Specify a starting animation to play.
|
||||
*/
|
||||
animations?: string | string[];
|
||||
/**
|
||||
* Specify a starting state machine to play.
|
||||
*/
|
||||
stateMachines?: string | string[];
|
||||
/**
|
||||
* Specify a starting Layout object to set Fill and Alignment for the drawing surface. See docs at https://help.rive.app/runtimes/layout for more on layout configuration.
|
||||
*/
|
||||
layout?: Layout;
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
@@ -14,15 +34,17 @@ const Rive = ({
|
||||
src,
|
||||
artboard,
|
||||
animations,
|
||||
stateMachines,
|
||||
layout,
|
||||
useOffscreenRenderer = true,
|
||||
...rest
|
||||
}: RiveProps & ComponentProps<'div'>) => {
|
||||
}: RiveProps & ComponentProps<'canvas'>) => {
|
||||
const params = {
|
||||
src,
|
||||
artboard,
|
||||
animations,
|
||||
layout,
|
||||
stateMachines,
|
||||
autoplay: true,
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import React, {
|
||||
ComponentProps,
|
||||
RefCallback,
|
||||
} from 'react';
|
||||
import { Rive, EventType } from '@rive-app/webgl';
|
||||
import { Rive, EventType } from '@rive-app/canvas';
|
||||
import {
|
||||
UseRiveParameters,
|
||||
UseRiveOptions,
|
||||
@@ -23,20 +23,23 @@ type RiveComponentProps = {
|
||||
function RiveComponent({
|
||||
setContainerRef,
|
||||
setCanvasRef,
|
||||
className = '',
|
||||
style,
|
||||
...rest
|
||||
}: RiveComponentProps & ComponentProps<'div'>) {
|
||||
}: RiveComponentProps & ComponentProps<'canvas'>) {
|
||||
const containerStyle = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
...style,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={setContainerRef}
|
||||
style={'className' in rest ? undefined : containerStyle}
|
||||
{...rest}
|
||||
className={className}
|
||||
{...(!className && { style: containerStyle })}
|
||||
>
|
||||
<canvas ref={setCanvasRef} style={{ verticalAlign: 'top' }} />
|
||||
<canvas ref={setCanvasRef} style={{ verticalAlign: 'top' }} {...rest} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -230,20 +233,28 @@ export default function useRive(
|
||||
const animations = riveParams?.animations;
|
||||
useEffect(() => {
|
||||
if (rive && animations) {
|
||||
rive.stop(rive.animationNames);
|
||||
rive.play(animations);
|
||||
if (rive.isPlaying) {
|
||||
rive.stop(rive.animationNames);
|
||||
rive.play(animations);
|
||||
} else if (rive.isPaused) {
|
||||
rive.stop(rive.animationNames);
|
||||
rive.pause(animations);
|
||||
}
|
||||
}
|
||||
}, [animations, rive]);
|
||||
|
||||
const Component = useCallback((props: ComponentProps<'div'>): JSX.Element => {
|
||||
return (
|
||||
<RiveComponent
|
||||
setContainerRef={setContainerRef}
|
||||
setCanvasRef={setCanvasRef}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}, []);
|
||||
const Component = useCallback(
|
||||
(props: ComponentProps<'canvas'>): JSX.Element => {
|
||||
return (
|
||||
<RiveComponent
|
||||
setContainerRef={setContainerRef}
|
||||
setCanvasRef={setCanvasRef}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return {
|
||||
canvas: canvasRef.current,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Rive, StateMachineInput } from '@rive-app/webgl';
|
||||
import { Rive, StateMachineInput } from '@rive-app/canvas';
|
||||
|
||||
/**
|
||||
* Custom hook for fetching a stateMachine input from a rive file.
|
||||
|
||||
@@ -5,4 +5,4 @@ import useStateMachineInput from './hooks/useStateMachineInput';
|
||||
export default Rive;
|
||||
export { useRive, useStateMachineInput };
|
||||
export { RiveState, UseRiveParameters, UseRiveOptions } from './types';
|
||||
export * from '@rive-app/webgl';
|
||||
export * from '@rive-app/canvas';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RefCallback, ComponentProps } from 'react';
|
||||
import { Rive, RiveParameters } from '@rive-app/webgl';
|
||||
import { Rive, RiveParameters } from '@rive-app/canvas';
|
||||
|
||||
export type UseRiveParameters = Partial<Omit<RiveParameters, 'canvas'>> | null;
|
||||
|
||||
@@ -29,5 +29,5 @@ export type RiveState = {
|
||||
setCanvasRef: RefCallback<HTMLCanvasElement>;
|
||||
setContainerRef: RefCallback<HTMLElement>;
|
||||
rive: Rive | null;
|
||||
RiveComponent: (props: ComponentProps<'div'>) => JSX.Element;
|
||||
RiveComponent: (props: ComponentProps<'canvas'>) => JSX.Element;
|
||||
};
|
||||
|
||||
33
test/Rive.test.tsx
Normal file
33
test/Rive.test.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import RiveComponent from '../src/components/Rive';
|
||||
import {render} from '@testing-library/react'
|
||||
|
||||
jest.mock('@rive-app/canvas', () => ({
|
||||
Rive: jest.fn().mockImplementation(() => ({
|
||||
on: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
})),
|
||||
Layout: jest.fn(),
|
||||
Fit: {
|
||||
Cover: 'cover',
|
||||
},
|
||||
Alignment: {
|
||||
Center: 'center',
|
||||
},
|
||||
EventType: {
|
||||
Load: 'load',
|
||||
},
|
||||
StateMachineInputType: {
|
||||
Number: 1,
|
||||
Boolean: 2,
|
||||
Trigger: 3,
|
||||
},
|
||||
}));
|
||||
|
||||
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" />);
|
||||
expect(container.firstChild).toHaveClass('container-styles');
|
||||
expect(getByLabelText('Foo label').tagName).toEqual('CANVAS');
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import { mocked } from 'jest-mock';
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
|
||||
import useRive from '../src/hooks/useRive';
|
||||
import * as rive from '@rive-app/webgl';
|
||||
import * as rive from '@rive-app/canvas';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
jest.mock('@rive-app/webgl', () => ({
|
||||
jest.mock('@rive-app/canvas', () => ({
|
||||
Rive: jest.fn().mockImplementation(() => ({
|
||||
on: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
@@ -285,6 +287,7 @@ describe('useRive', () => {
|
||||
stop: stopMock,
|
||||
play: playMock,
|
||||
animationNames: ['light'],
|
||||
isPlaying: true,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
@@ -308,4 +311,75 @@ describe('useRive', () => {
|
||||
expect(stopMock).toBeCalledWith(['light']);
|
||||
expect(playMock).toBeCalledWith('dark');
|
||||
});
|
||||
|
||||
it('updates the paused animation when the animations param changes if the animation is paused', async () => {
|
||||
const params = {
|
||||
src: 'file-src',
|
||||
animations: 'light',
|
||||
};
|
||||
|
||||
const playMock = jest.fn();
|
||||
const pauseMock = jest.fn();
|
||||
const stopMock = jest.fn();
|
||||
|
||||
const riveMock = {
|
||||
on: (_: string, cb: () => void) => cb(),
|
||||
stop: stopMock,
|
||||
play: playMock,
|
||||
pause: pauseMock,
|
||||
animationNames: ['light'],
|
||||
isPlaying: false,
|
||||
isPaused: true,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
mocked(rive.Rive).mockImplementation(() => riveMock);
|
||||
|
||||
const canvasSpy = document.createElement('canvas');
|
||||
|
||||
const { result, rerender } = renderHook((params) => useRive(params), {
|
||||
initialProps: params,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
|
||||
rerender({
|
||||
src: 'file-src',
|
||||
animations: 'dark',
|
||||
});
|
||||
|
||||
expect(stopMock).toBeCalledWith(['light']);
|
||||
expect(pauseMock).toBeCalledWith('dark');
|
||||
expect(playMock).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('does not set styles if className is passed in for the canvas container', async () => {
|
||||
const params = {
|
||||
src: 'file-src',
|
||||
};
|
||||
|
||||
const riveMock = {
|
||||
on: (_: string, cb: () => void) => cb(),
|
||||
stop: jest.fn(),
|
||||
stopRendering: jest.fn(),
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
mocked(rive.Rive).mockImplementation(() => riveMock);
|
||||
|
||||
const canvasSpy = document.createElement('canvas');
|
||||
const { result } = renderHook(() => useRive(params));
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCanvasRef(canvasSpy);
|
||||
});
|
||||
|
||||
const { RiveComponent: RiveTestComponent } = result.current;
|
||||
const { container } = render(
|
||||
<RiveTestComponent className="rive-test-clas" style={{ width: '50%' }} />
|
||||
);
|
||||
expect(container.firstChild).not.toHaveStyle('width: 50%');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,9 +2,9 @@ import { mocked } from 'jest-mock';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
import useStateMachineInput from '../src/hooks/useStateMachineInput';
|
||||
import {Rive, StateMachineInput} from '@rive-app/webgl';
|
||||
import {Rive, StateMachineInput} from '@rive-app/canvas';
|
||||
|
||||
jest.mock('@rive-app/webgl', () => ({
|
||||
jest.mock('@rive-app/canvas', () => ({
|
||||
Rive: jest.fn().mockImplementation(() => ({
|
||||
on: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
"target": "es5",
|
||||
"typeRoots": ["./types", "./node_modules/@types"]
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
"include": ["src/**/*", "../examples/stories"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user