mirror of
https://github.com/rive-app/rive-react.git
synced 2026-03-13 08:22:30 +08:00
inital work for data binding hooks
This commit is contained in:
74
src/hooks/useViewModel.ts
Normal file
74
src/hooks/useViewModel.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { EventType, Rive, ViewModel } from '@rive-app/canvas';
|
||||
import { UseViewModelParameters } from '../types';
|
||||
|
||||
const defaultParams: UseViewModelParameters = {
|
||||
useDefault: false,
|
||||
name: '',
|
||||
};
|
||||
|
||||
const equal = (
|
||||
params: UseViewModelParameters | null,
|
||||
to: UseViewModelParameters | null
|
||||
): boolean => {
|
||||
if (!params || !to) {
|
||||
return false;
|
||||
}
|
||||
if (params.useDefault !== to.useDefault || params.name !== to.name) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook for fetching a view model.
|
||||
*
|
||||
* @param rive - Rive instance
|
||||
* @param userParameters - Parameters to load view model
|
||||
* @returns
|
||||
*/
|
||||
export default function useViewModel(
|
||||
rive: Rive | null,
|
||||
userParameters?: UseViewModelParameters
|
||||
): ViewModel | null {
|
||||
const [viewModel, setViewModel] = useState<ViewModel | null>(null);
|
||||
const currentParams = useRef<UseViewModelParameters | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const parameters = {
|
||||
...defaultParams,
|
||||
...userParameters,
|
||||
};
|
||||
|
||||
function getViewModel(): ViewModel | null {
|
||||
if (rive) {
|
||||
if (parameters?.useDefault) {
|
||||
return rive!.defaultViewModel();
|
||||
} else if (parameters?.name) {
|
||||
return rive.viewModelByName(parameters?.name);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function setViewModelValue() {
|
||||
if (!rive) {
|
||||
setViewModel(null);
|
||||
currentParams.current = null;
|
||||
} else {
|
||||
const viewModel = getViewModel();
|
||||
setViewModel(viewModel);
|
||||
currentParams.current = parameters;
|
||||
}
|
||||
}
|
||||
|
||||
if (!equal(parameters, currentParams.current)) {
|
||||
rive?.on(EventType.Load, setViewModelValue);
|
||||
setViewModelValue();
|
||||
}
|
||||
return () => {
|
||||
rive?.off(EventType.Load, setViewModelValue);
|
||||
};
|
||||
}, [rive, userParameters]);
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
91
src/hooks/useViewModelInstance.ts
Normal file
91
src/hooks/useViewModelInstance.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
EventType,
|
||||
Rive,
|
||||
ViewModel,
|
||||
ViewModelInstance,
|
||||
} from '@rive-app/canvas';
|
||||
import { UseViewModelInstanceParameters } from '../types';
|
||||
|
||||
const defaultParams: UseViewModelInstanceParameters = {
|
||||
useDefault: false,
|
||||
useNew: true,
|
||||
name: '',
|
||||
};
|
||||
|
||||
const equal = (
|
||||
params: UseViewModelInstanceParameters | null,
|
||||
to: UseViewModelInstanceParameters | null
|
||||
): boolean => {
|
||||
if (!params || !to) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
params.useDefault !== to.useDefault ||
|
||||
params.useNew !== to.useNew ||
|
||||
params.name !== to.name
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook for fetching a view model instance.
|
||||
*
|
||||
* @param rive - Rive instance
|
||||
* @param userParameters - Parameters to load view model instance
|
||||
* @returns
|
||||
*/
|
||||
export default function useViewModel(
|
||||
rive: Rive | null,
|
||||
viewModel: ViewModel | null,
|
||||
userParameters?: UseViewModelInstanceParameters
|
||||
) : ViewModelInstance | null {
|
||||
const [viewModelInstance, setViewModelInstance] =
|
||||
useState<ViewModelInstance | null>(null);
|
||||
const currentParams = useRef<UseViewModelInstanceParameters | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const parameters = {
|
||||
...defaultParams,
|
||||
...userParameters,
|
||||
};
|
||||
|
||||
function setInstance(instance: ViewModelInstance | null) {
|
||||
setViewModelInstance(instance);
|
||||
rive!.setDataContextFromInstance(instance);
|
||||
currentParams.current = parameters;
|
||||
}
|
||||
function getViewModelInstance(): ViewModelInstance | null {
|
||||
if (viewModel) {
|
||||
if (parameters.useDefault) {
|
||||
return viewModel?.defaultInstance();
|
||||
} else if (parameters.name) {
|
||||
return viewModel?.instanceByName(parameters.name);
|
||||
} else if (parameters.useNew) {
|
||||
return viewModel?.instance();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function setViewModelValue() {
|
||||
if (!rive || !viewModel) {
|
||||
setViewModelInstance(null);
|
||||
} else {
|
||||
const instance = getViewModelInstance();
|
||||
setInstance(instance ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
if (!equal(parameters, currentParams.current)) {
|
||||
rive?.on(EventType.Load, setViewModelValue);
|
||||
setViewModelValue();
|
||||
}
|
||||
return () => {
|
||||
rive?.off(EventType.Load, setViewModelValue);
|
||||
};
|
||||
}, [rive, userParameters]);
|
||||
|
||||
return viewModelInstance;
|
||||
}
|
||||
99
src/hooks/useViewModelInstanceProperty.ts
Normal file
99
src/hooks/useViewModelInstanceProperty.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
EventType,
|
||||
ViewModelInstance,
|
||||
} from '@rive-app/canvas';
|
||||
import { UseViewModelInstanceValueParameters } from '../types';
|
||||
|
||||
const defaultParams: UseViewModelInstanceValueParameters = {
|
||||
viewModelInstance: null,
|
||||
};
|
||||
|
||||
const equal = (
|
||||
path: string[],
|
||||
params: UseViewModelInstanceValueParameters | null,
|
||||
to: HookArguments | null
|
||||
): boolean => {
|
||||
if (!params || !to) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
params.rive !== to.parameters.rive ||
|
||||
params.viewModelInstance !== to.parameters.viewModelInstance ||
|
||||
path.join('') !== to.path.join('')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
type HookArguments = {
|
||||
path: string[],
|
||||
parameters: UseViewModelInstanceValueParameters,
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook for fetching a view model instance value.
|
||||
*
|
||||
* @param name - name of the propery
|
||||
* @param path - Path to reach the required property
|
||||
* @param userParameters - Parameters to load view model instance number
|
||||
* @returns
|
||||
*/
|
||||
export default function useViewModelInstanceProperty(
|
||||
path: string[] = [],
|
||||
userParameters?: UseViewModelInstanceValueParameters
|
||||
): ViewModelInstance | null {
|
||||
const [viewModelInstance, setViewModelValue] =
|
||||
useState<ViewModelInstance | null>(null);
|
||||
const currentArguments = useRef<HookArguments | null>(
|
||||
null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const parameters = {
|
||||
...defaultParams,
|
||||
...userParameters,
|
||||
};
|
||||
|
||||
function getInstanceValue(): ViewModelInstance | null {
|
||||
let viewModelInstance: ViewModelInstance | null = null;
|
||||
if (userParameters?.viewModelInstance) {
|
||||
viewModelInstance = userParameters?.viewModelInstance;
|
||||
} else if (userParameters?.rive) {
|
||||
viewModelInstance = userParameters?.rive?.viewModelInstance;
|
||||
}
|
||||
if (viewModelInstance) {
|
||||
let index = 0;
|
||||
while (index < path?.length) {
|
||||
if(!viewModelInstance) {
|
||||
return null;
|
||||
}
|
||||
viewModelInstance = viewModelInstance?.viewModel(path[index]);
|
||||
index++;
|
||||
}
|
||||
return viewModelInstance;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function searchViewModelInstance() {
|
||||
const instanceValue = getInstanceValue();
|
||||
setViewModelValue(instanceValue);
|
||||
currentArguments.current = {
|
||||
parameters,
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
if (!equal(path, parameters, currentArguments.current)) {
|
||||
parameters.rive?.on(EventType.Load, searchViewModelInstance);
|
||||
searchViewModelInstance();
|
||||
}
|
||||
return () => {
|
||||
parameters.rive?.off(EventType.Load, searchViewModelInstance);
|
||||
};
|
||||
}, [path, userParameters]);
|
||||
|
||||
return viewModelInstance;
|
||||
}
|
||||
90
src/hooks/useViewModelNumber.ts
Normal file
90
src/hooks/useViewModelNumber.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
EventType,
|
||||
ViewModelInstance,
|
||||
ViewModelInstanceNumber,
|
||||
} from '@rive-app/canvas';
|
||||
import { UseViewModelInstanceNumberParameters } from '../types';
|
||||
import useViewModelInstanceProperty from './useViewModelInstanceProperty';
|
||||
|
||||
const defaultParams: UseViewModelInstanceNumberParameters = {
|
||||
viewModelInstance: null,
|
||||
initialValue: 0,
|
||||
};
|
||||
|
||||
const equal = (
|
||||
name: string,
|
||||
params: UseViewModelInstanceNumberParameters | null,
|
||||
viewModelInstance: ViewModelInstance | null,
|
||||
to: HookArguments | null
|
||||
): boolean => {
|
||||
if (!params || !to) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
params.initialValue !== to.parameters.initialValue ||
|
||||
name !== to.name ||
|
||||
viewModelInstance !== to.viewModelInstance
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
type HookArguments = {
|
||||
name: string,
|
||||
parameters: UseViewModelInstanceNumberParameters,
|
||||
viewModelInstance: ViewModelInstance | null,
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook for fetching a view model instance value.
|
||||
*
|
||||
* @param name - name of the propery
|
||||
* @param path - Path to reach the required property
|
||||
* @param userParameters - Parameters to load view model instance number
|
||||
* @returns
|
||||
*/
|
||||
export default function useViewModelNumber(
|
||||
name: string,
|
||||
path: string[] = [],
|
||||
userParameters?: UseViewModelInstanceNumberParameters
|
||||
): ViewModelInstanceNumber | null {
|
||||
const [viewModel, setViewModelValue] =
|
||||
useState<ViewModelInstanceNumber | null>(null);
|
||||
const currentArguments = useRef<HookArguments | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const viewModelInstance = useViewModelInstanceProperty(path, userParameters);
|
||||
|
||||
useEffect(() => {
|
||||
const parameters = {
|
||||
...defaultParams,
|
||||
...userParameters,
|
||||
};
|
||||
|
||||
function searchViewModelValue() {
|
||||
const instanceValue = viewModelInstance?.number(name) || null;
|
||||
if(instanceValue !== null && parameters.initialValue !== undefined) {
|
||||
instanceValue.value = parameters.initialValue;
|
||||
}
|
||||
setViewModelValue(instanceValue);
|
||||
currentArguments.current = {
|
||||
parameters,
|
||||
name,
|
||||
viewModelInstance,
|
||||
};
|
||||
}
|
||||
|
||||
if (!equal(name, parameters, viewModelInstance, currentArguments.current)) {
|
||||
parameters.rive?.on(EventType.Load, searchViewModelValue);
|
||||
searchViewModelValue();
|
||||
}
|
||||
return () => {
|
||||
parameters.rive?.off(EventType.Load, searchViewModelValue);
|
||||
};
|
||||
}, [name, userParameters, viewModelInstance]);
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
174
src/hooks/useViewModelProperties.ts
Normal file
174
src/hooks/useViewModelProperties.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
EventType,
|
||||
ViewModelInstance,
|
||||
ViewModelInstanceValue,
|
||||
} from '@rive-app/canvas';
|
||||
import { UseViewModelInstanceValueParameters } from '../types';
|
||||
|
||||
const defaultParams: UseViewModelInstanceValueParameters = {
|
||||
viewModelInstance: null,
|
||||
};
|
||||
|
||||
const equal = (
|
||||
properties: string[],
|
||||
params: UseViewModelInstanceValueParameters | null,
|
||||
to: HookArguments | null
|
||||
): boolean => {
|
||||
if (!params || !to) {
|
||||
return false;
|
||||
}
|
||||
if (properties.length !== to.properties.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < properties.length; i += 1) {
|
||||
if (properties[i] !== to.properties[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (
|
||||
params.rive !== to.parameters.rive ||
|
||||
params.viewModelInstance !== to.parameters.viewModelInstance
|
||||
) {
|
||||
console.log('paso 3');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
type HookArguments = {
|
||||
properties: string[];
|
||||
parameters: UseViewModelInstanceValueParameters;
|
||||
};
|
||||
|
||||
type PropertyResult = {
|
||||
query: string;
|
||||
property: ViewModelInstanceValue | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook for fetching a view model instance value.
|
||||
*
|
||||
* @param properties - list of queries properties
|
||||
* @param path - Path to reach the required property
|
||||
* @param userParameters - Parameters to load view model properties
|
||||
* @returns
|
||||
*/
|
||||
export default function useViewModelProperties(
|
||||
properties: string[],
|
||||
userParameters?: UseViewModelInstanceValueParameters
|
||||
): PropertyResult[] {
|
||||
const [result, setResult] = useState<PropertyResult[]>([]);
|
||||
const currentArguments = useRef<HookArguments | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const parameters = {
|
||||
...defaultParams,
|
||||
...userParameters,
|
||||
};
|
||||
|
||||
function getViewModelInstance() {
|
||||
if (parameters.viewModelInstance) {
|
||||
return parameters.viewModelInstance;
|
||||
} else if (parameters.rive) {
|
||||
return parameters.rive.viewModelInstance;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPropertyViewModelInstance(
|
||||
path: string
|
||||
): ViewModelInstance | null {
|
||||
const viewModelInstance: ViewModelInstance | null = getViewModelInstance();
|
||||
if(path === '') {
|
||||
return viewModelInstance;
|
||||
}
|
||||
return viewModelInstance?.viewModel(path) || null;
|
||||
}
|
||||
|
||||
function getProperty(
|
||||
viewModelInstance: ViewModelInstance | null,
|
||||
name: string
|
||||
): ViewModelInstanceValue | null {
|
||||
if (viewModelInstance) {
|
||||
const viewModelProperties = viewModelInstance.properties;
|
||||
const propertyData = viewModelProperties.find((candidate) => {
|
||||
if (candidate.name === name) {
|
||||
return candidate;
|
||||
}
|
||||
});
|
||||
if (propertyData !== null) {
|
||||
switch (propertyData!.type.toString()) {
|
||||
case 'number':
|
||||
return viewModelInstance.number(name);
|
||||
case 'string':
|
||||
return viewModelInstance.string(name);
|
||||
case 'boolean':
|
||||
return viewModelInstance.boolean(name);
|
||||
case 'enumType':
|
||||
return viewModelInstance.enum(name);
|
||||
case 'color':
|
||||
return viewModelInstance.color(name);
|
||||
case 'trigger':
|
||||
return viewModelInstance.trigger(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function searchViewModelValues() {
|
||||
const viewModelInstance = getViewModelInstance();
|
||||
if (!viewModelInstance) {
|
||||
setResult([]);
|
||||
} else {
|
||||
const result: PropertyResult[] = [];
|
||||
properties.forEach((propertyQuery) => {
|
||||
if (propertyQuery === '') {
|
||||
result.push({
|
||||
query: propertyQuery,
|
||||
property: null,
|
||||
});
|
||||
} else {
|
||||
const propertyParts = propertyQuery.split('/');
|
||||
const propertyName = propertyParts.pop();
|
||||
const propertyViewModelPath = propertyParts.join('/');
|
||||
const propertyViewModelInstance = getPropertyViewModelInstance(
|
||||
propertyViewModelPath
|
||||
);
|
||||
const property = getProperty(
|
||||
propertyViewModelInstance,
|
||||
propertyName!
|
||||
);
|
||||
if (property) {
|
||||
result.push({
|
||||
query: propertyQuery,
|
||||
property: property,
|
||||
});
|
||||
} else {
|
||||
result.push({
|
||||
query: propertyQuery,
|
||||
property: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
setResult(result);
|
||||
}
|
||||
currentArguments.current = {
|
||||
properties: properties,
|
||||
parameters: parameters,
|
||||
};
|
||||
}
|
||||
|
||||
if (!equal(properties, parameters, currentArguments.current)) {
|
||||
parameters.rive?.on(EventType.Load, searchViewModelValues);
|
||||
searchViewModelValues();
|
||||
}
|
||||
return () => {
|
||||
parameters.rive?.off(EventType.Load, searchViewModelValues);
|
||||
};
|
||||
}, [name, userParameters]);
|
||||
|
||||
return result;
|
||||
}
|
||||
23
src/index.ts
23
src/index.ts
@@ -1,10 +1,29 @@
|
||||
import Rive, { RiveProps } from './components/Rive';
|
||||
import useRive from './hooks/useRive';
|
||||
import useStateMachineInput from './hooks/useStateMachineInput';
|
||||
import useViewModel from './hooks/useViewModel';
|
||||
import useViewModelInstance from './hooks/useViewModelInstance';
|
||||
import useViewModelNumber from './hooks/useViewModelNumber';
|
||||
import useViewModelProperties from './hooks/useViewModelProperties';
|
||||
import useResizeCanvas from './hooks/useResizeCanvas';
|
||||
import useRiveFile from './hooks/useRiveFile';
|
||||
|
||||
export default Rive;
|
||||
export { useRive, useStateMachineInput, useResizeCanvas, useRiveFile , RiveProps };
|
||||
export { RiveState, UseRiveParameters, UseRiveFileParameters, UseRiveOptions } from './types';
|
||||
export {
|
||||
useRive,
|
||||
useStateMachineInput,
|
||||
useResizeCanvas,
|
||||
useRiveFile,
|
||||
useViewModel,
|
||||
useViewModelInstance,
|
||||
useViewModelNumber,
|
||||
useViewModelProperties,
|
||||
RiveProps,
|
||||
};
|
||||
export {
|
||||
RiveState,
|
||||
UseRiveParameters,
|
||||
UseRiveFileParameters,
|
||||
UseRiveOptions,
|
||||
} from './types';
|
||||
export * from '@rive-app/canvas';
|
||||
|
||||
21
src/types.ts
21
src/types.ts
@@ -3,6 +3,7 @@ import {
|
||||
RiveFile,
|
||||
RiveFileParameters,
|
||||
RiveParameters,
|
||||
ViewModelInstance,
|
||||
} from '@rive-app/canvas';
|
||||
import { ComponentProps, RefCallback } from 'react';
|
||||
|
||||
@@ -57,3 +58,23 @@ export type RiveFileState = {
|
||||
riveFile: RiveFile | null;
|
||||
status: FileStatus;
|
||||
};
|
||||
|
||||
export type UseViewModelParameters = {
|
||||
useDefault?: boolean;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export type UseViewModelInstanceParameters = {
|
||||
useNew?: boolean;
|
||||
useDefault?: boolean;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export type UseViewModelInstanceValueParameters = {
|
||||
viewModelInstance?: ViewModelInstance | null;
|
||||
rive?: Rive | null;
|
||||
};
|
||||
|
||||
export type UseViewModelInstanceNumberParameters = UseViewModelInstanceValueParameters & {
|
||||
initialValue?: number;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user