Skip to content

Instantly share code, notes, and snippets.

@brandontrowe
Created August 8, 2017 01:11
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brandontrowe/dc0d9f09507f19809ed92aa8d769d184 to your computer and use it in GitHub Desktop.
Save brandontrowe/dc0d9f09507f19809ed92aa8d769d184 to your computer and use it in GitHub Desktop.
RxJS Swipe Left Observable
const tolerance = 200; // swipe distance in pixels
const swipeLeft$ = Rx.Observable.fromEvent(document, "touchstart")
// Switch to listen to touchmove to determine position
.switchMap(startEvent =>
Rx.Observable.fromEvent(document, "touchmove")
// Listen until "touchend" is fired
.takeUntil(Rx.Observable.fromEvent(document, "touchend"))
// Output the pageX location
.map(event => event.touches[0].pageX)
// Accumulate the pageX difference from the start of the touch
.scan((acc, pageX) => Math.round(startEvent.touches[0].pageX - pageX), 0)
// Take the last output and filter it to output only swipes
// greater than the defined tolerance
.takeLast(1)
.filter(difference => difference >= tolerance)
)
// All of your subscribe logic
swipeLeft$.subscribe(val => console.log(`You swiped left by ${val} pixels!`))
@MickL
Copy link

MickL commented Aug 12, 2021

Some thoughts:

  • For touchstart I would add a preventDefault(), otherwise it will open a context menu when holding on an image for example
  • For takeUntil(fromEvent(document, 'touchend')), wouldnt it make sense to add take(1) so the fromEvent completes? Otherwise there might accumulate a lot of observables to touchend.
  • .scan((acc, pageX) is missing typings: .scan((acc: number, pageX: number)
  • This might get updated to use pipe's.

@enten
Copy link

enten commented Feb 4, 2022

@MickL

export function fromSwipeLeft(tolerance: number = 200): Observable<number> {
  const doc = typeof window !== 'undefined' ? window.document : undefined;

  if (!doc) {
    return NEVER;
  }

  return fromEvent<TouchEvent>(doc, 'touchstart').pipe(
    switchMap(startEvent => {
      startEvent.preventDefault();
      startEvent.stopImmediatePropagation();

      return fromEvent<TouchEvent>(doc, 'touchmove').pipe(
        takeUntil(fromEvent(doc, 'touchend').pipe(take(1))),
        map(event => event.touches[0].pageX),
        scan((_acc: number, pageX: number) => Math.round(startEvent.touches[0].pageX - pageX), 0),
        takeLast(1),
        filter(difference => difference >= tolerance)
      );
    }),
  );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment