I implement calendar which can be veristically dragged by a mouse. I would like to have some inertia on mouse release. I was able to achieve this functionality by using use-gesture + react-spring libs.
This is how it looks
The problem I would like to solve is how to force the inertia to stop at the nearest row? Currently it can be stopped at the middle of the row.
Here is my code. I removed some parts for simplicity
[...]
const [animatedOffsetY, animatedOffsetYProps] = useSpring(() => ({
offsetY: 0,
config: {
decay: true
frequency: 5.5,
}
}))
[...]
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 {
animatedOffsetYProps.start({
offsetY: offsetY,
config: {
velocity: state.direction[1] * state.velocity[1],
}
})
}
}, {
from: [0, offsetY],
bounds: { bottom: 0, top: (totalHeight - containerHeight) * -1 },
rubberband: true
})
[...]
Update
I investigated, popmotion and framer-motion (which use popmotion under the hood) and they have modifyTarget prop which should solve my problem. I would prefer to stay with react-spring or custom solution.

For some reason using
decayin the springconfigis giving me lots of trouble. I've got it working just fine with a standardfriction/tensionbased spring.You want to animate to a position that is at the exact top of the nearest row when ending a drag. While dragging, ie.
if (state.down), you want to animate to the exact coordinates. When the mouse is released, ie.else, you want to compute the desired resting position and animate to that.I'm calling this value
snapOffsetY, and you can compute it by rounding how many boxes you've scrolled:Instead of calling
setOffsetYon every movement, I'm using a component state to store the last offset which was snapped to. So I'm only setting it on drag release.We can then use this
lastOffsetYas part of theboundscalculation. Initially,bottomis0meaning that we can't drag above the top. But you need to be able to scroll back up once you've scrolled down the list. So theboundsneed to change based on the last stopping position.My code looks like this:
Code Sandbox Demo