123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- import {
- useState, useRef, useCallback, useEffect,
- } from 'react';
- import { fromEvent, Subscription } from 'rxjs';
- import {
- throttleTime,
- } from 'rxjs/operators';
- 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(() => {
- 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: any): void => {
- entered.current = true;
- isDown.current = true;
- onMouseMoveEvent(e);
- };
- const onTouch = (e: any): 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) {
- mouseSubscription = fromEvent(element, 'mousemove').pipe(throttleTime(time)).subscribe(onMouseMoveEvent);
- touchSubscription = fromEvent(element, 'touchmove').pipe(throttleTime(time)).subscribe(onTouchMoveEvent);
- const addEvent = element.addEventListener.bind(element);
- addEvent('mouseenter', onEnter);
- addEvent('mouseleave', onLeave);
- addEvent('mousedown', onDown);
- addEvent('touchstart', onTouch);
- addEvent('mouseup', onUp);
- addEvent('touchend', onUp);
- }
- return (): void => {
- if (element) {
- const removeEvent = element.removeEventListener.bind(element);
- removeEvent('mouseenter', onEnter);
- removeEvent('mouseleave', onLeave);
- removeEvent('mousedown', onDown);
- removeEvent('touchstart', onTouch);
- removeEvent('mouseup', onUp);
- removeEvent('touchend', onUp);
- }
- };
- }, [element]);
- return [state, setElement];
- };
- export default useCursorPosition;
|