Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
RxJS Cookbook for Reactive Programming

You're reading from   RxJS Cookbook for Reactive Programming Discover 40+ real-world solutions for building async, event-driven web apps

Arrow left icon
Product type Paperback
Published in Mar 2025
Publisher Packt
ISBN-13 9781788624053
Length 310 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Nikola Mitrovic Nikola Mitrovic
Author Profile Icon Nikola Mitrovic
Nikola Mitrovic
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Handling Errors and Side Effects in RxJS 2. Building User Interfaces with RxJS FREE CHAPTER 3. Understanding Reactive Animation Systems with RxJS 4. Testing RxJS Applications 5. Performance Optimizations with RxJS 6. Building Reactive State Management Systems with RxJS 7. Building Progressive Web Apps with RxJS 8. Building Offline-First Applications with RxJS 9. Going Real-Time with RxJS 10. Building Reactive NestJS Microservices with RxJS 11. Index
12. Other Books You May Enjoy

Unlocking a phone with precision using RxJS-powered swipe gestures

How cool would it be to have a phone unlock pattern component? In this recipe, we’re going to build a component like that so that we can seamlessly react to every user touch swipe, orchestrate all user events, and unlock the phone once a correct combination of numbers is entered.

How to do it…

To create a phone unlock component, we’ll create UI controls representing number pads and identify key events to react to user actions. Once the user lifts their finger off the screen, we’ll compare the result with the correct pattern to unlock our phone.

Step 1 – Creating number pads

Our swipe-unlock.component.html file must contain the following markup for the swipe area and all phone buttons:

<div #swipeArea class="swipe-area">
    <button #one class="number">1</button>
    <button #two class="number">2</button>
    <button #three class="number">3</button>
    <button #four class="number">4</button>
    <button #five class="number">5</button>
    <button #six class="number">6</button>
    <button #seven class="number">7</button>
    <button #eight class="number">8</button>
    <button #nine class="number">9</button>
    <button #zero class="number">0</button>
</div>

With a little bit of CSS magic, we can see the component in the UI:

Figure 2.1: Phone swipe component

Figure 2.1: Phone swipe component

Meanwhile, in our swipe-unlock.component.ts file, we can reference various elements of the number pad’s UI so that we can manipulate any events that are performed on them:

@ViewChild('swipeArea')
swipeArea!: ElementRef;
@ViewChildren('one, two, three, four, five, six, seven,
              eight, nine, zero')
numbers!: QueryList<ElementRef>;

Step 2 – Identifying user touch events

What we’re interested in are the events where a user touches the screen, moves (swipes), and lifts their finger off the screen. We can create those streams of events like so:

const touchStart$ = fromEvent<TouchEvent>(
    this.swipeArea.nativeElement,
    'touchstart'
);
const touchMove$ = fromEvent<TouchEvent>(
    this.swipeArea.nativeElement,
    'touchmove'
);
const touchEnd$ = fromEvent<TouchEvent>(
    this.swipeArea.nativeElement,
    'touchend'
);

From here, we can react to these events and figure out the coordinates of a touch event, check if it’s intersecting with the number pad area, and highlight it in the UI:

const swipe$ = touchStart$.pipe(
    switchMap(() =>
        touchMove$.pipe(
            takeUntil(touchEnd$),
            map((touchMove) => ({
                x: touchMove.touches[0].clientX,
                y: touchMove.touches[0].clientY,
            }))
        )
    ),
);

Now, when we subscribe to those swipe coordinates, we can perform the required actions in sequence, such as selecting the number pad and creating a dot trail:

swipe$.pipe(
    tap((dot) => this.selectNumber(dot)),
    mergeMap((dot) => this.createTrailDot(dot)),
).subscribe();

Step 3 – Marking selected number pads

After getting the coordinates from each swipe, we can easily check whether it’s intersecting the area surrounding the number pad:

private selectNumber(dot: PixelCoordinates): void {
    this.numbersElement.forEach((number) => {
        if (
        dot.y > number.getBoundingClientRect().top &&
        dot.y < number.getBoundingClientRect().bottom &&
        dot.x > number.getBoundingClientRect().left &&
        dot.x < number.getBoundingClientRect().right
      ) {
            number.classList.add('selected');
            this.patternAttempt.push(parseInt(
                number.innerText)  
            );
        }
    });
}

By adding a selected class to each intersecting element, we can visually represent the selected number pads:

Figure 2.2: Marking the selected number pads

Figure 2.2: Marking the selected number pads

Step 4 – Creating a trail

With the help of the mergeMap operator, we can assemble all swipe events and their coordinates, create a dot in the DOM representing the trail of user action, and, after a certain delay, remove the trail from the DOM. Additionally, a nice performance consideration might be grouping many swipe events into one buffer. We can do this by using bufferCount, an operator that helps us to ensure optimal memory usage and computational efficiency:

private createTrailDot(
    dotCoordinates: PixelCoordinates
): Observable<string[]> {
    const dot = document.createElement('div');
    dot.classList.add('trail-dot');
    dot.style.left = `${dotCoordinates.x}px`;
    dot.style.top = `${dotCoordinates.y}px`;
    this.swipeArea.nativeElement.appendChild(dot);
    return of('').pipe(
        delay(1000),
        bufferCount(100, 50),
        finalize(() => dot.remove())
    );
}

Now, in our browser’s Dev Tools, we can inspect the creation of the trail by looking at the DOM:

Figure 2.3: Swipe trail

Figure 2.3: Swipe trail

Step 5 – Checking the result

Finally, at the end of the stream in the showMessage method, we must check whether the patternAttempt array, which was filled with each selected number pad, matches our pattern for unlocking the phone, which is 1 2 5 8 7.

Pattern matching

Since this is pattern matching and not exact password matching, the phone can be unlocked by inputting those buttons in any order, so long as those numbers in the pattern are included.

See also

  • The fromEvent function: https://rxjs.dev/api/index/function/fromEvent
  • The switchMap operator: https://rxjs.dev/api/operators/switchMap
  • The takeUntil operator: https://rxjs.dev/api/operators/takeUntil
  • The finalize operator: https://rxjs.dev/api/operators/finalize
  • The mergeMap operator: https://rxjs.dev/api/operators/mergeMap
  • The bufferCount operator: https://rxjs.dev/api/operators/bufferCount
You have been reading a chapter from
RxJS Cookbook for Reactive Programming
Published in: Mar 2025
Publisher: Packt
ISBN-13: 9781788624053
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime