I've been trying to implement some kind of smooth scroll. I followed this codepen to create such scroll: https://codepen.io/osublake/pen/QqPqbN
I tried to translate into TypeScript/Angular, but it somehow still throws the following error - not when updateScroller()
is being called inside of ngAfterViewInit
, but when I try to scroll for the first time - pointing at const resized = this.scroller.resizeRequest > 0
:
core.js:9110 ERROR TypeError: Cannot read property 'scroller' of undefined
at updateScroller (app.component.ts:50)
at ZoneDelegate.invokeTask (zone-evergreen.js:391)
at Object.onInvokeTask (core.js:34182)
at ZoneDelegate.invokeTask (zone-evergreen.js:390)
at Zone.runTask (zone-evergreen.js:168)
at invokeTask (zone-evergreen.js:465)
at ZoneTask.invoke (zone-evergreen.js:454)
at timer (zone-evergreen.js:2650)
export class AppComponent implements OnInit, AfterViewInit {
scroller = {
target: document.querySelector('.scrollable-container'),
ease: 0.05,
endY: 0,
y: 0,
resizeRequest: 1,
scrollRequest: 0,
};
requestId = null;
constructor(@Inject(PLATFORM_ID) private platformId) {
}
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
console.log('Hi, this is NØREBRO!');
}
}
ngAfterViewInit(): void {
setTimeout(() => {
this.scroller.target = document.querySelector('.scrollable-container');
TweenLite.set(this.scroller.target, {
rotation: 0.01,
force3D: true
});
this.updateScroller();
});
window.focus();
}
updateScroller() {
const html = document.documentElement;
const body = document.body;
const resized = this.scroller.resizeRequest > 0;
if (resized) {
const height = this.scroller.target.clientHeight;
body.style.height = height + 'px';
this.scroller.resizeRequest = 0;
}
const scrollY = window.pageYOffset || html.scrollTop || body.scrollTop || 0;
this.scroller.endY = scrollY;
this.scroller.y += (scrollY - this.scroller.y) * this.scroller.ease;
if (Math.abs(scrollY - this.scroller.y) < 0.05 || resized) {
this.scroller.y = scrollY;
this.scroller.scrollRequest = 0;
}
TweenLite.set(this.scroller.target, {
y: -this.scroller.y
});
this.requestId = this.scroller.scrollRequest > 0 ? requestAnimationFrame(this.updateScroller) : null;
}
@HostListener('window:scroll', [])
scroll() {
this.scroller.scrollRequest++;
console.log(this.requestId);
if (!this.requestId) {
console.log('uff');
this.requestId = requestAnimationFrame(this.updateScroller);
}
}
}
It seems that this
is undefined
by repeated method calls. Without being able to debug your application, I assume that you need to add this
as a scope to your this.updateScroller
method.
The scope (this
) of a function is always determined by the execution context and with requestAnimationFrame
the function is called outside the context of your class instance (component). There are several interesting articles about function scopes, e.g. see this.
There should be two ways to solve your problem:
a) Replace all requestAnimationFrame(this.updateScroller)
calls with
requestAnimationFrame(this.updateScroller.bind(this))
b) Replace all requestAnimationFrame(this.updateScroller)
calls with
requestAnimationFrame(() => {
this.updateScroller()
})