【问题标题】:Smooth scroll angular2平滑滚动角2
【发布时间】:2016-07-05 16:04:49
【问题描述】:

我无法让平滑滚动服务在 angular 2 中工作。是否有任何用于平滑滚动或普通锚滚动的服务,在 angular 2 团队获得 $anchorScroll angular2 等效工作之前可能有效?

到目前为止,我刚刚尝试过:

在父 div 上设置 *ngFor 循环增量 id

[attr.id]="'point' + i"

在传递了 id 的按钮上调用 scrollto

<button 
     type="button" 
     class="btn btn-lg btn-default " 
     (click)="smoothScroll('point'+i)">
           Scroll to point
</button>

并且在相关组件中我正在尝试实现一个普通的 js 平滑滚动功能

smoothScroll(eID) {
        var startY = currentYPosition();
        var stopY = elmYPosition(eID);
        var distance = stopY > startY ? stopY - startY : startY - stopY;
        if (distance < 100) {
            scrollTo(0, stopY); return;
        }
        var speed = Math.round(distance / 100);
        if (speed >= 20) speed = 20;
        var step = Math.round(distance / 25);
        var leapY = stopY > startY ? startY + step : startY - step;
        var timer = 0;
        if (stopY > startY) {
            for (var i = startY; i < stopY; i += step) {
                setTimeout(this.win.scrollTo(0, leapY), timer * speed);
                leapY += step; if (leapY > stopY) leapY = stopY; timer++;
            } return;
        }
        for (var i = startY; i > stopY; i -= step) {
            setTimeout(this.win.scrollTo(0,leapY), timer * speed);
            leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
        }
    }
function currentYPosition() {
    // Firefox, Chrome, Opera, Safari
    if (self.pageYOffset) return self.pageYOffset;
    // Internet Explorer 6 - standards mode
    if (document.documentElement && document.documentElement.scrollTop)
        return document.documentElement.scrollTop;
    // Internet Explorer 6, 7 and 8
    if (document.body.scrollTop) return document.body.scrollTop;
    return 0;
}
function elmYPosition(eID) {
    var elm = document.getElementById(eID);
    var y = elm.offsetTop;
    var node = elm;
    while (node.offsetParent && node.offsetParent != document.body) {
        node = node.offsetParent;
        y += node.offsetTop;
    } return y;
}

我还尝试为 this._win.scrollTo 提供对窗口的访问权限,该窗口来自窗口提供程序服务

import {Injectable, Provider} from 'angular2/core';
import {window} from 'angular2/src/facade/browser';
import {unimplemented} from 'angular2/src/facade/exceptions';

function _window(): Window {
  return window
}

export abstract class WINDOW {
  get nativeWindow(): Window {
    return unimplemented();
  }
}

class WindowRef_ extends WINDOW {
  constructor() {
    super();
  }
  get nativeWindow(): Window {
    return _window();
  }
}

export const WINDOW_PROVIDERS = [
  new Provider(WINDOW, { useClass: WindowRef_ }),
];

** 编辑 ---------------------**

我将 this.win.scrollTo 更改为 this.win.window.scrollTo ,现在我得到了类似于 angular1.x $anchorscroll 的效果,其中滚动是快速而不是平滑过渡,但滚动是不顺利,我收到以下异常错误。

更新

在发现 angular2 执行 setTimeout 有点不同后,我不再收到该错误,但滚动仍然是瞬时的,而不是平滑滚动。

我变了

  setTimeout(this.win.scrollTo(0, leapY), timer * speed);

 setTimeout(() => this.win.scrollTo(0, leapY), timer * speed);

【问题讨论】:

  • 你试过什么?什么没有按预期工作?您能否添加一些代码来演示您尝试完成的工作?
  • 我刚刚编辑了问题

标签: scroll typescript angular smooth-scrolling


【解决方案1】:

window 对象中有一个名为scrollTo() 的方法。 如果您将行为设置为“平滑”,则页面将处理平滑滚动。 示例(滚动到页面顶部):

 window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });

