123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- import { useState, useRef, useCallback, useEffect } from 'react';
- import { fromEvent, Subscription } from 'rxjs';
- import { throttleTime } from 'rxjs/operators';
- import MobileDetect from 'mobile-detect';
- import { getAbsoluteCoordinate } from '../helpers/position';
- const defaultTime = 35;
- type CursorPosition = {
- x: number | null;
- y: number | null;
- clientX: number | null;
- clientY: number | null;
- screenX: number | null;
- screenY: number | null;
- };
- type UseCursorPositionType = (
- throttleTime?: number,
- ) => [
- CursorPosition,
- (element: HTMLElement | SVGSVGElement | SVGCircleElement | null) => void,
- ];
- const initialState = {
- x: null,
- y: null,
- clientX: null,
- clientY: null,
- screenX: null,
- screenY: null,
- };
- const useCursorPosition: UseCursorPositionType = (time = defaultTime) => {
- const [state, setState] = useState<CursorPosition>(initialState);
- const [element, setElement] = useState<
- HTMLElement | SVGSVGElement | SVGCircleElement | null
- >(null);
- const entered = useRef(false);
- const isDown = useRef(false);
- const onMouseMoveEvent = useCallback(
- (e: MouseEvent | Event): void => {
- if (element) {
- const { clientX, clientY, screenX, screenY } = e as MouseEvent;
- const coordinate = getAbsoluteCoordinate(element, e as MouseEvent);
- setState({
- x: coordinate.x,
- y: coordinate.y,
- clientX,
- clientY,
- screenX,
- screenY,
- });
- }
- },
- [element],
- );
- const onTouchMoveEvent = useCallback(
- (e: TouchEvent | Event): void => {
- if (element) {
- const {
- clientX,
- clientY,
- screenX,
- screenY,
- } = (e as TouchEvent).touches[0];
- const coordinate = getAbsoluteCoordinate(element, e as TouchEvent);
- setState({
- x: coordinate.x,
- y: coordinate.y,
- clientX,
- clientY,
- screenX,
- screenY,
- });
- }
- },
- [element],
- );
- useEffect(() => {
- const md = new MobileDetect(window.navigator.userAgent);
- let mouseSubscription: Subscription | null = null;
- let touchSubscription: Subscription | null = null;
- const onEnter = (): void => {
- entered.current = true;
- };
- const onLeave = (): void => {
- entered.current = false;
- setState(initialState);
- };
- const onDown = (e: Event): void => {
- entered.current = true;
- isDown.current = true;
- onMouseMoveEvent(e);
- };
- const onTouch = (e: Event): void => {
- entered.current = true;
- isDown.current = true;
- onTouchMoveEvent(e);
- };
- const onUp = (): void => {
- entered.current = false;
- isDown.current = false;
- setState(initialState);
- if (mouseSubscription) mouseSubscription.unsubscribe();
- if (touchSubscription) touchSubscription.unsubscribe();
- };
- if (element) {
- const addEvent = element.addEventListener.bind(element);
- if (md.mobile() || md.tablet()) {
- touchSubscription = fromEvent(element, 'touchmove')
- .pipe(throttleTime(time))
- .subscribe(onTouchMoveEvent);
- addEvent('touchstart', onTouch);
- addEvent('touchend', onUp);
- } else {
- mouseSubscription = fromEvent(element, 'mousemove')
- .pipe(throttleTime(time))
- .subscribe(onMouseMoveEvent);
- addEvent('mouseenter', onEnter);
- addEvent('mouseleave', onLeave);
- addEvent('mousedown', onDown);
- addEvent('mouseup', onUp);
- }
- }
- return (): void => {
- if (element) {
- const removeEvent = element.removeEventListener.bind(element);
- if (md.mobile() || md.tablet()) {
- removeEvent('touchstart', onTouch);
- removeEvent('touchend', onUp);
- } else {
- removeEvent('mouseenter', onEnter);
- removeEvent('mouseleave', onLeave);
- removeEvent('mousedown', onDown);
- removeEvent('mouseup', onUp);
- }
- }
- };
- }, [element]);
- return [state, setElement];
- };
- export default useCursorPosition;
|