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

如何截取包含HTML内容与Three.js场景的完整页面截图?

实现方案

最稳定的实现方式是分层截取后手动合成,不需要依赖复杂第三方库,兼容性最好,具体方案如下:

方案1:手动分层合成(推荐)

核心思路是分别截取WebGL画布内容和普通HTML内容,按照元素在页面的实际位置合并到同一张画布上导出。

前置配置

初始化Three.js渲染器时必须开启preserveDrawingBuffer配置,否则WebGL缓冲区会在渲染后清空,无法读取画布内容:

const renderer = new THREE.WebGLRenderer({
  preserveDrawingBuffer: true, // 必须添加该配置
  alpha: true,
  antialias: true
  // 其余原有配置保持不变
})

实现步骤

  • 记录当前页面滚动位置,临时隐藏Three.js对应的canvas元素,避免html2canvas重复渲染画布内容
  • 调用html2canvas截取整页HTML内容,得到HTML层的canvas对象
  • 恢复canvas元素的显示,调用renderer.domElement.toDataURL()拿到Three.js场景的截图
  • 创建和整页尺寸(宽为document.documentElement.scrollWidth,高为document.documentElement.scrollHeight)一致的空白画布
  • 按照元素实际布局位置,先把Three.js场景截图绘制到空白画布底层,再把HTML层截图绘制到上层
  • 把最终合成的画布转成图片格式,调用下载方法保存

参考实现代码

// 原有保存文件方法可直接复用,修正了原方法中未定义变量的问题
var saveFile = function(strData, filename) {
  var link = document.createElement('a');
  if (typeof link.download === 'string') {
    document.body.appendChild(link); //Firefox requires the link to be in the body
    link.download = filename;
    link.href = strData;
    link.click();
    document.body.removeChild(link); //remove the link when done
  } else {
    location.replace(strData);
  }
}

async function takeFullPageScreenshot() {
  const pageWidth = document.documentElement.scrollWidth;
  const pageHeight = document.documentElement.scrollHeight;
  const threeCanvas = core.renderer.domElement;
  // 记录canvas在页面中的实际位置
  const canvasRect = threeCanvas.getBoundingClientRect();
  const scrollX = window.scrollX;
  const scrollY = window.scrollY;

  // 截取HTML层
  threeCanvas.style.visibility = 'hidden'; // 临时隐藏canvas避免重复截取
  const htmlCanvas = await html2canvas(document.body, {
    width: pageWidth,
    height: pageHeight,
    scrollX: 0,
    scrollY: 0,
    useCORS: true // 允许加载跨域图片
  });
  threeCanvas.style.visibility = 'visible';

  // 读取Three.js场景截图
  const threeImg = new Image();
  threeImg.src = core.renderer.domElement.toDataURL('image/jpeg', 0.95);
  await new Promise(resolve => threeImg.onload = resolve);

  // 合成最终图片
  const finalCanvas = document.createElement('canvas');
  finalCanvas.width = pageWidth;
  finalCanvas.height = pageHeight;
  const ctx = finalCanvas.getContext('2d');
  // 先绘制Three.js场景层
  ctx.drawImage(
    threeImg,
    canvasRect.left + scrollX,
    canvasRect.top + scrollY,
    canvasRect.width,
    canvasRect.height
  );
  // 再绘制HTML内容层
  ctx.drawImage(htmlCanvas, 0, 0, pageWidth, pageHeight);

  // 导出下载
  const finalImgData = finalCanvas.toDataURL('image/jpeg', 0.95);
  saveFile(finalImgData.replace('image/jpeg', 'image/octet-stream'), 'full-screenshot.jpg');
}

// 调用方法即可触发截图
takeFullPageScreenshot().catch(err => console.error('截图失败:', err));

方案2:使用支持WebGL的DOM截图库

如果不想手动处理合成逻辑,可以选择原生支持WebGL画布识别的截图库,这类库不会尝试把canvas元素转HTML重绘,而是直接读取WebGL画布的像素内容,自动处理层级合成,直接调用整页截图接口即可得到包含Three.js场景的完整截图。注意不要使用不支持WebGL的旧版截图库,否则会出现画布区域全黑的问题。

方案3:原生渲染层截图(适用于桌面端套壳场景)

如果页面运行在Electron、PWA桌面端等环境,可以直接调用Chromium内核内置的页面捕获API,这个API是从浏览器渲染层直接读取像素,不管是DOM内容还是WebGL、CSS3D内容都可以完整截取,不需要额外处理兼容问题。

常见坑点
  • 所有图片资源(包括HTML里的img标签、Three.js加载的纹理)都要配置CORS跨域允许,否则画布会被跨域污染,调用toDataURL时会抛出安全错误
  • 如果页面有fixed定位的元素,截图前要把滚动位置重置到顶部,或者计算位置时把滚动偏移量算准,避免元素错位
  • 如果Three.js场景开启了后处理效果,要确保后处理通道渲染完成后再调用截图方法,否则会截到未渲染完成的画面
  • 高清屏场景下要把devicePixelRatio算入画布尺寸,否则导出的截图会模糊

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:09:42