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);
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); }
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With