
Содержание статьи
Анри Литтл – Бейль

Наверное, все мы знакомы с концепцией джойстика.
Начинаем держать ручку джойстика, двигаем ручку, а когда отпускаем, ручка плавно возвращается в исходное положение.
Теперь что, если мы хотим создать какой-то программный компонент, имитирующий поведение джойстика в браузере?
Что ж, с RxJS это оказывается достаточно просто. И это тоже интересное упражнение для подтверждения вашего реактивного мышления. Вы можете перейти прямо к коду здесь, если хотите, иначе продолжайте читать и посмотрите, что мы можем сделать.
Какие события нас интересуют?
Поведение джойстика можно увидеть как ряд событий, объединенных определенным образом.
Первое событие, которое нас интересует, когда пользователь нажимает мышь на ручку (mousedown
) – ручка является лишь центральной частью изображения джойстика.
Если вы держите мышь нажатой, вы можете перемещаться, и вы увидите, как двигается ручка соответственно. mousemove
Поэтому события мыши – вторая серия событий, которые мы хотим зафиксировать.
Наконец, мы должны учитывать, когда пользователь отпускает мышь (mouseup
), поскольку это событие, заставляющее ручку джойстика вернуться в исходное положение.

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

Если мы сможем построить такой поток событий, мы в хорошем положении для достижения нашей цели – то есть реализовать программный компонент Joystick для браузера с помощью RxJS.
Строительные блоки из RxJS
На самом деле браузер предоставляет нам уведомление о интересующих нас событиях: mousedown
событие на элементе DOM, представляющем ручку джойстика, и mousemove
и mouseup
события на уровне документа DOM.
RxJS, со своей стороны, поставляется с функцией fromEvent
что позволяет нам создать Observable из события браузера.

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

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

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

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

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

Была бы уведомлена только первая последовательность событий перемещения, а затем поток событий был бы закрыт. Больше никаких событий для джойстика.
Сейчас пришло время побочных эффектов
Мы научились создавать поток всех событий, касающихся джойстика. Чтобы сделать что-то полезное с этим потоком, нам нужно увязать события с определенным действием, которое мы хотим выполнить. Более конкретно:
- когда мы чувствуем а
mousemove
событий мы должны изменить положение дескриптора в браузере - когда мы чувствуем а
mouseup
В этом случае мы должны осторожно повернуть ручку в исходное положение, установив определенный стиль перехода - когда мы чувствуем а
mousedown
мы должны сбросить стиль перехода
Но осторожно. Не все такие mousemove
события, не все есть mouseup
события, и не все есть mousedown
события. Только те, которые принадлежат к множеству «соответствующие события для джойстика». Нас, например, не все интересуют mousemove
события, происходящие до активации джойстика (нажатие мыши на ручке) или после отпускания джойстика.

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

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.