Smooth scroll angular2
Solution 1
Alright, after scratching my head a little bit, here is a solution that seems to be working ok.
Same as before, I declared my conditional id and a button with the scrollTo function call when clicked.
Now, there are only two files in the solution is a service that will help return the document window and the template's component. Nothing was changed in the window service from the state above but I will include it again for the sake of a good answer.
window.service.ts : shout out to https://gist.github.com/lokanx/cc022ee0b8999cd3b7f5 for helping with this piece
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)
I created a plunk to show this example working: Plunk Example
Solution 2
there is a method in the window
object called scrollTo()
.
If you set the behavior to 'smooth' the page will handle the smooth scroll.
example (scroll to top of page):
window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
And with fallback example:
try
{
window.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
} catch (e) {
window.scrollTo(0, 0);
}
Solution 3
The easier way to achieve this is by using this polyfill: http://iamdustan.com/smoothscroll/
- Install it as: npm install smoothscroll-polyfill
- Import it in your polyfill.ts file as: require('smoothscroll-polyfill').polyfill();
-
Now you can use behavior option of scrollIntoView as:
(document.querySelector('#'+ anchor)).scrollIntoView({ behavior: 'smooth' });
Solution 4
example:
function goToElement(elemId){
let element = window.getElementById(elemId);
element.scrollIntoView({behavior: "smooth"});
}
Solution 5
If you want a very simple anchor jump that works after routing and within routed views, you can also use ng2-simple-page-scroll.
<a simplePageScroll href="#myanchor">Go there</a>
Or right after routing:
<a simplePageScroll [routerLink]="['Home']" href="#myanchor">Go there</a>
It does a simple instant jump, but it works.
Alex J
Updated on August 02, 2020Comments
-
Alex J almost 4 years
I am having trouble getting a smooth scroll service to work in angular 2. Are there any services for smooth scrolling, or plain anchor scrolling, that might work until the angular 2 team gets the $anchorScroll angular2 equivalent working?
So far I have just tried:
Setting *ngFor loop incremental id on a parent div
[attr.id]="'point' + i"
Calling a scrollto on a button with the id passed
<button type="button" class="btn btn-lg btn-default " (click)="smoothScroll('point'+i)"> Scroll to point </button>
And in the associated component I am trying to implement a plain js smooth scroll function
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; }
I'm also trying to give access to the window for the this._win.scrollTo which is coming from a window provider service
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_ }), ];
** EDIT ---------------------**
I changed the this.win.scrollTo to this.win.window.scrollTo and now I am getting an effect similar to angular1.x $anchorscroll where the scroll is a snappy just instead of a smooth transition, but the scroll is not smooth and I am getting the following exception error.
UPDATE
I am no longer getting that error after finding out that angular2 is doing the setTimeout a bit differently, but the scroll is still instantaneous and not a smooth scroll.
I changed
setTimeout(this.win.scrollTo(0, leapY), timer * speed);
to
setTimeout(() => this.win.scrollTo(0, leapY), timer * speed);