2

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?

JonasK
  • 149
  • 2
  • 8

2 Answers2

4

Unfortunately Michaels answer only works for mouse events, because the endtouch event doesn´t include clientX information.

Therefore I tweaked his answer:

ngAfterViewInit(): void {
  fromEvent<TouchEvent>(document, 'touchstart')
    .pipe(
      zipWith(
        fromEvent<TouchEvent>(document, 'touchend').pipe(
          withLatestFrom(fromEvent<TouchEvent>(document, 'touchmove'))
        )
      )
    )
    .subscribe(([touchstart, [_, touchmove]]) => {
      const xDiff =
        touchstart.touches[0].clientX - touchmove.touches[0].clientX;
      console.log(xDiff);
      if (Math.abs(xDiff) > 0.3 * document.body.clientWidth &&
          touchstart.timeStamp <= touchmove.timeStamp) {
        if (xDiff > 0) {
          console.log('right swipe');
        } else {
          console.log('left swipe');
        }
      }
    });
}
JonasK
  • 149
  • 2
  • 8
  • If this solves your issue, you could mark it as the answer. [Self answers](https://stackoverflow.com/help/self-answer) are encouraged in StackExchange. – ruth Jan 27 '22 at 12:37
  • @MichaelD Self answers are only possible after 48 hours, therefore I will mark it tomorrow – JonasK Jan 27 '22 at 15:53
2

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

ruth
  • 29,535
  • 4
  • 30
  • 57
  • 1
    Thanks for the help. Unfortunately only works for mouse events, because the endtouch event doesn´t include clientX information. I fixed it below. – JonasK Jan 27 '22 at 10:19