i'm trying one of those on scroll canvas image animation but the last image at the bottom on the page doesn't show up

57 Views Asked by At

I did an animation on scroll with jpeg images that cover the whole page and it works quite well for the most part but when i reach the bottom of the page, the last image doesn't show up.

Chat-gpt was a real help to make it relatively responsive but this problem resulted from the his responsive modified version.

here is the script :

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

const frameCount = 750;
const imageBasePath = 'images/photo_animation4/';

const currentFrame = index => (
    `${imageBasePath}${index.toString().padStart(4, '0')}.jpg`
);

const preloadImages = () => {
    const images = [];
    for (let i = 1; i < frameCount; i++) {
        const img = new Image();
        img.src = currentFrame(i);
        images.push(img);
    }
    return images;
};

let images = preloadImages();
let currentFrameIndex = 6;
let imageLoaded = false;

const updateCanvasSize = () => {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    if (imageLoaded) {
        updateImage(currentFrameIndex);
    }
};

window.addEventListener('resize', () => {
    updateCanvasSize();
});

const firstImage = new Image();
firstImage.src = currentFrame(1);
firstImage.onload = function () {
    imageLoaded = true;
    updateCanvasSize();
};

const updateImage = index => {
    const img = images[index - 1];
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(img, 0, 0, canvas.width, canvas.height);
};

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

    if (frameIndex + 1 !== currentFrameIndex) {
        currentFrameIndex = frameIndex + 1;
        updateImage(currentFrameIndex);
    }
});

setTimeout(() => {
    updateImage(currentFrameIndex);
}, 1000);

This is my first time asking for help on stack overflow so advices on question asking are very welcome.

1

There are 1 best solutions below

1
On

In your code you are excluding the last frame (750) I'm assuming that is your last image ...

The initialization uses the loop:
... let i = 1; i < frameCount ...
That stops are 749

Then in your frameIndex calculation you have:
... Math.min(frameCount - 1, ... that also excludes the last value


To debug that you can add some console.log statement and add some debug text to the canvas
Here is a small example showing that...
I'm simulating the scroll with the mousemove over the canvas in the X axis

const html = document.documentElement;
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.font = "50px Arial";

const frameCount = 5
for (let i = 1; i < frameCount; i++) {
  console.log("init", i)
}

function mouseMove(evt) {
  const scrollTop = evt.clientX
  const maxScrollTop = canvas.width
  const scrollFraction = scrollTop / maxScrollTop;
  const frameIndex = Math.min(frameCount-1, Math.ceil(scrollFraction * frameCount));
  context.clearRect(0, 0, canvas.width, canvas.height)
  context.fillText(frameIndex, evt.clientX, evt.clientY);
}

canvas.addEventListener('mousemove', mouseMove);
canvas { border: solid 1px }
<canvas id="canvas">


We can fix that loop with:
i <= frameCount

In the frameIndex calculation we can remove the:
Math.min(frameCount-1

See working code below:

const html = document.documentElement;
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.font = "50px Arial";

const frameCount = 5
for (let i = 1; i <= frameCount; i++) {
  console.log("init", i)
}

function mouseMove(evt) {
  const scrollTop = evt.clientX
  const maxScrollTop = canvas.width
  const scrollFraction = scrollTop / maxScrollTop;
  const frameIndex = Math.ceil(scrollFraction * frameCount);
  context.clearRect(0, 0, canvas.width, canvas.height)
  context.fillText(frameIndex, evt.clientX, evt.clientY);
}

canvas.addEventListener('mousemove', mouseMove);
canvas { border: solid 1px }
<canvas id="canvas">