I want to preload create image sequencing using next js and framer motion
I have the code to run the image sequencing it works fine the issue lies in preloading the image and then serving the same
I saw in network tab that the image are preloaded properly but when I scroll the image they are loading individually one after the another and when i scroll reverse it loads again
the typescript code for this is below
'use client';
import { useEffect, useState } from 'react';
import Image from 'next/image';
import styles from './ImageSequencing.module.scss';
const imageSequence = Array.from(
{ length: 65 },
(_, i) =>
`/home/imageSequencingResult/website rough animation${String(
i + 1,
).padStart(4, '0')}_result.webp`,
);
export default function Home() {
const [scrollY, setScrollY] = useState(0);
const [loaded, setLoaded] = useState(false);
const [loadingPercentage, setLoadingPercentage] = useState(0);
const [currentImage, setCurrentImage] = useState(imageSequence[0]);
const [loadedImage, setLoadedImage] = useState<HTMLImageElement[]>([]);
const totalHeight = imageSequence.length * 10 + 15; // Assuming each image is 100vh
const loadImage = (src: string): Promise<string> =>
new Promise((resolve, reject) => {
const img = new window.Image();
img.src = src;
img.onload = () => resolve(src);
img.onerror = (err) => reject(err);
img.onprogress = (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
setLoadingPercentage(Math.round(percent));
}
};
});
const loadImages = async () => {
try {
await Promise.all(imageSequence.map((src) => loadImage(src)));
setLoaded(true);
} catch (error) {
console.error('Error loading images:', error);
}
};
useEffect(() => {
loadImages();
}, []);
useEffect(() => {
const handleScroll = () => {
const index = Math.min(
imageSequence.length - 1,
Math.floor(
(window.scrollY / window.innerHeight) * imageSequence.length,
),
);
console.log(index, 'index');
setCurrentImage(imageSequence[index]);
setScrollY(window.scrollY);
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
console.log(loadedImage, 'loadedImage');
return (
<div className={styles.wrapper} style={{ height: `${totalHeight}vh` }}>
<div className={styles.container}>
{!loaded && <div className={styles.loader}>Loading...</div>}
{loaded && (
<div className={styles.imageWrapper}>
<Image
src={currentImage}
alt={`Image ${imageSequence.indexOf(currentImage)}`}
width={1920} // Adjust width and height as per your image dimensions
height={1080}
layout="responsive"
/>
</div>
)}
</div>
</div>
);
}
just for reference this is the scss code
.wrapper {
height: 100vh; /* Adjust as needed */
// overflow: hidden; /* Ensure no scrollbars appear */
position: relative;
.container {
position: sticky;
top: 0;
left: 0;
height: 100vh; /* Adjust as needed */
width: 100%;
display: flex;
justify-content: center;
align-items: center;
.imageWrapper {
position: relative;
width: 100%;
height: 100vh; /* Adjust as needed */
> div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
}
.loader {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 24px;
color: #000;
}
}
I also tried to load all the images using this but same result
<div
style={{
position: 'absolute',
zIndex: -1,
visibility: 'hidden',
height: 0,
width: 0,
}}
>
{imageSequence.map((item, index) => {
return (
<Image
src={item}
alt={`Image ${index}`}
width={0} // Adjust width and height as per your image dimensions
height={0}
// layout="responsive"
onLoadStart={() => setLoaded(false)}
onLoadingComplete={() => setLoaded(true)}
/>
);
})}
</div>
also this
const loadImage = (src: string): Promise<string> =>
new Promise((resolve, reject) => {
const img = new Image();
img.src = src;
img.onload = () => resolve(src);
img.onerror = (err) => reject(err);
img.onprogress = (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
setLoadingPercentage(Math.round(percent));
}
};
});