Open tommedema opened 5 years ago
I've come up with this solution for now. Can you think of a case where this logic would fail?
useMousePosition.ts
/**
* Inspired by: https://github.com/sandiiarov/use-events/blob/master/src/useMousePosition/index.tsx
*
* Enhanced to take scrolling into consideration.
* @see https://github.com/sandiiarov/use-events/issues/177
*/
import React, { useEffect, useState } from 'react'
const useMousePosition = (): [
number,
number,
{ onMouseMove: (e: React.MouseEvent) => void }
] => {
const [lastScrollX, setLastScrollX] = useState(
(window.document.scrollingElement && window.document.scrollingElement.scrollLeft) || 0
)
const [lastScrollY, setLastScrollY] = useState(
(window.document.scrollingElement && window.document.scrollingElement.scrollTop) || 0
)
const [x, setX] = useState(0)
const [y, setY] = useState(0)
useEffect(() => {
const onScroll = () => {
const scrollElement = window.document.scrollingElement
if (scrollElement) {
const {
scrollTop,
scrollLeft
} = scrollElement
setX(x + scrollLeft - lastScrollX)
setY(y + scrollTop - lastScrollY)
setLastScrollX(scrollLeft)
setLastScrollY(scrollTop)
}
}
window.document.addEventListener('scroll', onScroll, true)
return () => {
window.document.removeEventListener('scroll', onScroll, true)
}
})
const bindings = {
onMouseMove: ({ nativeEvent: { offsetX, offsetY } }: React.MouseEvent) => {
setX(offsetX)
setY(offsetY)
}
}
return [x, y, bindings]
}
export default useMousePosition
Since useMousePosition returns the mouse position in the document (and not the window), the position changes as the user scrolls. Yet this is not recognized by the hook. See this video:
https://www.loom.com/share/7009214d57ba44d8b6bc2e0bbdbf04e1
useMousePosition should therefore update the X,Y when onscroll is fired. I also think it would be good to include an option to only look at the mouse position in the window, and not the document, as that is often more useful.