2

I have implemented a way to show a button only on up-scroll: The way how I implemented, it feels like, it takes way to many computations, because the logic listens for every scroll-event. Maybe some of you nerds, have a better approach than mine. :) The Requirement is: When the Page initially loads or on up-scroll the button should be displayed in the UI. On down-scroll the button should be hidden.

I used Angulars @HostListener(..) to listen for the scroll event.

Component

  public lastScrolledHeight: number = 0;
  public showAddButton: boolean = true;

  @HostListener('window:scroll', ['$event']) onScroll(event) {
    const window = event.path[1];
    const currentScrollHeight = window.scrollY;
    console.log(currentScrollHeight);

    if (currentScrollHeight > this.lastScrolledHeight) {
      this.showAddButton = false;
      console.log('should NOT show button');
    } else {
      this.showAddButton = true;
      console.log('should show button');
    }
    this.lastScrolledHeight = currentScrollHeight;
  }

HTML

  <button class="btn btn-success btn-circle btn-xl"
          [ngClass]="(showAddButton === true) ? 'scale-in' : 'scale-out'"
  </button>

For the sake of completion the CSS:

.scale-out {
  -webkit-animation: scale-out .2s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
  animation: scale-out .2s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
}
.scale-in {
  -webkit-animation: scale-in .2s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
  animation: scale-in .2s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
}

Looking forward for any Input. :)

Edit: I created a Stackblitz for Testing

Stackblitz

2 Answers 2

2

you should convert scroll events to Observable. Then you can control processing by using debounceTime.

You can either add a Subject, pass the scroll info, then execute your logic

  scroll = new Subject<number>();
  ngOnInit() {
    this.scroll
      .pipe(debounceTime(200))
      .subscribe((y) => this.dealWithScroll(window.scrollY));
  }
  ngOnDestroy() {
    this.scroll.complete();
  }
  @HostListener('window:scroll') watchScroll() {
    this.scroll.next(window.scrollY);
  }
  dealWithScroll(y: number) {}

Or you could create Observable from event

  scroller: Subscription;
  ngOnInit() {    
    this.scroller = fromEvent(window, 'scroll')
      .pipe(debounceTime(200))
      .subscribe(() => this.dealWithScroll(window.scrollY));      }
  ngOnDestroy() {
    this.scroller.unsubscribe();
  }

As you see you can access window object directly. Also showAddButton === true seems excessive showAddButton should be good enough. Don't forget to unsubscribe/complete Observable.

Sign up to request clarification or add additional context in comments.

1 Comment

Wow this is a pretty advanced and nerdy approach. Totally liking the implementation with observable-streams and schedulers (debouncetime() operator).
1

I would add a small buffer

It would make the app less delicate touch sensitive, and less calculation needed.

export class AppComponent {
  public lastScrolledHeight: number = 0;
  public showAddButton: boolean = true;
  private buffer = 0

  @HostListener('window:scroll', ['$event']) onScroll(event) {
    const window = event.path[1];

    if (this.ignoredByBuffer()) {
      return;
    }

    const currentScrollHeight = window.scrollY;

    if (currentScrollHeight > this.lastScrolledHeight) {
      this.showAddButton = false;
      console.log('should NOT show button');
    } else {
      this.showAddButton = true;
      console.log('should show button');
    }
    this.lastScrolledHeight = currentScrollHeight;
  }

  private ignoredByBuffer(): boolean {
    if (this.buffer < 10) {
      this.buffer += 1;
      return true;
    }
    this.buffer = 0;
    return false;
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.