Feat: Add canvasProps as an option to useRive so that there's a way to set props on the canvas element

This commit is contained in:
Zach Plata
2022-04-11 12:24:55 -07:00
parent 3a2ed32856
commit 81150646e5
5 changed files with 35 additions and 1 deletions

View File

@@ -131,6 +131,7 @@ export default Example;
- `useDevicePixelRatio`: _(optional)_ If `true`, the hook will scale the resolution of the animation based the [devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). Defaults to `true`. NOTE: Requires the `setContainerRef` ref callback to be passed to a element wrapping a canvas element. If you use the `RiveComponent`, then this will happen automatically.
- `fitCanvasToArtboardHeight`: _(optional)_ If `true`, then the canvas will resize based on the height of the artboard. Defaults to `false`.
- `useOffscreenRenderer`: _(optional)_ If `true`, the Rive instance will share (or create if one does not exist) an offscreen `WebGL` context. This allows you to display multiple Rive animations on one screen to work around some browser limitations regarding multiple concurrent WebGL contexts. If `false`, each Rive instance will have its own dedicated `WebGL` context, and you may need to be cautious of the browser limitations just mentioned. Defaults to `true`.
- `canvasProps`: _(optional)_ An object of props to pass to the underlying `<canvas>` element created in the `RiveComponent` returned from this hook. You can also set `canvasProps` directly on the returned `RiveComponent` as well if you prefer. Currently by default, spreading any HTML props on the `RiveComponent` goes to the `<canvas>`'s container `<div>` element, however, in a next major version, this may be restructured such that by default, spread props go onto the `canvas` element itself.
### useStateMachineInput Hook

View File

@@ -6,28 +6,34 @@ export type RiveProps = {
src: string;
artboard?: string;
animations?: string | string[];
stateMachines?: string | string[];
layout?: Layout;
useOffscreenRenderer?: boolean;
canvasProps?: ComponentProps<'canvas'>;
};
const Rive = ({
src,
artboard,
animations,
stateMachines,
layout,
useOffscreenRenderer = true,
canvasProps = {},
...rest
}: RiveProps & ComponentProps<'div'>) => {
const params = {
src,
artboard,
animations,
stateMachines,
layout,
autoplay: true,
};
const options = {
useOffscreenRenderer,
canvasProps,
};
const { RiveComponent } = useRive(params, options);

View File

@@ -18,11 +18,13 @@ import { useWindowSize } from '../utils';
type RiveComponentProps = {
setContainerRef: RefCallback<HTMLElement>;
setCanvasRef: RefCallback<HTMLCanvasElement>;
canvasProps?: ComponentProps<'canvas'>,
};
function RiveComponent({
setContainerRef,
setCanvasRef,
canvasProps = {},
...rest
}: RiveComponentProps & ComponentProps<'div'>) {
const containerStyle = {
@@ -36,7 +38,7 @@ function RiveComponent({
style={'className' in rest ? undefined : containerStyle}
{...rest}
>
<canvas ref={setCanvasRef} style={{ verticalAlign: 'top' }} />
<canvas {...canvasProps} ref={setCanvasRef} style={{ verticalAlign: 'top' }} />
</div>
);
}
@@ -45,6 +47,7 @@ const defaultOptions = {
useDevicePixelRatio: true,
fitCanvasToArtboardHeight: false,
useOffscreenRenderer: true,
canvasProps: {},
};
/**
@@ -240,6 +243,7 @@ export default function useRive(
<RiveComponent
setContainerRef={setContainerRef}
setCanvasRef={setCanvasRef}
canvasProps={options.canvasProps}
{...props}
/>
);

View File

@@ -7,6 +7,7 @@ export type UseRiveOptions = {
useDevicePixelRatio: boolean;
fitCanvasToArtboardHeight: boolean;
useOffscreenRenderer: boolean;
canvasProps: ComponentProps<'canvas'>;
};
export type Dimensions = {

View File

@@ -1,4 +1,6 @@
import React from 'react';
import { mocked } from 'jest-mock';
import {render} from '@testing-library/react';
import { renderHook, act } from '@testing-library/react-hooks';
import useRive from '../src/hooks/useRive';
@@ -308,4 +310,24 @@ describe('useRive', () => {
expect(stopMock).toBeCalledWith(['light']);
expect(playMock).toBeCalledWith('dark');
});
it('passes canvasProps down to the canvas element', () => {
const params = {
src: 'file-src',
animations: 'light',
};
const options = {
canvasProps: {
'data-testid': 'foo',
'aria-label': 'test label',
},
};
const { result } = renderHook(() => useRive(params, options));
const ResultComponent = result.current.RiveComponent;
const {getByTestId} = render(<ResultComponent />);
expect(getByTestId('foo')).toHaveAttribute('aria-label', 'test label');
});
});