Lets say we had this (working) script, but as it uses querySelector only works for the first instance:
if ( $( '.marquee' ).length ) {
const marquee = document.querySelector( '.marquee__content' );
let lerpScroll = 0;
let lastScrollPos = 0;
const maxSpeed = 20;
function getScrollPercent() {
let h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
return (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
}
function isInViewport() {
const rect = marquee.getBoundingClientRect();
const vOffset = marquee.offsetHeight;
const percent = getScrollPercent() * -1;
lerpScroll += (percent - lerpScroll) * 0.06;
marquee.style.transform = `translateX(${lerpScroll * 4}%)`;
// skew
let scrollPos = window.scrollY;
const container = marquee.querySelector( '.marquee__inner' );
getDirection = scrollPos - lastScrollPos;
lastScrollPos = scrollPos;
if (getDirection > maxSpeed) {
getDirection = maxSpeed;
}
if (getDirection < -maxSpeed) {
getDirection = -maxSpeed;
}
container.style.transform = getDirection;
window.requestAnimationFrame(isInViewport);
}
isInViewport();
}
So I tried converting it to a class (and dump the jquery code):
class Marquee {
constructor(el) {
this.marquee = el;
this.maxSpeed = 20;
this.lerpScroll = 0;
this.lastScrollPos = 0;
}
getScrollPercent() {
let h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
return (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
}
isInViewport() {
if (this.marquee) { /* <----- this is line 24 */
console.log(this.marquee.offsetHeight);
const vOffset = this.marquee.offsetHeight;
const percent = this.getScrollPercent() * -1;
this.lerpScroll += (percent - this.lerpScroll) * 0.06;
this.marquee.style.transform = `translateX(${this.lerpScroll * 4}%)`;
// skew
let scrollPos = window.scrollY;
const container = this.marquee.querySelector( '.marquee__inner' );
let getDirection = scrollPos - this.lastScrollPos;
this.lastScrollPos = scrollPos;
if (getDirection > this.maxSpeed) {
getDirection = this.maxSpeed;
}
if (getDirection < -this.maxSpeed) {
getDirection = -this.maxSpeed;
}
container.style.transform = getDirection;
window.requestAnimationFrame(this.isInViewport);
}
}
init () {
this.isInViewport();
}
};
And using it like this:
if ( document.querySelectorAll( '.marquee' ).length ) {
document.querySelectorAll( '.marquee__content' ).forEach((element) => {
const m = new Marquee(element);
m.init();
})
}
Even that it printed the value
Any idea what i'm doing wrong? here's a fiddle https://jsfiddle.net/bkj3ry74/2/
So the problem is that it complains about it can't read a prop that i can see the console.log
scripts.js?ver=1.0.0:24 Uncaught TypeError: Cannot read properties of undefined (reading 'marquee')
at isInViewport
on this line
window.requestAnimationFrame(this.isInViewport);
you are passing the function from the class as a callback function for .requestAnimationFrame
which is fine and dandy, however because you are passing it as a function, in this context it is getting a new this
inside the function. Maybe using an anonymous arrow function instead will keep the context of this
i.e.
window.requestAnimationFrame(() => this.isInViewport());