Created
March 20, 2020 07:31
-
-
Save natterstefan/69c65b36c9497f3b05dcb53b018180eb to your computer and use it in GitHub Desktop.
React | usescrolldrag hook
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
const App = () => { | |
const containerRef = useRef<HTMLDivElement | null>(null) | |
return ( | |
<div ref={containerRef} {...useScrollDrag(containerRef)}> | |
{ /* content */ } | |
</div> | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ---------------------------------------------------------------------------- | |
// 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