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

d3.js中导出SVG为PNG后嵌入外链图片不显示的问题

问题描述

使用d3.js创建包含Unsplash外链图片的SVG,添加下载按钮将SVG导出为PNG后,导出的PNG里嵌入的图片无法显示。

代码示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Random Image SVG with Download Button</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
  <svg id="svg-container" width="400" height="200"></svg>
  <br>
  <button onclick="downloadSvg()">Download as Image</button>

  <script>
    async function getRandomImageUrl() {
      const response = await fetch('https://source.unsplash.com/random');
      const imageUrl = response.url;
      return imageUrl;
    }

    async function createRandomImageSVG() {
      const imageUrl = await getRandomImageUrl();

      const svg = d3.select('#svg-container');

      // Append the image directly to the SVG
      svg.append('image')
        .attr('x', 0)
        .attr('y', 0)
        .attr('width', 400)
        .attr('height', 200)
        .attr('href', imageUrl); // Use href instead of xlink:href for newer browsers
    }

    createRandomImageSVG();

    function downloadSvg() {
      const svgElement = document.getElementById('svg-container');
      const serializer = new XMLSerializer();
      const svgString = serializer.serializeToString(svgElement);

      // Create a temporary canvas
      const canvas = document.createElement('canvas');
      canvas.width = 400;
      canvas.height = 200;

      const context = canvas.getContext('2d');

      // Create a new image
      const img = new Image();
      img.onload = function() {
        // Draw SVG onto the canvas
        context.drawImage(img, 0, 0);

        // Convert the canvas to data URL
        const dataUrl = canvas.toDataURL('image/png');

        // Create a temporary link element
        const a = document.createElement('a');
        a.href = dataUrl;
        a.download = 'random_image.png';
        a.click();
      };

      // Encode the SVG string to Base64 and set as source of image element
      img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgString)));
    }
  </script>
</body>
</html>

预期行为

点击“Download as Image”按钮时,SVG转换为PNG图片,下载的图片包含从Unsplash获取的随机图片。

实际行为

导出的PNG文件中未显示Unsplash的嵌入图片,仅为空白。

问题根源

SVG引用了跨域外链图片,浏览器出于安全限制,在将SVG转换为Data URL时阻止了跨域请求。

解决方案

方法1:将图片转为Base64嵌入SVG

把Unsplash图片下载后转为Base64格式嵌入SVG,彻底消除跨域问题。修改代码如下:

  1. 改造获取图片的函数,直接返回Base64编码:
async function getRandomImageBase64() {
  const response = await fetch('https://source.unsplash.com/random');
  const blob = await response.blob();
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result);
    reader.readAsDataURL(blob);
  });
}
  1. 修改SVG创建逻辑,使用Base64图片:
async function createRandomImageSVG() {
  const imageBase64 = await getRandomImageBase64();

  const svg = d3.select('#svg-container');

  svg.append('image')
    .attr('x', 0)
    .attr('y', 0)
    .attr('width', 400)
    .attr('height', 200)
    .attr('href', imageBase64);
}

方法2:Canvas分层绘制图片与SVG内容

先将Unsplash图片绘制到Canvas,再叠加SVG内容,最后导出Canvas为PNG:

修改下载函数:

async function downloadSvg() {
  const canvas = document.createElement('canvas');
  canvas.width = 400;
  canvas.height = 200;
  const ctx = canvas.getContext('2d');

  // 绘制Unsplash图片到Canvas
  const img = new Image();
  img.crossOrigin = 'anonymous';
  const imageUrl = await getRandomImageUrl();
  img.src = imageUrl;

  await new Promise((resolve) => img.onload = resolve);
  ctx.drawImage(img, 0, 0, 400, 200);

  // 绘制SVG内容到Canvas
  const svgElement = document.getElementById('svg-container');
  const serializer = new XMLSerializer();
  const svgString = serializer.serializeToString(svgElement);
  const svgImg = new Image();
  svgImg.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgString)));
  
  await new Promise((resolve) => svgImg.onload = resolve);
  ctx.drawImage(svgImg, 0, 0);

  // 导出PNG
  const dataUrl = canvas.toDataURL('image/png');
  const a = document.createElement('a');
  a.href = dataUrl;
  a.download = 'random_image.png';
  a.click();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:13:14