Как построить реактивный джойстик в виде единого потока RxJS Observable

1656523334 kak postroit reaktivnyj dzhojstik v vide edinogo potoka rxjs observable

Анри Литтл – Бейль

5cDleuly3iJJ-3OmyNvzqpEjh4SLGgvlLo-E

Наверное, все мы знакомы с концепцией джойстика.

Начинаем держать ручку джойстика, двигаем ручку, а когда отпускаем, ручка плавно возвращается в исходное положение.

Теперь что, если мы хотим создать какой-то программный компонент, имитирующий поведение джойстика в браузере?

Что ж, с RxJS это оказывается достаточно просто. И это тоже интересное упражнение для подтверждения вашего реактивного мышления. Вы можете перейти прямо к коду здесь, если хотите, иначе продолжайте читать и посмотрите, что мы можем сделать.

Какие события нас интересуют?

Поведение джойстика можно увидеть как ряд событий, объединенных определенным образом.

Первое событие, которое нас интересует, когда пользователь нажимает мышь на ручку (mousedown) – ручка является лишь центральной частью изображения джойстика.

Если вы держите мышь нажатой, вы можете перемещаться, и вы увидите, как двигается ручка соответственно. mousemove Поэтому события мыши – вторая серия событий, которые мы хотим зафиксировать.

Наконец, мы должны учитывать, когда пользователь отпускает мышь (mouseup), поскольку это событие, заставляющее ручку джойстика вернуться в исходное положение.

nl-lPdU3SnW8QP6FtuGXwFhwftNOJvwzy2fF
Ответные события для корпуса джойстика

После отпускания ручки всю последовательность можно повторить. Мышь нажимают на ручку, затем ее перемещают, затем отпускают. Снова и снова.

Это повторение можно рассматривать как поток событий. Можно сказать, что поведение джойстика регулируется этим потоком событий.

PSeMoBEzyvtgqnNjQFIMNhKZBVk0UyhSr5Ii
Поток событий джойстика

Если мы сможем построить такой поток событий, мы в хорошем положении для достижения нашей цели – то есть реализовать программный компонент Joystick для браузера с помощью RxJS.

Строительные блоки из RxJS

На самом деле браузер предоставляет нам уведомление о интересующих нас событиях: mousedown событие на элементе DOM, представляющем ручку джойстика, и mousemove и mouseup события на уровне документа DOM.

RxJS, со своей стороны, поставляется с функцией fromEvent что позволяет нам создать Observable из события браузера.

E5gArW8EsLVN47YdlzZQDfsr-J-v7wRYha2w
Создайте Observable с помощью функции RxJS `fromEvent`

Используя этот механизм, мы можем создать три потока событий, которые станут строительными блоками нашего решения: mouse_DOWN_Obs, mouse_MOVE_Obs, mouse_UP_Obs.

18oYzRw8TkzIoscKyhouvoJEe08erjL0cnfL
Потоки событий, являющиеся нашими строительными блоками

Но это только наши строительные блоки. Нам нужно что-то делать с ними, чтобы получить то, что мы хотим: нам нужно все игнорировать mousemove события, происходящие перед первыми mousedown а затем игнорировать все mousemove события, которые происходят после следующего mouseup. Затем повторяем все это снова, когда появится новый mousedown происходит событие. Эти составляют «поток событий для джойстика».

57-sjEw-zvOm53BVKUeUh9KaC55f7ytcUHA-
Поток событий джойстика, созданный из строительных блоков

Преобразование Observables с помощью композиции операторов

Все начинается, когда пользователь нажимает мышью на ручку джойстика, т.е. mouse_DOWN_Obs. Мы можем назвать его источником Observable.

Как только мы получим уведомление о событии от mouse_DOWN_Obs мы должны переключатель и начните слушать mouse_MOVE_Obs.

s3esbte2H8imrlxNz2pHYtOGaXC7NMYd-Ytt
Первое преобразование с помощью switchMap

Может показаться, что мы не многого добились, но на самом деле мы сейчас в положении, где можем брать в mouse_MOVE_Obs оповещение пока мы слышим от mouse_UP_Obs. На этом этапе мы останавливаемся, чтобы перезапуститься при следующем уведомлении mouse_DOWN_Obs.

zfSEa0bNUZx0i52yfZXkpg1-FqxBSGaRENJy
Второе преобразование из takeUntil

Обратите внимание, что мы применяем takeUntil к mouse_MOVE_Obs, потому что это Observable, который мы хотим завершить. Если бы мы подали заявку на уровень выше, до mouse_DOWN_Obsвот что бы произошло:

ZS1GyApmXIMmbSunvKPbVTBWGsJf1TZmS3FT

Была бы уведомлена только первая последовательность событий перемещения, а затем поток событий был бы закрыт. Больше никаких событий для джойстика.

Сейчас пришло время побочных эффектов

Мы научились создавать поток всех событий, касающихся джойстика. Чтобы сделать что-то полезное с этим потоком, нам нужно увязать события с определенным действием, которое мы хотим выполнить. Более конкретно:

  • когда мы чувствуем а mousemove событий мы должны изменить положение дескриптора в браузере
  • когда мы чувствуем а mouseup В этом случае мы должны осторожно повернуть ручку в исходное положение, установив определенный стиль перехода
  • когда мы чувствуем а mousedown мы должны сбросить стиль перехода

Но осторожно. Не все такие mousemove события, не все есть mouseup события, и не все есть mousedown события. Только те, которые принадлежат к множеству «соответствующие события для джойстика». Нас, например, не все интересуют mousemove события, происходящие до активации джойстика (нажатие мыши на ручке) или после отпускания джойстика.

jMq3dPUHgqbRRovbFLXCKmSG9XueC-dIMa0V
Где нам нужны побочные эффекты

Давайте вернемся к нашему основному пути размышления. Нам нужно что-то делать при наступлении каких-то событий. Что-то изменяющее состояние системы. В нашем случае это положение дескриптора на обозревателе. в функциональный программирование термины, которые называются побочные эффектыто есть функции, изменяющие состояние системы.

jHOP52U5BPDPSJOF2BLBvcHzCg6ZW7ZZ9m8J
Побочные эффекты как функции — Функции как побочные эффекты

RxJS дает нам два способа реализации побочные эффекты.

Первый из них subscribe метод наблюдаемого. Второй – это tap оператор, ранее известный как doкакой «выполняет а посторонний эффект для каждого излучения источника Observable, но возвращает Observable, идентичный источнику» — тот посторонний эффект определяется функцией, к которой передается tap как параметр. tap это метод, который мы собираемся использовать.

В конце концов это ядро ​​кода, который реализует наш реактивный джойстик.

const handle = document.getElementById("handle");const mouse_DOWN_Obs = rxjs.fromEvent(handle, 'mousedown');const mouse_MOVE_Obs = rxjs.fromEvent(document, 'mousemove');const mouse_UP_Obs = rxjs.fromEvent(document, 'mouseup');
function activateJoytick() {  mouse_DOWN_Obs.pipe(    rxjs.operators.tap(() => joystickActivated()),    rxjs.operators.switchMap(() => mouse_MOVE_Obs.pipe(      rxjs.operators.takeUntil(mouse_UP_Obs.pipe(        rxjs.operators.tap(() => joystickReleased())      )),    )),    rxjs.operators.tap(event => showHandleInNewPosition(event))  )  .subscribe();}

Пример кода

Вы можете найти пример кода здесь, где вы можете сравнить реализацию RxJS с реализацией, созданной с помощью чистого JavaScript.

Добавить комментарий

Ваш адрес email не будет опубликован.