You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas滚动序列帧动画图片分段延迟预加载优化求助

滚动触发Canvas序列帧动画的图片分段加载优化方案

针对884张序列帧图片加载过长的问题,以下是分段加载的优化实现,核心思路是初始加载前100张,之后每5秒加载下一批100张,同时加入图片缓存避免重复请求,优化滚动体验。

优化后的JavaScript代码

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

const frameCount = 884;
const batchSize = 100; // 每批加载图片数量
const imageCache = []; // 缓存已加载的图片,索引对应帧号(从1开始)
let currentBatch = 0; // 当前加载批次序号

// 生成指定帧的图片路径
const currentFrame = index => `compressed/${index.toString().padStart(9, 'web_0000')}.webp`;

// 加载指定范围的图片批次
const loadBatch = (start, end) => {
  for (let i = start; i <= end; i++) {
    if (i > frameCount) break;
    const img = new Image();
    img.src = currentFrame(i);
    img.onload = () => {
      imageCache[i] = img;
      // 第一帧加载完成后绘制初始画面
      if (i === 1) scaleToFill(img);
    };
  }
};

// 初始化加载第一批图片
const initPreload = () => {
  const end = Math.min(batchSize, frameCount);
  loadBatch(1, end);
  currentBatch = 1;
  // 启动后续批次的定时加载
  startBatchLoading();
};

// 定时加载后续批次图片
const startBatchLoading = () => {
  const interval = setInterval(() => {
    const start = currentBatch * batchSize + 1;
    const end = Math.min((currentBatch + 1) * batchSize, frameCount);
    if (start > frameCount) {
      clearInterval(interval);
      return;
    }
    loadBatch(start, end);
    currentBatch++;
  }, 5000); // 每5秒加载一批
};

// 将图片缩放填充至整个画布
function scaleToFill(img) {
  const scale = Math.max(canvas.width / img.width, canvas.height / img.height);
  const x = (canvas.width / 2) - (img.width / 2) * scale;
  const y = (canvas.height / 2) - (img.height / 2) * scale;
  context.drawImage(img, x, y, img.width * scale, img.height * scale);
}

// 更新画布显示的帧
const updateImage = index => {
  const targetImg = imageCache[index];
  if (targetImg) {
    scaleToFill(targetImg);
  } else {
    // 目标帧未加载时,显示最近已加载的帧避免空白
    const nearestLoaded = imageCache.slice(1).reverse().find(img => img);
    if (nearestLoaded) scaleToFill(nearestLoaded);
  }
};

// 滚动事件监听,触发帧更新
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)
  );
  
  requestAnimationFrame(() => updateImage(frameIndex + 1));
});

// 初始化画布尺寸
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

// 窗口大小变化时重新调整画布并绘制当前帧
window.addEventListener('resize', () => {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  const scrollTop = html.scrollTop;
  const maxScrollTop = html.scrollHeight - window.innerHeight;
  const scrollFraction = scrollTop / maxScrollTop;
  const frameIndex = Math.min(
    frameCount - 1,
    Math.ceil(scrollFraction * frameCount)
  );
  const currentImg = imageCache[frameIndex + 1] || imageCache[1];
  if (currentImg) scaleToFill(currentImg);
});

// 启动加载流程
initPreload();

HTML代码(无改动)

<canvas id="hero-lightpass"></canvas>

关键优化点说明

  • 分段加载逻辑:拆分图片加载任务,初始加载前100张,后续每5秒自动加载下一批,大幅降低页面初始加载压力
  • 图片缓存:用数组缓存已加载的图片,滚动时直接从缓存取图,避免重复发起网络请求
  • 降级处理:当滚动到未加载的帧时,自动显示最近已加载的帧,避免画布空白
  • 窗口适配:新增窗口 resize 监听,自动调整画布尺寸并重绘当前帧,保证不同屏幕尺寸下的显示效果

内容的提问来源于stack exchange,提问作者Aslan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:20:28