还有备用示例:

    try 
    { 
     window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
     } catch (e) {
      window.scrollTo(0, 0);
      }

【讨论】:

  • 浏览器兼容性如何?
  • Vivaldi 和 IE 在我的测试期间无法正常工作。所以我使用了后备。尝试 { window.scrollTo({ left: 0, top: 0, behavior: 'smooth' }); } 捕捉 (e) { window.scrollTo(0, 0); }
【解决方案2】:

好吧,在摸索了一下头之后,这是一个似乎可以正常工作的解决方案。

和以前一样,我声明了我的条件 id 和一个按钮,点击时调用了 scrollTo 函数。

现在,解决方案中只有两个文件是一个服务,它将帮助返回文档窗口和模板的组件。与上述状态相比,窗口服务没有任何变化,但为了一个好的答案,我将再次包含它。

window.service.ts : 向 https://gist.github.com/lokanx/cc022ee0b8999cd3b7f5 大声疾呼以帮助完成这篇文章

import {Injectable, Provider} from 'angular2/core';
import {window} from 'angular2/src/facade/browser';
import {unimplemented} from 'angular2/src/facade/exceptions';

function _window(): Window {
  return window
}

export abstract class WINDOW {
  get nativeWindow(): Window {
    return unimplemented();
  }
}

class WindowRef_ extends WINDOW {
  constructor() {
    super();
  }
  get nativeWindow(): Window {
    return _window();
  }
}

export const WINDOW_PROVIDERS = [
  new Provider(WINDOW, { useClass: WindowRef_ }),
];

app.component.ts

import { bootstrap } from 'angular2/platform/browser';
import { Component } from 'angular2/core';
import {WINDOW, WINDOW_PROVIDERS} from './window.service';

@Component({
  selector: 'my-app',
  templateUrl: 'app.tpl.html',
  providers: [WINDOW_PROVIDERS]
})

class AppComponent {
    win: Window;
    private offSet: number;
    constructor(
        private _win: WINDOW) { 
        this.win = _win.nativeWindow;
    }
    title = 'Ultra Racing';
    things = new Array(200);

    scrollTo(yPoint: number, duration: number) {
        setTimeout(() => {
            this.win.window.scrollTo(0, yPoint)
        }, duration);
        return;
    }
    smoothScroll(eID) {
        var startY = currentYPosition();
        var stopY = elmYPosition(eID);
        var distance = stopY > startY ? stopY - startY : startY - stopY;
        if (distance < 100) {
            this.win.window.scrollTo(0, stopY); return;
        }
        var speed = Math.round(distance / 100);
        if (speed >= 20) speed = 20;
        var step = Math.round(distance / 100);
        var leapY = stopY > startY ? startY + step : startY - step;
        var timer = 0;
        if (stopY > startY) {
            for (var i = startY; i < stopY; i += step) {
                this.scrollTo(leapY, timer * speed);
                leapY += step; if (leapY > stopY) leapY = stopY; timer++;
            } return;
        }
        for (var i = startY; i > stopY; i -= step) {
            this.scrollTo(leapY, timer * speed);
            leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
        }
    }
}
function currentYPosition() {
    // Firefox, Chrome, Opera, Safari
    if (self.pageYOffset) return self.pageYOffset;
    // Internet Explorer 6 - standards mode
    if (document.documentElement && document.documentElement.scrollTop)
        return document.documentElement.scrollTop;
    // Internet Explorer 6, 7 and 8
    if (document.body.scrollTop) return document.body.scrollTop;
    return 0;
}
function elmYPosition(eID) {
    var elm = document.getElementById(eID);
    var y = elm.offsetTop;
    var node = elm;
    while (node.offsetParent && node.offsetParent != document.body) {
        node = node.offsetParent;
        y += node.offsetTop;
    } return y;
}

bootstrap(AppComponent)

我创建了一个 plunk 来展示这个例子的工作: Plunk Example

