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

如何将含CSS背景图的canvas c2内容完整导出到canvas c3?

问题

我有一张id为picc的图片,先使用drawImage()方法将其绘制到canvas c1中;接着通过getImageData()获取c1的图像数据,修改部分像素的透明度后,用putImageData()将处理后的数据放到带有CSS背景图的canvas c2中。请问如何让另一个canvas c3完整包含c2的输出内容(包括其CSS背景图)?

当前代码如下(注:渲染canvas时可能存在跨域问题):

现有代码

HTML

<div>
  <img src='https://previews.123rf.com/images/rabbybsl/rabbybsl2008/rabbybsl200800018/153141202-holi-color-background-on-white-background.jpg' id="picc" width="360" height="640"/>
</div>

<div>
  <canvas id="c1"></canvas>
  <canvas id="c2"></canvas>
</div>

CSS

#c2 {
  background-image: url("https://cdn.britannica.com/84/4284-050-16C7E8C2/Photograph-Jupiter-range-Voyager-1-cloud-bands-February-1-1979.jpg");
  background-repeat: no-repeat;
  background-size: cover;
}

JavaScript

document.addEventListener("DOMContentLoaded", () => {
  img = document.getElementById("picc");
  c1 = document.getElementById("c1");
  ctx1 = c1.getContext("2d");
  c2 = document.getElementById("c2");
  ctx2 = c2.getContext("2d");
  c1.width = c2.width = img.width; 
  c1.height = c2.height = img.height; 

  ctx1.drawImage(img, 0, 0, img.width, img.height);
  let frame = ctx1.getImageData(0, 0, c1.width, c1.height);

  let l = frame.data.length / 4;  
  
  for (let i = 0; i < l; i++) {
    let r = frame.data[i * 4 + 0];
    let g = frame.data[i * 4 + 1];
    let b = frame.data[i * 4 + 2];
    if (g >= 242 && r >= 244 && b <= 243)
      frame.data[i * 4 + 3] = 0
  }

  ctx2.putImageData(frame, 0, 0);
});

解决方案

canvas的drawImage()只能捕获目标canvas自身的像素数据,读不到它的CSS背景样式,所以要让c3完整包含c2的内容(背景+canvas绘制层),有两种实用方法:

方法一:先给c3画背景,再叠上c2的内容

这个方案不用改你现有的c2代码,只要新增c3的处理逻辑:

  1. 新建一个Image对象,加载c2的CSS背景图
  2. 等背景图加载完成后,按照c2的background-size: cover规则绘制到c3上
  3. 再把c2的canvas内容叠加到c3上

修改后的完整代码

HTML(新增c3)
<div>
  <img src='https://previews.123rf.com/images/rabbybsl/rabbybsl2008/rabbybsl200800018/153141202-holi-color-background-on-white-background.jpg' id="picc" width="360" height="640"/>
</div>

<div>
  <canvas id="c1"></canvas>
  <canvas id="c2"></canvas>
  <canvas id="c3"></canvas> <!-- 新增c3 -->
</div>
JavaScript(新增c3处理逻辑)
document.addEventListener("DOMContentLoaded", () => {
  img = document.getElementById("picc");
  c1 = document.getElementById("c1");
  ctx1 = c1.getContext("2d");
  c2 = document.getElementById("c2");
  ctx2 = c2.getContext("2d");
  c3 = document.getElementById("c3");
  ctx3 = c3.getContext("2d");
  
  c1.width = c2.width = c3.width = img.width; 
  c1.height = c2.height = c3.height = img.height; 

  // 原有c1、c2的处理逻辑
  ctx1.drawImage(img, 0, 0, img.width, img.height);
  let frame = ctx1.getImageData(0, 0, c1.width, c1.height);

  let l = frame.data.length / 4;  
  for (let i = 0; i < l; i++) {
    let r = frame.data[i * 4 + 0];
    let g = frame.data[i * 4 + 1];
    let b = frame.data[i * 4 + 2];
    if (g >= 242 && r >= 244 && b <= 243)
      frame.data[i * 4 + 3] = 0
  }
  ctx2.putImageData(frame, 0, 0);

  // 处理c3:先绘制背景图,再叠加c2的内容
  const bgImg = new Image();
  // 跨域处理:如果图片来自外部域名,需服务器支持CORS
  bgImg.crossOrigin = "anonymous";
  bgImg.src = getComputedStyle(c2).backgroundImage.replace(/url\(['"]?(.*?)['"]?\)/, '$1');
  
  bgImg.onload = () => {
    // 按照cover模式绘制背景图
    const scale = Math.max(c3.width / bgImg.width, c3.height / bgImg.height);
    const x = (c3.width - bgImg.width * scale) / 2;
    const y = (c3.height - bgImg.height * scale) / 2;
    ctx3.drawImage(bgImg, x, y, bgImg.width * scale, bgImg.height * scale);
    
    // 叠加c2的canvas内容
    ctx3.drawImage(c2, 0, 0);
  };
});

方法二:把背景图直接画到c2的canvas内部

直接调整c2的实现,不用CSS背景,改成把背景图画到canvas内部,这样c3直接调用drawImage(c2)就能拿到所有内容:

  1. 先加载背景图,绘制到c2上
  2. 再把处理后的透明图像叠上去(注意putImageData()会覆盖像素,所以得先画背景)

修改后的完整代码

CSS(移除c2的背景样式)
/* 移除#c2的背景样式 */
JavaScript(修改c2的绘制逻辑)
document.addEventListener("DOMContentLoaded", () => {
  img = document.getElementById("picc");
  c1 = document.getElementById("c1");
  ctx1 = c1.getContext("2d");
  c2 = document.getElementById("c2");
  ctx2 = c2.getContext("2d");
  c3 = document.getElementById("c3");
  ctx3 = c3.getContext("2d");
  
  c1.width = c2.width = c3.width = img.width; 
  c1.height = c2.height = c3.height = img.height; 

  // 原有c1的处理逻辑
  ctx1.drawImage(img, 0, 0, img.width, img.height);
  let frame = ctx1.getImageData(0, 0, c1.width, c1.height);

  let l = frame.data.length / 4;  
  for (let i = 0; i < l; i++) {
    let r = frame.data[i * 4 + 0];
    let g = frame.data[i * 4 + 1];
    let b = frame.data[i * 4 + 2];
    if (g >= 242 && r >= 244 && b <= 243)
      frame.data[i * 4 + 3] = 0
  }

  // 修改c2:先绘制背景图,再叠加处理后的图像
  const bgImg = new Image();
  bgImg.crossOrigin = "anonymous";
  bgImg.src = "https://cdn.britannica.com/84/4284-050-16C7E8C2/Photograph-Jupiter-range-Voyager-1-cloud-bands-February-1-1979.jpg";
  
  bgImg.onload = () => {
    // 按照cover模式绘制背景
    const scale = Math.max(c2.width / bgImg.width, c2.height / bgImg.height);
    const x = (c2.width - bgImg.width * scale) / 2;
    const y = (c2.height - bgImg.height * scale) / 2;
    ctx2.drawImage(bgImg, x, y, bgImg.width * scale, bgImg.height * scale);
    
    // 叠加处理后的图像
    ctx2.putImageData(frame, 0, 0);
    
    // c3直接绘制c2即可
    ctx3.drawImage(c2, 0, 0);
  };
});

注意事项

  • 跨域问题:如果使用的图片来自外部域名,需要确保图片服务器配置了CORS允许跨域访问,否则getImageData()和drawImage()会抛出安全错误。可以通过设置img.crossOrigin = "anonymous"尝试解决,但最终取决于服务器是否支持。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 21:10:35