默认情况下,我从 websocket 检索数据,每 2 秒采样一次。我使用 zustand 传播这些数据以进行状态管理。我有多个依赖于此数据的动画/组件。为了补偿 2 秒的间隔间隙,我计算了不是由 zustand 而是由 useState 处理的每一帧的数据。
我可以使用 CustomEvent 来更新每一帧的所有依赖组件,或者我可以使用自定义钩子,我认为这是我更喜欢的。
问题是每当 api > zustand > useStore 更新钩子的本地状态(样本)时,useFrame 都会丢失本地状态。
这是钩子...
/**
* merge two streams of similar data
* one stream comes from api and has a default sample-rate of about 2 seconds
* the second stream updates every frame change, roughly 60 frames / second
*/
export default () => {
const { dc, status, metrics } = useStore((state) => state.api);
const [sample, setSample] = useState(metrics.slice(-1)[0]);
const advanceSample = (duration, sample) => {
if (status === 'open') {
const radiansPerSecond = (sample.rpm / 60) * 2 * Math.PI;
const radiansPerFrame = radiansPerSecond * duration;
const r = (sample.r + radiansPerFrame) % (2 * Math.PI);
const active = degreesToActive[r * (180 / Math.PI)];
return {
ms: Date.now(),
r,
rpm: sample.rpm,
c1: { mA: mA(active, dc), t: t(active, dc) },
c2: { mA: mA(active, dc), t: t(active, dc) },
c3: { mA: mA(active, dc), t: t(active, dc) },
c4: { mA: mA(active, dc), t: t(active, dc) },
c5: { mA: mA(active, dc), t: t(active, dc) },
c6: { mA: mA(active, dc), t: t(active, dc) },
};
}
return sample;
};
useFrame(({ clock }, delta) => setSample(advanceSample(delta, sample)));
// const sampleEvent = new CustomEvent('sample', { detail: { sample } });
// document.dispatchEvent(sampleEvent);
return sample;
};
快速回答是否定的,在这种情况下,useFrame、zustand 和 React.useState 都没有冲突
我的解决方案是通过 zustand 在每一帧上更新托管状态,并通过状态管理器传播状态,如下所示......存储方法:
// increment state
next: (duration) => {
const { dc, metrics } = get().api;
const [sample] = metrics.slice(-1);
const ms = Date.now();
if (get().api.status === 'open') {
const radiansPerSecond = (sample.rpm / 60) * 2 * Math.PI;
const radiansPerFrame = radiansPerSecond * duration;
const r = (sample.r + radiansPerFrame) % (2 * Math.PI);
const active = activeCoilIndex[r * (180 / Math.PI)];
set({
api: {
...get().api,
metrics: [
...get().api.metrics,
{
ms,
r,
rpm: sample.rpm,
...range(1, 6).reduce((accumulator, coil, index) => ({ ...accumulator, [`c${coil}`]: { mA: mA(active, dc), t: t(active, dc) } }), {}),
},
].slice(-512),
},
});
}
然后在一个单独的组件中:
// increment state at generally 60fps
const nextSample = useStore((state) => state.api.next);
useFrame(({ clock }, delta) => nextSample(delta));
最后是传播:
// use current state within each component as follows
const unsubscribe = useStore.subscribe(
(metrics) => {
const [sample] = metrics.slice(-1);
if (crankshaftRef.current) crankshaftRef.current.rotation.z = sample.r;
const sqrRodLength = Math.pow(rodLength, 2);
const sqrCrankRadius = Math.pow(crankRadius, 2);
const calcMotion = (offsetRotation) => {
const radians = sample.r + offsetRotation;
const cosR = Math.cos(radians);
const sinR = Math.sin(radians);
const displacement = crankRadius * cosR + Math.sqrt(sqrRodLength - sqrCrankRadius * Math.pow(sinR, 2));
const moment = Math.asin(crankRadius / (rodLength / sinR)) * -1;
return [displacement, moment];
};
if (conrod1Ref.current && plunger1Ref.current) {
const [displacement, moment] = calcMotion(pistonsModels['piston1'].offsetRotation);
conrod1Ref.current.position.x = displacement;
conrod1Ref.current.rotation.z = moment;
plunger1Ref.current.position.x = displacement;
}
...
},
(state) => state.api.metrics
);