Skip to content

Instantly share code, notes, and snippets.

@natterstefan
Created March 20, 2020 07:31
Show Gist options
  • Save natterstefan/69c65b36c9497f3b05dcb53b018180eb to your computer and use it in GitHub Desktop.
Save natterstefan/69c65b36c9497f3b05dcb53b018180eb to your computer and use it in GitHub Desktop.
React | usescrolldrag hook
import React from 'react'
const App = () => {
const containerRef = useRef<HTMLDivElement | null>(null)
return (
<div ref={containerRef} {...useScrollDrag(containerRef)}>
{ /* content */ }
</div>
)
}
// ----------------------------------------------------------------------------
// Scroll Drag hook utility
// inspired by https://github.com/perrin4869/react-scroll-ondrag
// ----------------------------------------------------------------------------
const useScrollDrag = <T extends HTMLElement>(
ref: React.MutableRefObject<T | null>,
) => {
const internalState = useRef<{
lastMouseX: number
lastMouseY: number
isMouseDown: boolean
}>({
lastMouseX: 0,
lastMouseY: 0,
isMouseDown: false,
})
const scroll = useCallback(
(dx: number, dy: number) => {
const element = ref.current
if (element) {
element.scrollLeft = Math.min(
element.scrollWidth - element.clientWidth,
element.scrollLeft + dx,
) // eslint-disable-line no-param-reassign
element.scrollTop = Math.min(
element.scrollHeight - element.clientHeight,
element.scrollTop + dy,
) // eslint-disable-line no-param-reassign
}
},
[ref],
)
const onMouseDown: (
event: React.MouseEvent<T, MouseEvent>,
) => void = useCallback(e => {
internalState.current.isMouseDown = true
internalState.current.lastMouseX = e.clientX
internalState.current.lastMouseY = e.clientY
}, [])
const onMouseUp: (
event: React.MouseEvent<T, MouseEvent>,
) => void = useCallback(() => {
internalState.current.isMouseDown = false
}, [])
const onMouseMove: (
event: React.MouseEvent<T, MouseEvent>,
) => void = useCallback(
e => {
if (!internalState.current.isMouseDown) {
return
}
// diff is negative because we want to scroll in the opposite direction of the movement
const dx = -(e.clientX - internalState.current.lastMouseX)
const dy = -(e.clientY - internalState.current.lastMouseY)
internalState.current.lastMouseX = e.clientX
internalState.current.lastMouseY = e.clientY
scroll(dx, dy)
},
[scroll],
)
return {
onMouseDown,
onMouseUp,
onMouseMove,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment