1

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
  • Welcome to StackOverflow! your question is a great first question ... in the future if you want to make it better you can refactor your code to something that will be easier to reproduce by others, for example in your question you have images and a lot that will be very hard for someone to reproduce since those images are somewhere not accessible to others, instead of images you can draw text and use smaller values to show the problem Commented Jan 19, 2024 at 18:44

1 Answer 1

0

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">

Sign up to request clarification or add additional context in comments.

1 Comment

@EnoLewandowski Don't forget to accept the answer as correct if it solves your problem and upvote if it was useful

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.