【讨论】:

    【解决方案3】:

    更简单的方法是使用这个 polyfill: http://iamdustan.com/smoothscroll/

    1. 安装为:npm install smoothscroll-polyfill
    2. 在您的 polyfill.ts 文件中将其导入为:require('smoothscroll-polyfill').polyfill();
    3. 现在你可以使用 scrollIntoView 的行为选项了:

      (document.querySelector('#'+ 锚点)).scrollIntoView({ behavior: 'smooth' });

    【讨论】:

    • 我们应该使用 require 还是应该使用 import 'smoothscroll-polyfill';
    • 您应该使用require,我尝试使用import,但没有成功。另外,不要忘记调用 .polyfill(): require('smoothscroll-polyfill').polyfill();在你的 polyfill.ts 文件中
    • 在我的情况下 require 抛出错误,当我尝试编译时它不识别 require
    • 对于打字稿,只需:import 'smoothscroll-polyfill';
    • 我通过将其添加到 polyfills.ts 中使其工作:import smoothscroll from 'smoothscroll-polyfill/dist/smoothscroll'; smoothscroll.polyfill();
    【解决方案4】:

    示例:

    function goToElement(elemId){
     let element = window.getElementById(elemId);
     element.scrollIntoView({behavior: "smooth"});
    }
    

    【讨论】:

    • 这在 Angular 6+ 中对我有用。稍作修改:ngOnInit() { this.route.fragment.subscribe(fragment =&gt; { if (fragment) { document.querySelector('#' + fragment).scrollIntoView({ behavior: 'smooth' }) } }) }
    【解决方案5】:

    如果您想要在路由之后和路由视图内工作的非常简单的锚点跳转,您也可以使用ng2-simple-page-scroll

    <a simplePageScroll href="#myanchor">Go there</a>
    

    或者在路由之后:

    <a simplePageScroll [routerLink]="['Home']" href="#myanchor">Go there</a>
    

    它做了一个简单的即时跳跃,但它确实有效。

    【讨论】:

    • 你有没有机会在 ng2-simple-page-scroll 网站上添加一个演示?
    • @MichaelJDI,是的,我会努力的。希望明天在某个地方我会完成它。同时,结帐github.com/bbottema/simple-java-mail/tree/master/src/main/…。它具有一个用于 java 库的工作信息站点,但它是 Angular2 rc1,包括 ng2-simple-page-scroll。
    • @BennyBottema 这适用于不推荐使用的路由器还是仅适用于新路由器?我必须使用特定的 LocationStrategy 吗?
    • @Konst 它只适用于新路由器,否则你将无法编译它,我想。我没有想到具体的哈希策略,但我一直在使用 HashLocationStrategy。
    • @BennyBottema 我们可以使用没有&lt;a&gt; 标签的simplePageScroll 吗?我们可以通过编程方式触发滚动吗?
    【解决方案6】:

    对于仍在寻找平滑滚动的任何人,@alex-j 的答案在 Angular 2.0 中对我来说非常有用 - 但我不得不将 Window 服务更改为此:-

    import { Injectable } from '@angular/core';
    
    function _window() : any {
        // return the global native browser window object
        return window;
    }
    
    @Injectable()
    export class WindowRef {
        get nativeWindow() : any {
            return _window();
        }
    }
    

    此博客的所有道具http://juristr.com/blog/2016/09/ng2-get-window-ref/ - 现在我有一个流畅的滚动服务,我可以从任何地方调用:)

    【讨论】:

    • 您解决了我与那段代码的其他无关问题的问题,谢谢! :D
    【解决方案7】:

    我使用这个代码。

            var dis = distance  ;
            var interval = setInterval(() => {
                this.document.body.scrollTop = dis;
                 dis=dis-5 ;
                 if (dis<10){
                     clearInterval(interval);
                 }
            }, 5);
    

    【讨论】:

      【解决方案8】:

      感谢接受的答案,我能够实现平滑的“滚动到顶部”。滚动到顶部实际上比滚动到特定目标元素更容易,因为我们总是滚动到 0 位置。代码如下:

      scrollTo(yPoint: number, duration: number) {
          setTimeout(() => {
              window.scrollTo(0, yPoint)
          }, duration);
          return;
      }
      
      smoothScrollToTop() {
          let startY = this.currentYPosition();
          let stopY = 0; // window top
          let distance = stopY > startY ? stopY - startY : startY - stopY;
          if (distance < 100) {
              window.scrollTo(0, stopY);
              return;
          }
          let speed = Math.round(distance / 100);
          let step = speed;
          speed = Math.max(9, speed); //min 9 otherwise it won't look smooth
          let leapY = stopY > startY ? startY + step : startY - step;
          let timer = 0;
          if (stopY > startY) {
              for (let i = startY; i < stopY; i += step) {
                  // since setTimeout is asynchronous, the for-loop will will fire all scrolls
                  // nearly simoultaniously. Therefore, we need to multiply the speed with
                  // a counter which lets the scrolls start with a growing offset which lets the
                  // setTimeout wait for a growing time till it scrolls there
                  // that way, we prevent the window to scroll instantly to the target Yposition
                  this.scrollTo(leapY, timer * speed);
                  leapY += step; if (leapY > stopY) leapY = stopY; timer++;
              }
              return;
          } else {
              for (let i = startY; i > stopY; i -= step) {
                  this.scrollTo(leapY, timer * speed);
                  leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
              }
          }
      }
      
      currentYPosition() {
          // Firefox, Chrome, Opera, Safari
          if (self.pageYOffset) return self.pageYOffset;
          // Internet Explorer 6 - standards mode
          if (document.documentElement && document.documentElement.scrollTop)
              return document.documentElement.scrollTop;
          // Internet Explorer 6, 7 and 8
          if (document.body.scrollTop) return document.body.scrollTop;
          return 0;
      }
      

      如果您愿意,您可以让“滚动到顶部”按钮在用户滚动时动态显示:

      @HostListener('window:scroll', ['$event'])
      onWindowScroll(event) {
          this.onScrollFadeInOutScrollToTopButton();
      }
      
      shouldShowScrollToTop: boolean = false;
      
      onScrollFadeInOutScrollToTopButton() {
          this.shouldShowScrollToTop = (window.pageYOffset >= window.screen.height/2);
      }
      

      滚动到顶部按钮的 HTML:

      <div class="back-to-top">
      <button *ngIf="shouldShowScrollToTop" [@fadeInOutTrigger]="animateButtonEntryState" class="mat-primary" md-fab (click)="smoothScrollToTop()">^</button>
      

      如您所见,该按钮还有一个动画触发器。您可以考虑为按钮使用图标,理想情况下,您的按钮应具有position:fixed; 样式。

      【讨论】:

        【解决方案9】:

        还有另一种方法,值得考虑:使用 jQuery。

        也许它没有原生解决方案那么优雅,但非常简单且完美运行。

        在您的 index.html 中,您必须将其添加到正文的末尾:

        <script
        src="https://code.jquery.com/jquery-3.1.1.min.js"
        integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
        crossorigin="anonymous"></script>
        
        <script>
          $(document).on("click", "a[href*='#']:not([href='#'])", function() {
            if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
              var target = $(this.hash);
              target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
              if (target.length) {
                  $('html, body').animate({
                  scrollTop: target.offset().top - 100
                  }, 1000);
                  return false;
              }
            }
          });
        
        </script>
        

        现在您可以像这样使用简单的&lt;a href("#section")&gt; 导航:

        <a href="#section2">Link</a>
        

        它也适用于路由:

        <a class="btn" role="button" routerLink="/contact" fragment="contact_form">Contact us!</a>
        

        【讨论】:

        • 我不可能将整个 jQuery 库添加到项目中,只是为了实现 scrollTo 功能。强烈反对 ;) 角度世界中不需要 jQuery ;)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-23
        • 2012-05-20
        • 2017-01-16
        相关资源
        最近更新 更多