mirror of
https://github.com/rive-app/rive-react.git
synced 2026-03-13 08:22:30 +08:00
Compare commits
4 Commits
zp/dynamic
...
resizeExam
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25afe7d77e | ||
|
|
258270b4fc | ||
|
|
41962ef864 | ||
|
|
c69fffa5cf |
23
CHANGELOG.md
23
CHANGELOG.md
@@ -4,32 +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.23](https://github.com/rive-app/rive-react/compare/v3.0.21...v3.0.23)
|
||||
|
||||
- Bump runtime version to fix broken version [`3c578b7`](https://github.com/rive-app/rive-react/commit/3c578b730f82059469393522722316f2ad3a61d3)
|
||||
|
||||
#### [v3.0.21](https://github.com/rive-app/rive-react/compare/v3.0.20...v3.0.21)
|
||||
|
||||
> 22 July 2022
|
||||
|
||||
- chore: release 3.0.21 [`f7aced0`](https://github.com/rive-app/rive-react/commit/f7aced03cd3c39d039cc53af54947e328fe18e83)
|
||||
- rev rive-wasm dependencies & update render delay to be 0ms [`eb07281`](https://github.com/rive-app/rive-react/commit/eb072814155bb803f6faa831caa0e0292b8f6f28)
|
||||
|
||||
#### [v3.0.20](https://github.com/rive-app/rive-react/compare/v3.0.19...v3.0.20)
|
||||
|
||||
> 22 July 2022
|
||||
|
||||
- update tests [`24d8e0a`](https://github.com/rive-app/rive-react/commit/24d8e0a90795f650806064d53ae1b362e3fd332f)
|
||||
- update resize behaviour to throttle, add parameters to enable switching modes [`1092b44`](https://github.com/rive-app/rive-react/commit/1092b44947e2ac07dd38d21e8b45445256c0a59d)
|
||||
- ensure we re evaluate state machine inputs when we play is triggered, looks like there maybe additional situations where we are going to need this. [`84b18cc`](https://github.com/rive-app/rive-react/commit/84b18cc3ddf86e55b6741956ea8f86d6d21f4078)
|
||||
|
||||
#### [v3.0.19](https://github.com/rive-app/rive-react/compare/v3.0.18...v3.0.19)
|
||||
|
||||
> 19 July 2022
|
||||
|
||||
- chore: release 3.0.19 [`efe28aa`](https://github.com/rive-app/rive-react/commit/efe28aa5f35f5ddde3e89085c34016ce87bb5cbb)
|
||||
- fix tests that were automatically calling the rive load callback to be more controlled [`16d836c`](https://github.com/rive-app/rive-react/commit/16d836c95928e4294b565ecb444d517653c4988b)
|
||||
- Fix: Add check before setting Rive as state variable on Rive instance load [`838ed1a`](https://github.com/rive-app/rive-react/commit/838ed1abf8aeec86ca63bfef07953424ba9cce90)
|
||||
|
||||
#### [v3.0.18](https://github.com/rive-app/rive-react/compare/v3.0.17...v3.0.18)
|
||||
|
||||
|
||||
32
examples/resize-test/README.md
Normal file
32
examples/resize-test/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# To run
|
||||
|
||||
This is a basic showcase of a resize issue we have, to test this locally with rive-react changes
|
||||
|
||||
1. run `npm start`
|
||||
2. run `npm run build` for the rive-react project
|
||||
|
||||
If you want to also test local wasm changes:
|
||||
|
||||
1. update `rive-react`/package.json to a locally checked out version of rive-wasm's canvas_single
|
||||
`"@rive-app/canvas": "../rive-wasm/js/npm/canvas_single",`
|
||||
2. cd to `rive-wasm/js` & run `npm run dev` (keep this going as it will watch for changes)
|
||||
3. run `npm start`
|
||||
4. run `npm run build` for the rive-react project
|
||||
|
||||
|
||||
# Resize issue:
|
||||
|
||||
update parameters in `utils.ts` to see this for yourself.
|
||||
|
||||
Resizing from window.resize
|
||||
- bottom animation moves slower than rest
|
||||
- cannot deal with the animations container resizing, unless its linked to the window resizing
|
||||
Resizing from ResizeObserver
|
||||
- all animations move together super smooth when resizing
|
||||
- top two animations flicker to white (blank canvas) when resizing
|
||||
Resizing form ResizeObserver - throttled (current default)
|
||||
- all animations move "together"
|
||||
- resizing looks pretty smooth, but everything lags behind a bit.
|
||||
|
||||
# Other issues
|
||||
its also very slow resizing when dev tools is open, its fine when closed though.
|
||||
36
examples/resize-test/package.json
Normal file
36
examples/resize-test/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
17
examples/resize-test/public/index.html
Normal file
17
examples/resize-test/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!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>
|
||||
BIN
examples/resize-test/public/magic-ball.riv
Normal file
BIN
examples/resize-test/public/magic-ball.riv
Normal file
Binary file not shown.
BIN
examples/resize-test/public/poison-loader.riv
Normal file
BIN
examples/resize-test/public/poison-loader.riv
Normal file
Binary file not shown.
30
examples/resize-test/src/App.js
Normal file
30
examples/resize-test/src/App.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useRive } from 'rive-react';
|
||||
|
||||
function App() {
|
||||
const params = {
|
||||
src: 'poison-loader.riv',
|
||||
autoplay: true,
|
||||
};
|
||||
|
||||
const { RiveComponent: RiveComponentBasic } = useRive(params);
|
||||
const { RiveComponent: RiveComponentBasic2 } = useRive(params);
|
||||
const { RiveComponent: RiveComponentBasic3 } = useRive(params);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ width: '100%' }}>
|
||||
<div style={{ height: '300px', width: '100%' }}>
|
||||
<RiveComponentBasic />
|
||||
</div>
|
||||
<div style={{ height: '300px', width: '100%' }}>
|
||||
<RiveComponentBasic2 />
|
||||
</div>
|
||||
<div style={{ height: '300px', width: '100%' }}>
|
||||
<RiveComponentBasic3 />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
10
examples/resize-test/src/index.js
Normal file
10
examples/resize-test/src/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rive-react",
|
||||
"version": "3.0.23",
|
||||
"version": "3.0.19",
|
||||
"description": "React wrapper around the rive-js library",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/types/index.d.ts",
|
||||
@@ -29,8 +29,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/rive-app/rive-react#readme",
|
||||
"dependencies": {
|
||||
"@rive-app/canvas": "1.0.83",
|
||||
"@rive-app/webgl": "1.0.79"
|
||||
"@rive-app/canvas": "1.0.71",
|
||||
"@rive-app/webgl": "1.0.68"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
|
||||
@@ -25,8 +25,6 @@ function RiveComponent({
|
||||
setCanvasRef,
|
||||
className = '',
|
||||
style,
|
||||
width,
|
||||
height,
|
||||
...rest
|
||||
}: RiveComponentProps & ComponentProps<'canvas'>) {
|
||||
const containerStyle = {
|
||||
@@ -41,12 +39,7 @@ function RiveComponent({
|
||||
className={className}
|
||||
{...(!className && { style: containerStyle })}
|
||||
>
|
||||
<canvas
|
||||
ref={setCanvasRef}
|
||||
style={{ verticalAlign: 'top' }}
|
||||
{...(width !== undefined && {width, 'data-rive-width-prop': width})}
|
||||
{...(height !== undefined && {height, 'data-c': height})}
|
||||
{...rest} />
|
||||
<canvas ref={setCanvasRef} style={{ verticalAlign: 'top' }} {...rest} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -89,7 +82,6 @@ export default function useRive(
|
||||
const containerRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
const [rive, setRive] = useState<Rive | null>(null);
|
||||
const riveRef = useRef<Rive | null>(null);
|
||||
const [dimensions, setDimensions] = useState<Dimensions>({
|
||||
height: 0,
|
||||
width: 0,
|
||||
@@ -100,9 +92,6 @@ export default function useRive(
|
||||
const size = useSize(containerRef);
|
||||
|
||||
const isParamsLoaded = Boolean(riveParams);
|
||||
|
||||
const artboardParam = riveParams?.artboard;
|
||||
|
||||
const options = getOptions(opts);
|
||||
|
||||
/**
|
||||
@@ -144,25 +133,15 @@ export default function useRive(
|
||||
if (options.fitCanvasToArtboardHeight) {
|
||||
containerRef.current.style.height = height + 'px';
|
||||
}
|
||||
const widthProp = canvasRef.current.dataset['rive-width-prop'];
|
||||
const heightProp = canvasRef.current.dataset['rive-height-prop'];
|
||||
if (options.useDevicePixelRatio) {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
if (!widthProp) {
|
||||
canvasRef.current.width = dpr * width;
|
||||
}
|
||||
if (!heightProp) {
|
||||
canvasRef.current.height = dpr * height;
|
||||
}
|
||||
canvasRef.current.width = dpr * width;
|
||||
canvasRef.current.height = dpr * height;
|
||||
canvasRef.current.style.width = width + 'px';
|
||||
canvasRef.current.style.height = height + 'px';
|
||||
} else {
|
||||
if (!widthProp) {
|
||||
canvasRef.current.width = width;
|
||||
}
|
||||
if (!heightProp) {
|
||||
canvasRef.current.height = height;
|
||||
}
|
||||
canvasRef.current.width = width;
|
||||
canvasRef.current.height = height;
|
||||
}
|
||||
setDimensions({ width, height });
|
||||
|
||||
@@ -207,9 +186,6 @@ export default function useRive(
|
||||
// on an unmounted component in some rare cases
|
||||
if (canvasRef.current) {
|
||||
setRive(r);
|
||||
// Setting a ref in addition to state so that we can get a reference to the Rive
|
||||
// instance on component unmount
|
||||
riveRef.current = r;
|
||||
}
|
||||
});
|
||||
} else if (canvas === null && canvasRef.current) {
|
||||
@@ -238,8 +214,8 @@ export default function useRive(
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(([entry]) => {
|
||||
entry.isIntersecting
|
||||
? rive && riveRef.current && rive.startRendering()
|
||||
: rive && riveRef.current && rive.stopRendering();
|
||||
? rive && rive.startRendering()
|
||||
: rive && rive.stopRendering();
|
||||
});
|
||||
|
||||
if (canvasRef.current) {
|
||||
@@ -251,46 +227,17 @@ export default function useRive(
|
||||
};
|
||||
}, [rive]);
|
||||
|
||||
/**
|
||||
* Reset Rive when the artboard, animations, or state machines change
|
||||
*/
|
||||
useEffect(() => {
|
||||
// Ensure at least the artboard is set before resetting Rive
|
||||
if (artboardParam && rive) {
|
||||
rive.stopRendering();
|
||||
rive.stop();
|
||||
rive.cleanup();
|
||||
|
||||
const { useOffscreenRenderer } = options;
|
||||
const r = new Rive({
|
||||
useOffscreenRenderer,
|
||||
...riveParams,
|
||||
canvas: canvasRef.current as HTMLCanvasElement,
|
||||
});
|
||||
r.on(EventType.Load, () => {
|
||||
if (canvasRef.current) {
|
||||
setRive(r);
|
||||
// Setting a ref in addition to state so that we can get a reference to the Rive
|
||||
// instance on component unmount
|
||||
riveRef.current = r;
|
||||
}
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [artboardParam]);
|
||||
|
||||
/**
|
||||
* On unmount, stop rive from rendering.
|
||||
*/
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (riveRef.current) {
|
||||
riveRef.current.stopRendering();
|
||||
riveRef.current.stop();
|
||||
riveRef.current.cleanup();
|
||||
if (rive) {
|
||||
rive.stop();
|
||||
setRive(null);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}, [rive]);
|
||||
|
||||
/**
|
||||
* Listen for changes in the animations params
|
||||
@@ -318,7 +265,7 @@ export default function useRive(
|
||||
/>
|
||||
);
|
||||
},
|
||||
[setCanvasRef, setContainerRef],
|
||||
[]
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
32
src/utils.ts
32
src/utils.ts
@@ -12,14 +12,15 @@ function throttle(f: Function, delay: number) {
|
||||
let timer = 0;
|
||||
return function (this: Function, ...args: any) {
|
||||
clearTimeout(timer);
|
||||
timer = window.setTimeout(() => f.apply(this, args), delay);
|
||||
timer = setTimeout(() => f.apply(this, args), delay) as unknown as number;
|
||||
};
|
||||
}
|
||||
|
||||
const MyResizeObserver = globalThis.ResizeObserver || FakeResizeObserver;
|
||||
const hasResizeObserver = globalThis.ResizeObserver !== undefined;
|
||||
|
||||
const useResizeObserver = hasResizeObserver;
|
||||
const preferResizeObserver = true;
|
||||
const throttleResizeObserver = true;
|
||||
const useResizeObserver = hasResizeObserver && preferResizeObserver;
|
||||
const useWindowListener = !useResizeObserver;
|
||||
|
||||
export function useSize(
|
||||
@@ -51,14 +52,23 @@ export function useSize(
|
||||
}, []);
|
||||
const observer = useRef(
|
||||
new MyResizeObserver(
|
||||
throttle((entries: any) => {
|
||||
if (useResizeObserver) {
|
||||
setSize({
|
||||
width: entries[entries.length - 1].contentRect.width,
|
||||
height: entries[entries.length - 1].contentRect.height,
|
||||
});
|
||||
}
|
||||
}, 0)
|
||||
throttleResizeObserver
|
||||
? throttle((entries: any) => {
|
||||
if (useResizeObserver) {
|
||||
setSize({
|
||||
width: entries[entries.length - 1].contentRect.width,
|
||||
height: entries[entries.length - 1].contentRect.height,
|
||||
});
|
||||
}
|
||||
}, 16)
|
||||
: (entries: any) => {
|
||||
if (useResizeObserver) {
|
||||
setSize({
|
||||
width: entries[entries.length - 1].contentRect.width,
|
||||
height: entries[entries.length - 1].contentRect.height,
|
||||
});
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ jest.mock('@rive-app/canvas', () => ({
|
||||
Rive: jest.fn().mockImplementation(() => ({
|
||||
on: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
stateMachineInputs: jest.fn(),
|
||||
})),
|
||||
Layout: jest.fn(),
|
||||
Fit: {
|
||||
@@ -27,21 +26,6 @@ jest.mock('@rive-app/canvas', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function getRiveMock({
|
||||
smiInputs,
|
||||
}: {
|
||||
smiInputs?: null | StateMachineInput[];
|
||||
} = {}) {
|
||||
const riveMock = new Rive({
|
||||
canvas: undefined as unknown as HTMLCanvasElement,
|
||||
});
|
||||
if (smiInputs) {
|
||||
riveMock.stateMachineInputs = jest.fn().mockReturnValue(smiInputs);
|
||||
}
|
||||
|
||||
return riveMock;
|
||||
}
|
||||
|
||||
describe('useStateMachineInput', () => {
|
||||
it('returns null if there is null rive object passed', () => {
|
||||
const { result } = renderHook(() => useStateMachineInput(null));
|
||||
@@ -49,29 +33,31 @@ describe('useStateMachineInput', () => {
|
||||
});
|
||||
|
||||
it('returns null if there is no state machine name', () => {
|
||||
const riveMock = getRiveMock();
|
||||
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
const riveMock = {};
|
||||
mocked(Rive).mockImplementation(() => riveMock as Rive);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock, '', 'testInput')
|
||||
useStateMachineInput(riveMock as Rive, '', 'testInput')
|
||||
);
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if there is no state machine input name', () => {
|
||||
const riveMock = getRiveMock();
|
||||
const riveMock = {};
|
||||
mocked(Rive).mockImplementation(() => riveMock as Rive);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock, 'smName', '')
|
||||
useStateMachineInput(riveMock as Rive, 'smName', '')
|
||||
);
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if there are no inputs for the state machine', () => {
|
||||
const riveMock = getRiveMock({ smiInputs: [] });
|
||||
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
const riveMock = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
stateMachineInputs: (_: string) => [] as StateMachineInput[],
|
||||
};
|
||||
mocked(Rive).mockImplementation(() => riveMock as Rive);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock as Rive, 'smName', '')
|
||||
@@ -83,12 +69,14 @@ describe('useStateMachineInput', () => {
|
||||
const smInput = {
|
||||
name: 'boolInput',
|
||||
} as StateMachineInput;
|
||||
const riveMock = getRiveMock({ smiInputs: [smInput] });
|
||||
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
const riveMock = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
stateMachineInputs: (_: string) => [smInput] as StateMachineInput[],
|
||||
};
|
||||
mocked(Rive).mockImplementation(() => riveMock as Rive);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock, 'smName', 'numInput')
|
||||
useStateMachineInput(riveMock as Rive, 'smName', 'numInput')
|
||||
);
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
@@ -97,12 +85,14 @@ describe('useStateMachineInput', () => {
|
||||
const smInput = {
|
||||
name: 'boolInput',
|
||||
} as StateMachineInput;
|
||||
const riveMock = getRiveMock({ smiInputs: [smInput] });
|
||||
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
const riveMock = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
stateMachineInputs: (_: string) => [smInput] as StateMachineInput[],
|
||||
};
|
||||
mocked(Rive).mockImplementation(() => riveMock as Rive);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock, 'smName', 'boolInput')
|
||||
useStateMachineInput(riveMock as Rive, 'smName', 'boolInput')
|
||||
);
|
||||
expect(result.current).toBe(smInput);
|
||||
});
|
||||
@@ -112,11 +102,14 @@ describe('useStateMachineInput', () => {
|
||||
name: 'boolInput',
|
||||
value: false,
|
||||
} as StateMachineInput;
|
||||
const riveMock = getRiveMock({ smiInputs: [smInput] });
|
||||
mocked(Rive).mockImplementation(() => riveMock);
|
||||
const riveMock = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
stateMachineInputs: (_: string) => [smInput] as StateMachineInput[],
|
||||
};
|
||||
mocked(Rive).mockImplementation(() => riveMock as Rive);
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useStateMachineInput(riveMock, 'smName', 'boolInput', true)
|
||||
useStateMachineInput(riveMock as Rive, 'smName', 'boolInput', true)
|
||||
);
|
||||
expect(result.current).toStrictEqual({
|
||||
...smInput,
|
||||
|
||||
Reference in New Issue
Block a user