Skip to content

Commit

Permalink
inital work for data binding hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
bodymovin committed Jan 8, 2025
1 parent e172e0f commit c3b6a69
Show file tree
Hide file tree
Showing 7 changed files with 570 additions and 2 deletions.
74 changes: 74 additions & 0 deletions src/hooks/useViewModel.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 91 additions & 0 deletions src/hooks/useViewModelInstance.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 99 additions & 0 deletions src/hooks/useViewModelInstanceProperty.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 90 additions & 0 deletions src/hooks/useViewModelNumber.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit c3b6a69

Please sign in to comment.