نحوه ردیابی حرکت ماوس و لمس در حالت فشرده با React

✨ در یوتیوب تماشا کنید | 🐙 گیت هاب | 🎮 نسخه ی نمایشی
وقتی رابطهای کاربری تعاملی پیچیدهای مانند لغزنده، ویرایشگر یا انتخابگر رنگ میسازیم، اغلب نیاز داریم که حرکت ماوس یا لمس را در حالت فشرده ردیابی کنیم. اجازه دهید یک جزء انتزاعی با کاربرد آسان را که دقیقاً برای این منظور طراحی شده است به اشتراک بگذارم.
جزء PressTracker
مؤلفه دو ویژگی دریافت می کند:
-
render– تابعی که جزء مصرف کننده باید از آن برای ارائه محتوا استفاده کند. یک شی را باpropsوpositionخواص اینpropsفیلد حاویrefو دستههای شروع موس و لمسی، باید به عنصر ظرف منتقل شود. اینpositionیک نقطه باxوyمختصات در محدوده 0 تا 1. نشان دهنده موقعیت نسبی مکان نما در داخل ظرف است. در تمام موارد عملی من، نیاز به دانستن موقعیت نسبی داشتم، بنابراین تصمیم گرفتم از آن به عنوان پیش فرض استفاده کنم. -
onChange– تابعی که با تغییر موقعیت فراخوانی می شود. یک شی را با همان دریافت می کندpositionبحث و جدل. توجه داشته باشید زمانی که منشاء پرس خارج از ظرف باشد، موقعیت آن خواهد بودnull.
import { Point } from "lib/entities/Point"
import { useBoundingBox } from "lib/shared/hooks/useBoundingBox"
import { enforceRange } from "lib/shared/utils/enforceRange"
import {
MouseEvent,
MouseEventHandler,
ReactNode,
TouchEvent,
TouchEventHandler,
useCallback,
useEffect,
useState,
} from "react"
import { useEvent } from "react-use"
interface ContainerProps {
onMouseDown: MouseEventHandler<HTMLElement>
onTouchStart: TouchEventHandler<HTMLElement>
ref: (node: HTMLElement | null) => void
}
interface ChangeParams {
position: Point | null
}
interface RenderParams extends ChangeParams {
props: ContainerProps
}
interface PressTrackerProps {
render: (props: RenderParams) => ReactNode
onChange?: (params: ChangeParams) => void
}
export const PressTracker = ({ render, onChange }: PressTrackerProps) => {
const [container, setContainer] = useState<HTMLElement | null>(null)
const box = useBoundingBox(container)
const [position, setPosition] = useState<Point | null>(null)
const handleMove = useCallback(
({ x, y }: Point) => {
if (!box) return
const { left, top, width, height } = box
setPosition({
x: enforceRange((x - left) / width, 0, 1),
y: enforceRange((y - top) / height, 0, 1),
})
},
[box]
)
const handleMouse = useCallback(
(event: MouseEvent) => {
handleMove({ x: event.clientX, y: event.clientY })
},
[handleMove]
)
const handleTouch = useCallback(
(event: TouchEvent) => {
const touch = event.touches[0]
if (touch) {
handleMove({ x: touch.clientX, y: touch.clientY })
}
},
[handleMove]
)
useEffect(() => {
if (onChange) {
onChange({ position })
}
}, [onChange, position])
const clearPosition = useCallback(() => {
setPosition(null)
}, [])
useEvent("mouseup", position ? clearPosition : undefined)
useEvent("touchend", position ? clearPosition : undefined)
useEvent("mousemove", position ? handleMouse : undefined)
useEvent("touchmove", position ? handleTouch : undefined)
return (
<>
{render({
props: {
ref: setContainer,
onMouseDown: handleMouse,
onTouchStart: handleTouch,
},
position: position,
})}
</>
)
}
این PressTracker جزء عنصر ظرف را در useState و از useBoundingBox قلاب را برای به دست آوردن موقعیت و اندازه آن. برای شروع فرآیند ردیابی، جزء مصرف کننده باید رندر شود PressTracker جزء و پروپ های لازم را به عنصر ظرف منتقل کنید.
<PressTracker
render={({ props, position }) => (
<Container {...props}>
{position && (
<Highlight
style={{
width: toPercents(position.x),
height: toPercents(position.y),
}}
/>
)}
</Container>
)}
/>
ما موقعیت را روی هر یک از ماوس به پایین در رویدادهای شروع لمسی تنظیم می کنیم. بنابراین هنگامی که کاربر با کانتینر تعامل کرد، ما شروع به ردیابی موقعیت مکان نما می کنیم useEvent قلاب از react-use کتابخانه گوش می دهد برای mouseup و touchend رویدادها برای توقف ردیابی، و همچنین mousemove و touchmove رویدادها برای به روز رسانی موقعیت مکان نما. این handleMove تابع، مختصات مطلق را از رویدادهای لمسی و ماوس تبدیل می کند، و اطمینان می دهد که مختصات در محدوده باقی می مانند 0 به 1، حتی زمانی که مکان نما خارج از ظرف است.



