Я пытаюсь создать несколько потоков, которые будут представлять некоторые пользовательские жесты, такие как удержание и пролистывание.Я столкнулся с проблемой с takeUntil
при попытке реализовать поток удержания.
Основная концепция заключается в том, что я создаю 3 потока указателей вниз / перемещение / вверх и объединяю их для создания новых потоков.
Поток удержания должен сработать всякий раз, когда происходит событие «вниз», и в течение 500 мс не происходит значительного движения или события «вверх».
Соответствующий код для этого следующий:
// will fire once whenever there is significant movement after a down event
const drag_start_s = down_s.pipe(
switchMap(down_e =>
move_s.pipe(
filter(move_e => {
return distance(eventPosition(move_e), eventPosition(down_e)) < 30
}),
take(1),
),
),
)
const hold_s = down_s.pipe(
switchMap(() =>
timer(500).pipe(
takeUntil(drag_start_s), // this never works
takeUntil(up_s),
),
),
)
Если вы попытаетесь удерживать указатель на сером прямоугольнике, вы увидите, что событие удержания срабатывает через 500 мс.
Если вы поднимитесь слишком рано, он будет отменен на takeUntil(up_s)
.
Но если вы переместитесь и нажмете drag_start_s
, удержание не будет отменено, takeUntil(drag_start_s)
не работаетВы даже можете увидеть, как происходит событие перетаскивания в консоли перед удержанием, но удержание не отменяется.
Я не уверен, почему это происходит.
Остальная часть коданиже и рабочая коды и демонстрационная коробка
import React, { useMemo, useEffect } from "react"
import ReactDOM from "react-dom"
import { Subject, timer } from "rxjs"
import { takeUntil, switchMap, take, filter } from "rxjs/operators"
const distance = ([x1, y1], [x2, y2]) =>
Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
const eventPosition = e => [e.clientX, e.clientY]
function App() {
const down_s = useMemo(() => new Subject(), [])
const up_s = useMemo(() => new Subject(), [])
const move_s = useMemo(() => new Subject(), [])
useEffect(() => {
const subscriptions = []
const drag_start_s = down_s.pipe(
switchMap(down_e =>
move_s.pipe(
filter(move_e => {
return distance(eventPosition(move_e), eventPosition(down_e)) < 30
}),
take(1),
),
),
)
const hold_s = down_s.pipe(
switchMap(() =>
timer(500).pipe(
takeUntil(drag_start_s),
takeUntil(up_s),
),
),
)
subscriptions.push(drag_start_s.subscribe(() => console.log("drag start")))
subscriptions.push(hold_s.subscribe(() => console.log("Hold")))
return () => subscriptions.forEach(sub => sub.unsubscribe())
}, [])
return (
<div
onPointerDown={e => {
e.persist()
down_s.next(e)
}}
onPointerUp={e => {
e.persist()
up_s.next(e)
}}
onPointerMove={e => {
e.persist()
move_s.next(e)
}}
style={{
height: 100,
width: 200,
background: "grey",
border: "1px solid black",
}}
>
Interact with me
</div>
)
}
const rootElement = document.getElementById("root")
ReactDOM.render(<App />, rootElement)