平滑滚动 angular2
Smooth scroll angular2
我无法在 angular 2 中使用平滑滚动服务。在 angular 2 团队获得之前,是否有任何平滑滚动或普通锚滚动的服务可能会起作用$anchorScroll angular2 等效工作?
到目前为止我刚刚尝试过:
在父级上设置 *ngFor 循环递增 id div
[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;
}
我也在尝试为来自 window 提供商服务
的 this._win.scrollTo 授予对 window 的访问权
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);
好吧,在摸索了一下之后,这里有一个似乎工作正常的解决方案。
和以前一样,我声明了我的条件 ID 和一个按钮,单击时会调用 scrollTo 函数。
现在,解决方案中只有两个文件,一个服务将帮助 return 文档 window 和模板的组件。 window 服务中的上述状态没有任何变化,但为了一个好的答案,我将再次包含它。
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
如果你想要一个非常简单的锚点跳转,在路由之后和路由视图中工作,你也可以使用 ng2-simple-page-scroll。
<a simplePageScroll href="#myanchor">Go there</a>
或路由后立即:
<a simplePageScroll [routerLink]="['Home']" href="#myanchor">Go there</a>
它有一个简单的即时跳跃,但很管用。
对于仍在寻找平滑滚动的任何人@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/ - 现在我有一个流畅的滚动服务,我可以从任何地方调用:)
应该考虑另一种方法:使用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>
现在您可以像这样使用简单的 <a href("#section")>
导航:
<a href="#section2">Link</a>
它也适用于路由:
<a class="btn" role="button" routerLink="/contact" fragment="contact_form">Contact us!</a>
我使用这个代码。
var dis = distance ;
var interval = setInterval(() => {
this.document.body.scrollTop = dis;
dis=dis-5 ;
if (dis<10){
clearInterval(interval);
}
}, 5);
更简单的方法是使用这个 polyfill:
http://iamdustan.com/smoothscroll/
- 安装为:npm install smoothscroll-polyfill
- 将其导入到您的 polyfill.ts 文件中:require('smoothscroll-polyfill').polyfill();
现在您可以使用 scrollIntoView 的行为选项:
(document.querySelector('#'+ anchor)).scrollIntoView({ 行为: 'smooth' });
window
对象中有一个名为 scrollTo()
的方法。
如果将行为设置为 'smooth',页面将处理平滑滚动。
示例(滚动到页面顶部):
window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
以及后备示例:
try
{
window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
} catch (e) {
window.scrollTo(0, 0);
}
多亏了接受的答案,我才能够顺利实施 "scroll to top"。滚动到顶部实际上比滚动到特定目标元素更容易,因为我们总是滚动到 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;
}
如果需要,您可以让 "Scroll-To-Top" 按钮在用户滚动时动态显示:
@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;
样式。
示例:
function goToElement(elemId){
let element = window.getElementById(elemId);
element.scrollIntoView({behavior: "smooth"});
}
我无法在 angular 2 中使用平滑滚动服务。在 angular 2 团队获得之前,是否有任何平滑滚动或普通锚滚动的服务可能会起作用$anchorScroll angular2 等效工作?
到目前为止我刚刚尝试过:
在父级上设置 *ngFor 循环递增 id div
[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;
}
我也在尝试为来自 window 提供商服务
的 this._win.scrollTo 授予对 window 的访问权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);
好吧,在摸索了一下之后,这里有一个似乎工作正常的解决方案。
和以前一样,我声明了我的条件 ID 和一个按钮,单击时会调用 scrollTo 函数。
现在,解决方案中只有两个文件,一个服务将帮助 return 文档 window 和模板的组件。 window 服务中的上述状态没有任何变化,但为了一个好的答案,我将再次包含它。
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
如果你想要一个非常简单的锚点跳转,在路由之后和路由视图中工作,你也可以使用 ng2-simple-page-scroll。
<a simplePageScroll href="#myanchor">Go there</a>
或路由后立即:
<a simplePageScroll [routerLink]="['Home']" href="#myanchor">Go there</a>
它有一个简单的即时跳跃,但很管用。
对于仍在寻找平滑滚动的任何人@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/ - 现在我有一个流畅的滚动服务,我可以从任何地方调用:)
应该考虑另一种方法:使用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>
现在您可以像这样使用简单的 <a href("#section")>
导航:
<a href="#section2">Link</a>
它也适用于路由:
<a class="btn" role="button" routerLink="/contact" fragment="contact_form">Contact us!</a>
我使用这个代码。
var dis = distance ;
var interval = setInterval(() => {
this.document.body.scrollTop = dis;
dis=dis-5 ;
if (dis<10){
clearInterval(interval);
}
}, 5);
更简单的方法是使用这个 polyfill: http://iamdustan.com/smoothscroll/
- 安装为:npm install smoothscroll-polyfill
- 将其导入到您的 polyfill.ts 文件中:require('smoothscroll-polyfill').polyfill();
现在您可以使用 scrollIntoView 的行为选项:
(document.querySelector('#'+ anchor)).scrollIntoView({ 行为: 'smooth' });
window
对象中有一个名为 scrollTo()
的方法。
如果将行为设置为 'smooth',页面将处理平滑滚动。
示例(滚动到页面顶部):
window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
以及后备示例:
try
{
window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
} catch (e) {
window.scrollTo(0, 0);
}
多亏了接受的答案,我才能够顺利实施 "scroll to top"。滚动到顶部实际上比滚动到特定目标元素更容易,因为我们总是滚动到 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;
}
如果需要,您可以让 "Scroll-To-Top" 按钮在用户滚动时动态显示:
@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;
样式。
示例:
function goToElement(elemId){
let element = window.getElementById(elemId);
element.scrollIntoView({behavior: "smooth"});
}