如何使用react-spring+decay让拖拽惯性停在最近的日历行位置
解决方案
你可以通过预先计算惯性滚动的最终目标位置,再将目标位置对齐到最近的行位置实现吸附效果,无需引入额外库,仅需修改现有react-spring逻辑即可。
步骤1:先定义基础常量
首先确定你的日历单行高度,以及提前声明滚动边界:
// 替换为你的日历组件实际单行高度 const ROW_HEIGHT = 60 // 和你现有useDrag配置的bounds保持一致 const MAX_SCROLL_TOP = (totalHeight - containerHeight) * -1 const MIN_SCROLL_TOP = 0
步骤2:修改松手后的动画逻辑
在useDrag的松手回调中,先计算惯性滚动的预估最终位置,对齐到最近的行后再启动动画,既保留原有的惯性手感,又能保证最终停在整行位置:
const bind = useDrag((state) => { const [, currentY] = state.movement const [, lastOffsetY] = state.lastOffset const offsetY = lastOffsetY + currentY if (state.down) { setOffsetY(offsetY) animatedOffsetYProps.set({ offsetY: offsetY }) } else { // 计算惯性滚动的预估最终偏移量:react-spring默认decay规则下最终位移为 速度/0.1 const velocity = state.direction[1] * state.velocity[1] const expectedFinalOffset = offsetY + velocity / 0.1 // 对齐到最近的行位置 const alignedOffset = Math.round(expectedFinalOffset / ROW_HEIGHT) * ROW_HEIGHT // 限制对齐后位置不超出滚动边界 const clampedAlignedOffset = Math.max(MAX_SCROLL_TOP, Math.min(MIN_SCROLL_TOP, alignedOffset)) // 启动弹簧动画到对齐后的位置,保留初始速度保证惯性手感 animatedOffsetYProps.start({ offsetY: clampedAlignedOffset, config: { velocity: velocity, frequency: 5.5, damping: 22 // 可调整阻尼控制吸附回弹手感,数值越大回弹越弱 } }) } }, { from: [0, offsetY], bounds: { bottom: 0, top: (totalHeight - containerHeight) * -1 }, rubberband: true })
步骤3:调整初始useSpring配置
移除原配置中的decay: true,改为普通弹簧配置即可:
const [animatedOffsetY, animatedOffsetYProps] = useSpring(() => ({ offsetY: 0, config: { frequency: 5.5, } }))
可选优化
如果你的日历行高是动态不固定的,可以提前计算所有行的顶部偏移量数组,然后将expectedFinalOffset和数组中的值做对比,取最近的偏移量作为对齐目标即可。
内容的提问来源于stack exchange,提问作者Roman Mahotskyi
相关产品推荐
相关产品推荐

