经纬志
首页 / 实战手册 / 3D 与 Cesium

toDataURL 截出来是黑图,Cesium 也一样

先看结论

点击“导出图片”,拿到一张纯黑或全透明的 PNG;同一段代码在别的页面又是正常的。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

页面上地球显示正常,调用截图后得到 1920×1080 的黑色图片。 部分浏览器不报错,部分会给出提示:

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement':
Tainted canvases may not be exported.

如果是黑色而不是报错,说明跨域问题不存在,问题在绘制缓冲已被清空。

原因

WebGL 的绘制缓冲默认不保留。浏览器在合成到屏幕后就会清空缓冲, 此时再调用 toDataURL,读到的是一张空画布。 Cesium 的 Viewer 构造时没有打开 preserveDrawingBuffer, 所以任何异步延迟之后再截图都会拿到黑图。

另一个独立问题是影像跨域:瓦片服务器没返回 Access-Control-Allow-Origin 时,画布被标记为污染,导出会抛出上面的 DOMException。

排查过程

  1. 在点击事件里立即调用 toDataURL,看是否仍为黑图,以区分时序问题与跨域问题。
  2. 看报错文本:出现 Tainted canvases 就是跨域,没有则是缓冲问题。
  3. 检查是否有 setTimeout、await 或 requestAnimationFrame 把截图推迟到下一帧之后。

解决方案

构造时打开缓冲保留。代价是每帧多一次拷贝,帧率会略降,建议只在需要截图的页面开启:

var viewer = new Cesium.Viewer('cesiumContainer', {
  contextOptions: {
    webgl: {
      preserveDrawingBuffer: true,
      alpha: false
    }
  }
});

不想改构造参数,就在渲染完成的同一帧里取图:

function capture() {
  return new Promise(function (resolve) {
    var remove = viewer.scene.postRender.addEventListener(function () {
      remove();
      var canvas = viewer.scene.canvas;
      resolve(canvas.toDataURL('image/png'));
    });
    viewer.scene.requestRender();
  });
}

capture().then(function (dataUrl) {
  var a = document.createElement('a');
  a.href = dataUrl;
  a.download = 'scene.png';
  a.click();
});

影像跨域时,给瓦片服务补上响应头,并把影像提供者的跨域开关打开:

var provider = new Cesium.UrlTemplateImageryProvider({
  url: 'https://tile.example.cn/{z}/{x}/{y}.png',
  credit: 'Example',
  tilingScheme: new Cesium.WebMercatorTilingScheme()
});

// 服务端需返回:Access-Control-Allow-Origin: *
// Nginx 示例
// location /tiles/ { add_header Access-Control-Allow-Origin *; }

验证

截图得到的 PNG 用图片查看器打开,应能看到与页面一致的底图和模型; canvas.toDataURL().length 明显大于纯黑图的长度; 连续截三张,尺寸和内容都一致。

性能取舍:preserveDrawingBuffer: true 在移动端会增加显存占用。 只在导出页面开启,其余页面保持默认;或者用 postRender 方案, 一次截图只多渲染一帧。

引用本文

经纬志(2026)。《toDataURL 截出来是黑图,Cesium 也一样》。https://www.jingweizhi.com/cookbook/cesium-screenshot-black.html(访问日期 2026-11-03)。