0

I'm trying to duplicate scrolling effect like airpods, it was working but it has some effects when scrolling as attachment.

I loaded 94 images to make it scrolling 360deg and preload 148 of images. I know that the scrolling effects can be removed if I use ScrollTrigger JS plug in but I want to build this by using vanilla JS only.

How can I remove the effects (When scrolling) on the image?

const html = document.documentElement;
const canvas = document.getElementById("hero-lightpass");
const context = canvas.getContext("2d");

const frameCount = 148;
//Getting file path
const currentFrame = index => (`https://connectplus.s3.ap-southeast-2.amazonaws.com/connect-plus/${index.toString().padStart(4, '0')}.png`)

//To preload the images of 148 as initialized above, then we loop through each image from the file path and create a new image
const preloadImages = () => {
    for(let i = 1; i < frameCount; i++){
        const img = new Image();
        img.src = currentFrame(i);
    }
};

const img = new Image()
img.src = currentFrame(1);
canvas.width = 1158;
canvas.height = 770;
img.onload = function(){
    context.drawImage(img, 0, 0);
}

const updateImage = index => {
    img.src = currentFrame(index);
    context.drawImage(img, 0, 0);
}

window.addEventListener('scroll', () => {
    context.fillRect(0, 0, canvas.width, canvas.height);
    const scrollTop = html.scrollTop;
    const maxScrollTop = html.scrollHeight - window.innerHeight;
    const scrollFraction = scrollTop / maxScrollTop;
    const frameIndex = Math.min(
        frameCount - 1,
        Math.ceil(scrollFraction * frameCount)
    );

    requestAnimationFrame(() => updateImage(frameIndex + 1))
});

preloadImages()
html {
  height: 100vh;
}

body {
    margin: 0;
    padding: 0;
    width: auto;
    height: auto;
}

.test {
    height: 500vh;
    background-color: #ffffff;
}
<html>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<body>
    <!--Scrolling effect without a library-->
    <div id="mainDiv" class="test bg-white">
        <canvas id="hero-lightpass" class=" sticky justify-center items-center top-1/4 left-1/4 transform -translate-x-1/4 -translate-y-1/4 max-w-screen-2xl max-h-screen overflow-hidden"></canvas>
    </div>
</body>
</html>

enter image description here

1 Answers1

1

Try using a closure in your preloader (and don't use const in the loop)... plus a little loaded validation to prevent canvas from crashing on images that haven't loaded:

const html = document.documentElement;
const canvas = document.getElementById("hero-lightpass");
const context = canvas.getContext("2d");

const frameCount = 148;
//Getting file path
const currentFrame = index => (`https://connectplus.s3.ap-southeast-2.amazonaws.com/connect-plus/${index.toString().padStart(4, '0')}.png`)

//To preload the images of 148 as initialized above, then we loop through each image from the file path and create a new image
var imgCache = {};
const preloadImages = () => {
    for(let i = 1; i < frameCount; i++){
        let img = new Image();
        img.src = currentFrame(i);
        var obj = {
            img : img,
            loaded : false
        }
        img.onload = (function(Vobj){
            return function(){
                Vobj.loaded = true;
            }
        }(obj))
        imgCache[i] = obj;
    }
};


const img = new Image()
img.src = currentFrame(1);
canvas.width = 1158;
canvas.height = 770;
img.onload = () => {
    context.drawImage(img, 0, 0);
}

const updateImage = index => {
    var item = imgCache[index];
    if(item && item.loaded){
        context.drawImage(item.img, 0, 0);
    }
}

window.addEventListener('scroll', () => {
    context.fillRect(0, 0, canvas.width, canvas.height);
    const scrollTop = html.scrollTop;
    const maxScrollTop = html.scrollHeight - window.innerHeight;
    const scrollFraction = scrollTop / maxScrollTop;
    const frameIndex = Math.min(
        frameCount - 1,
        Math.ceil(scrollFraction * frameCount)
    );

    requestAnimationFrame(() => updateImage(frameIndex + 1))
});

preloadImages()
html {
  height: 100vh;
}

body {
    margin: 0;
    padding: 0;
    width: auto;
    height: auto;
}

.test {
    height: 500vh;
    background-color: #ffffff;
}
<html>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<body>
    <!--Scrolling effect without a library-->
    <div id="mainDiv" class="test bg-white">
        <canvas id="hero-lightpass" class=" sticky justify-center items-center top-1/4 left-1/4 transform -translate-x-1/4 -translate-y-1/4 max-w-screen-2xl max-h-screen overflow-hidden"></canvas>
    </div>
</body>
</html>
bob
  • 7,539
  • 2
  • 46
  • 42
  • Is that mean, I need to create an empty object to store all unused images? Thank you for the closure. I missed it! – tengkuzulfadli Oct 26 '21 at 23:11
  • ya, you should cache all the images to "something", I like to use objects, since you can name them (property names can be URLs). Of course you can use an array as well. – bob Oct 27 '21 at 09:14