useTransform 與 MotionValue
use-gesture 的 handler 相對來說有效率,但不可避免的必須依賴 gesture-mode 對事件進行不同處理。
我們可以將目前圖片的 x, y 儲存在 useState 內交由 React 管理,不過我們實際上不需要因為圖片動了一個 pixel 就觸發 rerender。
我們也可以在各個 handler 內計算目前應該有的 gesture-mode,但這時除了邏輯重複外,也可能會有 race condition。
因此,這邊我們將 x, y 保留在 MotionValue 中,由 motion 管理動畫,並且藉由一個 useTransform 計算現在的 gesture-mode,必要時觸發 rerender。Handler 也可以直接 .get() 這個計算結果來用,不需要因為 state update 就重造 handler。
const gestureModeMotionValue = useTransform(() => {
if (zoomScaleMotionValue.get() > 1.0) {
return 'zoom';
}
const movement = Math.hypot(offsetXMotionValue.get(), offsetYMotionValue.get());
if (movement > DRAG_ACTIVATE_THRESHOLD) {
return 'pan-to-close';
}
return 'none';
});
// update gesture mode based on motion values
useMotionValueEvent(gestureModeMotionValue, 'change', (latest) => {
setGestureMode(latest as 'none' | 'zoom' | 'pan-to-close');
});
(註:在不需要 motion 的狀況下用 Zustand 應該也可行,單純用 React ref 則做不到根據狀態觸發更新的效果)