RxJS takeUntil не работает при попытке составить события указателя - PullRequest
0 голосов
/ 28 января 2019

Я пытаюсь создать несколько потоков, которые будут представлять некоторые пользовательские жесты, такие как удержание и пролистывание.Я столкнулся с проблемой с 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)

1 Ответ

0 голосов
/ 28 января 2019

Я не совсем понимаю, почему это не работает сам.Я думаю, что проблема связана с использованием switchMap и временем создания вашей timer Observable, и тот факт, что вы используете down_s дважды и один (hold_s), зависит от другого (* 1005)*).(использование takeUntil(drag_start_s) работает, если вы избавляетесь от switchMap, но затем ваш hold_s завершается, а это не то, что вам нужно)

Вы можете обойти эту проблему, создав новый Subject, которыйВы запускаете события перетаскивания.

const drag_s = useMemo(() => new Subject(), []);

const hold_s = down_s.pipe(
  switchMap(() =>
    timer(500).pipe(
      takeUntil(drag_s),
      takeUntil(up_s),
    ),
  ),
)

subscriptions.push(drag_start_s.subscribe(() => drag_s.next()));

Вы также должны добавить takeUntil(up_s) к своему move_s в drag_start_s, чтобы остановить события перетаскивания после события мыши.Я также думаю, что вы хотели использовать > вместо < при сравнении расстояний.

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 // <--- here
      }),
      takeUntil(up_s), // <--- here
      take(1),
    ),
  ),
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...