Compare commits

...

4 Commits

Author SHA1 Message Date
zplata
414d6f895a chore: release 3.0.8 2022-06-09 16:00:03 +00:00
Zach Plata
38625a00c3 Maint: Add GH workflow for deploying storybook to Github Pages 2022-06-09 10:55:55 -05:00
zplata
bad688dfa3 chore: release 3.0.7 2022-06-08 14:51:07 +00:00
Zach Plata
ec230faa73 Feat: Move existing examples into Storybook and add documentation 2022-06-08 09:47:41 -05:00
69 changed files with 559 additions and 985 deletions

View File

@@ -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'
}
}
};

28
.github/workflows/storybook.yml vendored Normal file
View 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
View 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'],
}

View 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&amp;display=swap"
/>
<link
rel="stylesheet"
data-href="https://fonts.googleapis.com/css2?family=Roboto+Mono&amp;display=swap"
/>
<style>
body {
font-family: 'Roboto', sans-serif;
font-size: 16px;
}
</style>

39
.storybook/preview.js Normal file
View 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);

View File

@@ -4,38 +4,9 @@ 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.6](https://github.com/rive-app/rive-react/compare/v3.0.5...v3.0.6)
#### [v3.0.8](https://github.com/rive-app/rive-react/compare/v3.0.1...v3.0.8)
- 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)
- Maint: Add GH workflow for deploying storybook to Github Pages [`38625a0`](https://github.com/rive-app/rive-react/commit/38625a00c313192d0edbe1c3a855bea1ec56bd2b)
#### [v3.0.1](https://github.com/rive-app/rive-react/compare/v3.0.0...v3.0.1)

0
docs/.gitignore vendored Normal file
View File

View File

@@ -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*

View File

@@ -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
```

View File

@@ -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"
]
}
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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')
);

View File

@@ -1 +0,0 @@
/// <reference types="react-scripts" />

View File

@@ -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"
]
}

View File

@@ -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
```

View File

@@ -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": "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"
]
}
}

View File

@@ -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>

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,29 +0,0 @@
import { useRive } from 'rive-react';
function App() {
const params = {
src: 'poison-loader.riv',
autoplay: true,
};
const { RiveComponent: RiveComponentBasic } = useRive(params);
const { RiveComponent: RiveComponentTouch } = useRive({
src: 'magic-ball.riv',
autoplay: true,
stateMachines: "Main State Machine",
});
return (
<>
<div style={{ height: '500px', width: '500px' }}>
<RiveComponentBasic />
</div>
<div style={{ height: '300px', width: '300px' }}>
<RiveComponentTouch />
</div>
</>
);
}
export default App;

View File

@@ -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')
);

View File

@@ -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
```

View File

@@ -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"
]
}
}

View File

@@ -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>

View File

Binary file not shown.

View File

@@ -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;

View File

@@ -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')
);

View File

@@ -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
```

View File

@@ -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"
]
}
}

View File

@@ -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>

View File

Binary file not shown.

View File

@@ -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;

View File

@@ -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')
);

View File

@@ -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
```

View File

@@ -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"
]
}
}

View File

@@ -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>

View File

Binary file not shown.

View File

@@ -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;

View File

@@ -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')
);

View File

@@ -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
```

View File

@@ -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"
]
}
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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')
);

View File

@@ -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
```

View File

@@ -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"
]
}
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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')
);

View File

@@ -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
```

View File

@@ -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"
]
}
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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')
);

View 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. 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.

View 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).

View 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>

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Binary file not shown.

View File

@@ -0,0 +1,7 @@
import React from 'react';
export const Button = ({onClick, children}) => {
return (
<button className="btn" onClick={onClick}>{children}</button>
);
};

View 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;
}

View File

@@ -1,6 +1,6 @@
{
"name": "rive-react",
"version": "3.0.6",
"version": "3.0.8",
"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",
@@ -34,6 +36,15 @@
"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",
@@ -44,7 +55,8 @@
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
"auto-changelog": "^2.3.0",
"bunchee": "1.8.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",
@@ -53,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",

View File

@@ -2,12 +2,31 @@ 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;
};

View File

@@ -17,5 +17,5 @@
"target": "es5",
"typeRoots": ["./types", "./node_modules/@types"]
},
"include": ["src/**/*"]
"include": ["src/**/*", "../examples/stories"]
}