'Angular swipe pages with native events and rxjs

I want to swipe between pages in angular using native events and rxjs. I found this and changed my code to work with rxjs:

ngAfterViewInit(): void {
  fromEvent<TouchEvent>(document, 'touchstart')
    .pipe(
      switchMap(() =>
        fromEvent<TouchEvent>(document, 'touchmove').pipe(
          takeUntil(fromEvent(document, 'touchend')),
          pairwise()
        )
      )
    )
    .subscribe(([touchstartEvent, touchendEvent]) => {
      const xDiff =
        touchstartEvent.touches[0].clientX - touchendEvent.touches[0].clientX;
      console.log(xDiff);

      if (Math.abs(xDiff) < 0.3 * document.body.clientWidth) {
        return;
      }

      if (xDiff > 0) {
        console.log('right swipe');
      } else {
        console.log('left swipe');
      }
    });
}

However my function in subscribe is called multiple times, which means my swipe is never executed because my treshhold 0.3 * document.body.clientWidth is never met. For debugging I added the following code:

fromEvent(document, 'touchend').subscribe((e) => {
  console.log('test2');
});

Then my console output is something like this, showing takeUntil not working as expected:

test -24.941162109375
test -8
test -9.41180419921875
test -8.4705810546875
test -9.88232421875
test2

What am I missing here?



Solution 1:[1]

Usage of the pairwise() operator is wrong here. Based on your expected emission [touchstartEvent, touchendEvent], the pairwise() isn't doing what you think it's doing. The actual values getting emitted are [touchmove[0], touchmove[1]].

I'd propose the following

  1. Use zipWith operator to merge touchstart and touchend and ignore the touchmove if it's value isn't required.

  2. Using return inside the subscription callback to stop the further execution doesn't look elegant to me. You could instead flip the condition of the if statement.

ngAfterViewInit(): void {
  fromEvent<TouchEvent>(document, 'touchstart')
    .pipe(zipWith(fromEvent<TouchEvent>(document, 'touchend')))
    .subscribe({
      next: ([touchstartEvent, touchendEvent]) => {
        const xDiff = 
          touchstartEvent.touches[0].clientX - touchendEvent.touches[0].clientX;
        console.log(xDiff);
        if (Math.abs(xDiff) > 0.3 * document.body.clientWidth) {
          if (xDiff > 0) {
            console.log('right swipe');
          } else {
            console.log('left swipe');
          }
        }
      }
    });
}

Working example (touch events replaced with mouse events): Stackblitz

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ruth