
本文详解滚动触发动画时最后一帧图像缺失的根本原因,并提供精准的代码修正方案,包括预加载循环边界、帧索引计算逻辑及防抖优化建议。
本文详解滚动触发动画时最后一帧图像缺失的根本原因,并提供精准的代码修正方案,包括预加载循环边界、帧索引计算逻辑及防抖优化建议。
在基于滚动位置切换 JPEG 图像序列的 Canvas 动画中,最后一帧(如第 750 帧)无法显示,是一个典型的边界索引越界与逻辑截断问题。根本原因在于两处关键代码使用了排他性比较(< 和 Math.min(frameCount - 1, ...)),导致最大合法帧号被强制限制为 frameCount - 1,从而永远跳过了最后一张图像。
? 问题定位:两处致命边界错误
-
预加载循环遗漏最后一帧
原始代码:for (let i = 1; i < frameCount; i++) { /* ... */ }当 frameCount = 750 时,i 最大只到 749,第 750 张图从未被创建和加载。应改为包含等号:
for (let i = 1; i <= frameCount; i++) { /* 正确:覆盖 1~750 全部帧 */ } -
滚动帧索引被人为截断
原始计算:const frameIndex = Math.min(frameCount - 1, Math.ceil(scrollFraction * frameCount));
即使 scrollFraction = 1(滚动到底部),Math.ceil(1 * 750) = 750,但 Math.min(749, 750) 仍返回 749 → 永远无法命中第 750 帧。
✅ 正确做法是移除 Math.min 截断,并确保索引在有效范围内安全访问:const frameIndex = Math.max(1, Math.min(frameCount, Math.ceil(scrollFraction * frameCount)));
✅ 修复后的核心代码(精简可直接替换)
const frameCount = 750;
const imageBasePath = 'images/photo_animation4/';
// ✅ 修正1:预加载全部 1~750 帧
const preloadImages = () => {
const images = [];
for (let i = 1; i <= frameCount; i++) { // ← 关键:i <= frameCount
const img = new Image();
img.src = `${imageBasePath}${i.toString().padStart(4, '0')}.jpg`;
images.push(img);
}
return images;
};
let images = preloadImages();
// ✅ 修正2:滚动帧计算(安全取值 + 防 NaN)
window.addEventListener('scroll', () => {
const scrollTop = html.scrollTop;
const maxScrollTop = html.scrollHeight - window.innerHeight;
const scrollFraction = maxScrollTop > 0 ? scrollTop / maxScrollTop : 0;
// 安全计算:保证结果在 [1, frameCount] 区间内
const frameIndex = Math.max(1, Math.min(frameCount, Math.ceil(scrollFraction * frameCount)));
if (frameIndex !== currentFrameIndex) {
currentFrameIndex = frameIndex;
updateImage(currentFrameIndex);
}
});⚠️ 补充注意事项
- 图像加载完成校验:当前代码仅监听第一张图的 onload,但未确保全部 750 张图加载完毕。生产环境建议使用 Promise.all(images.map(img => new Promise(r => img.onload = r))) 进行全量加载确认。
-
性能优化:高频 scroll 事件易引发卡顿,建议添加 requestAnimationFrame 节流:
let isScrolling = false; window.addEventListener('scroll', () => { if (!isScrolling) { requestAnimationFrame(() => { // 执行帧更新逻辑 isScrolling = false; }); isScrolling = true; } }); - Canvas 尺寸重置时机:resize 时调用 updateCanvasSize() 后,若图像尚未加载完成,updateImage() 可能失败。建议在 imageLoaded 为 true 时才执行绘制。
通过以上修正,滚动至页面底部时 scrollFraction = 1 → frameIndex = 750 → 正确渲染最后一张图像,动画完整性得以保障。记住:帧序列的索引逻辑必须与文件命名、预加载范围、运行时计算三者严格对齐。


















