Подтвердить что ты не робот

Angular 4 - Анимация прокрутки

Я создаю веб-страницу, имеющую полную ширину страницы/высоту div. Во время прокрутки у меня есть два типа методов.

Прокрутить по ссылке

//HTML
<a (click)="goToDiv('about')"></a>

//JS
    goToDiv(id) {
        let element = document.querySelector("#"+id);
        element.scrollIntoView(element);
      }

Прокрутка по HostListener

  @HostListener("window:scroll", ['$event'])
  onWindowScroll($event: any): void {
    this.topOffSet = window.pageYOffset;
    //window.scrollTo(0, this.topOffSet+662);
  }

1. Как добавить эффекты анимации прокрутки?

Также как:

$('.scroll').on('click', function(e) {
    $('html, body').animate({
        scrollTop: $(window).height()
    }, 1200);
});

2. И как использовать HostListener для прокрутки до следующего div?

4b9b3361

Ответ 1

Это весело. Решение, как и в большинстве случаев угловых 2, является наблюдаемым.

  getTargetElementRef(currentYPos: int): ElementRef {
      // you need to figure out how this works
      // I can't comment much on it without knowing more about the page
      // but you inject the host ElementRef in the component / directive constructor and use normal vanillaJS functions to find other elements
  }
  //capture the scroll event and pass to a function that triggers your own event for clarity and so you can manually trigger
  scrollToSource: Subject<int> = new Subject<int>();
  @HostListener("window:scroll", ['$event'])
  onWindowScroll($event: any): void {
    var target = getTargetElementRef(window.pageYOffset);
    this.scrollTo(target);
  }

  scrollTo(target: ElementRef): void {
     // this assumes you're passing in an ElementRef, it may or may not be appropriate, you can pass them to functions in templates with template variable syntax such as: <div #targetDiv>Scroll Target</div> <button (click)="scrollTo(targetDiv)">Click To Scroll</button>
     this.scrollToSource.next(target.nativeElement.offsetTop);
  }

  //switch map takes the last value emitted by an observable sequence, in this case, the user latest scroll position, and transforms it into a new observable stream
  this.scrollToSource.switchMap(targetYPos => {
       return Observable.interval(100) //interval just creates an observable stream corresponding to time, this emits every 1/10th of a second. This can be fixed or make it dynamic depending on the distance to scroll
           .scan((acc, curr) =>  acc + 5, window.pageYOffset) // scan takes all values from an emitted observable stream and accumulates them, here you're taking the current position, adding a scroll step (fixed at 5, though this could also be dynamic), and then so on, its like a for loop with +=, but you emit every value to the next operator which scrolls, the second argument is the start position
           .do(position => window.scrollTo(0, position)) /// here is where you scroll with the results from scan
           .takeWhile(val => val < targetYPos); // stop when you get to the target
  }).subscribe(); //don't forget!

Одним щелчком мыши это легко использовать. Вы просто привязываете scrollTo к клику

Это работает только для прокрутки в одном направлении, однако это должно помочь вам начать. Вы можете сделать сканирование более умным, чтобы оно вычитало, если вам нужно подняться, и вместо этого использовать функцию внутри takeWhile, которая вычисляет правильное условие завершения на основе того, идет ли вверх или вниз.

редактировать: rxjs 5+ совместимая версия

  this.scrollToSource.pipe(switchMap(targetYPos => 
       interval(100).pipe( //interval just creates an observable stream corresponding to time, this emits every 1/10th of a second. This can be fixed or make it dynamic depending on the distance to scroll
           scan((acc, curr) =>  acc + 5, window.pageYOffset), // scan takes all values from an emitted observable stream and accumulates them, here you're taking the current position, adding a scroll step (fixed at 5, though this could also be dynamic), and then so on, its like a for loop with +=, but you emit every value to the next operator which scrolls, the second argument is the start position
           takeWhile(val => val < targetYPos)) // stop when you get to the target
  )).subscribe(position => window.scrollTo(0, position)); // here is where you scroll with the results from scan

Ответ 2

Вы также можете использовать свойство scroll-behavior: smooth CSS scroll-behavior: smooth

в комбинации с

var yPosition = 1000;
window.scrollTo(0,yPosition)

Ссылка: developer.mozilla.org/docs/Web/CSS/scroll-behavior

Ответ 3

Ответ @bryan60 работает, но мне это было неудобно, и я предпочел использовать TimerObservable который кажется менее запутанным для других товарищей по команде, а также его легче настроить для использования в будущем.

Я предлагаю вам использовать общий сервис для тех случаев, когда вы касаетесь DOM или работаете со свитком и другими проблемами, связанными с HTML-элементами; Тогда вы можете использовать этот метод в этой службе (в противном случае его использование в компоненте не создает никаких проблем).

  // Choose the target element (see the HTML code bellow):
  @ViewChild('myElement') myElement: ElementRef;

  this.scrollAnimateAvailable:boolean;

animateScrollTo(target: ElementRef) {
    if (this.helperService.isBrowser()) {
      this.scrollAnimateAvailable = true;
      TimerObservable
        .create(0, 20).pipe(
        takeWhile(() => this.scrollAnimateAvailable)).subscribe((e) => {
        if (window.pageYOffset >= target.nativeElement.offsetTop) {
          window.scrollTo(0, window.pageYOffset - e);
        } else if (window.pageYOffset <= target.nativeElement.offsetTop) {
          window.scrollTo(0, window.pageYOffset + e);
        }

        if (window.pageYOffset + 30 > target.nativeElement.offsetTop && window.pageYOffset - 30 < target.nativeElement.offsetTop) {
          this.scrollAnimateAvailable = false;
        }

      });
    }

  }



 scrollToMyElement(){
   this.animateScrollTo(this.myElement)
  }

Вам нужно передать элемент этому методу, вот как вы можете это сделать:

<a (click)="scrollToMyElement()"></a>
<!-- Lots of things here... -->
<div #myElement></div>