如何在 Google Charts 中使用 svg 内的图像获取 png(base64)?

IT技术 javascript svg google-visualization
2021-03-09 21:40:48

如何在 svg 中获取带有图像的 base64?检查我从另一个问题中得到的这个小提琴如果您看到第二个图形,则它不会生成覆盖该条的图像。

var chart = new google.visualization.ColumnChart(document.getElementById('chart_survey'));

$("[fill='#FFFFFF']").each(function( index, element ) {
    var svgimg = document.createElementNS('http://www.w3.org/2000/svg','image');
    svgimg.setAttributeNS(null,'x',element.x.baseVal.value);
    svgimg.setAttributeNS(null,'y',element.y.baseVal.value);
    svgimg.setAttributeNS(null,'width',element.width.baseVal.value);
    svgimg.setAttributeNS(null,'height',element.height.baseVal.value);
    svgimg.setAttributeNS(null,'preserveAspectRatio','none');
    svgimg.setAttributeNS('http://www.w3.org/1999/xlink','href', '${application.contextPath}/images/textura/patt.gif');
    $(element).parent().append(svgimg);
});

$('#test').val(chart.getImageURI())
4个回答

为了将此 svg 保存到 png,它保持链接,<image>那么您必须首先将每个<image>'s编码href为 dataURL。

编辑

我重写了原始片段(仍然可以在编辑历史记录中找到)

  • 我将<image>标签更改 <use>. 这导致更少的内存使用。

  • 我还为 IE11 添加了一个后备,它接受将外部图像编码为数据 URL,但仍然无法通过画布将 svg 转换为 png。回退将用<img>画布替换目标标签。用户可以通过右键单击保存后者。

  • 一些警告:
    它在 Safari 7 中不起作用,可能在其他过时的 webkit 浏览器中也不起作用。这是一个奇怪的错误,因为它在本地主机中确实像魅力一样工作,但不会在任何其他网络上(即使在我的家庭网络上,使用 192.168.xxx)。
    IE 9 & IE 10 将无法将外部图像转换为数据 URL,CORS 问题。

// What to do with the result (either data URL or directly the canvas if tainted)
var callback = function(d, isTainted) {
  if (!isTainted) {
    $('#chartImg')[0].src = d;
  } else
    $('#chartImg')[0].parentNode.replaceChild(d, $('#chartImg')[0]);
};
// The url of the external image (must be cross-origin compliant)
var extURL = 'https://dl.dropboxusercontent.com/s/13dv8vzmrlcmla2/tex2.jpg';

google.load('visualization', '1', {
  packages: ['corechart']
})

var encodeCall = getbase64URI.bind(this, extURL, callback);
google.setOnLoadCallback(encodeCall);



// Google Chart part
function drawVisualizationDaily(imgUrl, callback, isTainted) {

  var data = google.visualization.arrayToDataTable([
    ['Daily', 'Sales'],
    ['Mon', 4],
    ['Tue', 6],
    ['Wed', 6],
    ['Thu', 5],
    ['Fri', 3],
    ['Sat', 7],
    ['Sun', 7]
  ]);

  var chart = new google.visualization.ColumnChart(document.getElementById('visualization'));

  chart.draw(data, {
    title: "Daily Sales",
    width: 500,
    height: 400,
    hAxis: {
      title: "Daily"
    }
  });

  // Link to chart's svg element
  var svgNode = chart.ea.querySelector('svg');
  // Create a symbol for our image
  var symbol = document.createElementNS('http://www.w3.org/2000/svg', 'symbol');
  // An svg wrapper to allow size changing with <use>
  symbol.setAttributeNS(null, 'viewBox', '0,0,10,10');
  symbol.setAttributeNS(null, 'preserveAspectRatio', 'none');
  symbol.id = 'background';
  // And the actual image, with our encoded image
  var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
  img.setAttributeNS(null, 'preserveAspectRatio', 'none');
  img.setAttributeNS(null, 'width', '100%');
  img.setAttributeNS(null, 'height', '100%');
  img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imgUrl);

  symbol.appendChild(img);
  svgNode.appendChild(symbol);

  var blueRects = $("[fill='#3366cc']");
  var max = blueRects.length - 1;
  blueRects.each(function(index, element) {
    var svgimg = document.createElementNS('http://www.w3.org/2000/svg', 'use');
    svgimg.setAttributeNS(null, 'x', element.x.baseVal.value);
    svgimg.setAttributeNS(null, 'y', element.y.baseVal.value);
    svgimg.setAttributeNS(null, 'width', element.width.baseVal.value);
    svgimg.setAttributeNS(null, 'height', element.height.baseVal.value);
    svgimg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '#background');
    svgNode.appendChild(svgimg);

    if (index === max && !isTainted) // no need to call it if we don't have our dataURL encoded images
    // a load event would be better but it doesn't fire in IE ...
      setTimeout(exportSVG.bind(this, svgNode, callback, isTainted), 200);
  });
}

function exportSVG(svgNode, callback, isTainted) {

  var svgData = (new XMLSerializer()).serializeToString(svgNode);

  var img = new Image();
  img.onload = function() {
    var canvas = document.createElement('canvas');
    canvas.width = svgNode.getAttribute('width');
    canvas.height = svgNode.getAttribute('height');
    canvas.getContext('2d').drawImage(this, 0, 0);
    var data, isTainted;
    try {
      data = canvas.toDataURL();
    } catch (e) {
      data = canvas;
      isTainted = true;
    }
    callback(data, isTainted);
  }
  img.src = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
}

// A simple function to convert an images's url to base64 data URL
function getbase64URI(url, callback) {
  var img = new Image();
  img.crossOrigin = "Anonymous";
  img.onload = function() {
    var c = document.createElement('canvas');
    c.width = this.width;
    c.height = this.height;
    c.getContext('2d').drawImage(this, 0, 0);
    var isTainted;
    try {
      c.toDataURL();
    } catch (e) {
      isTainted = true;
    }
    // if the canvas is tainted, return the url
    var output = (isTainted) ? url : c.toDataURL();
    drawVisualizationDaily(output, callback, isTainted);
  }
  img.src = url;
}
svg    { border: 1px solid yellow; }
img    { border: 1px solid green;  }
canvas { border: 1px solid red;    }
<script src="http://www.google.com/jsapi?.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="visualization"></div>
Right-click this image to save it:
<br>
<img id="chartImg" />

你的意思是 svg<image>标签?我在我的电脑上没有看到它,但我现在可能禁用了缓存。但无论如何,两个示例都应该使用相同的浏览器缓存。
2021-04-19 21:40:48
是的,我猜是这三个中的一点。但这主要是由于您使用的图像尺寸较大。第一个片段也可以是数据消耗性的,这意味着如果您在 svg 中链接了不同的图像,则可以完成许多网络请求。但是“png 一直在里面添加图像”是什么意思?
2021-04-21 21:40:48
你说的消费是什么?重物?加载整个 png 需要很长时间?更多的公羊?因为我注意到我看到 png 不断在里面添加图像。
2021-04-23 21:40:48
对于第一个示例,当我刷新页面时,我可以看到<img>添加 tex.jpg更新本身,因为在生成图形之后。但第二个,不是。
2021-04-25 21:40:48
但是请注意,这可能非常消耗资源:它首先从服务器获取所有图像,然后在将最终图像重新编码为最后一个 dataURL 之前对它们进行编码。对于生产,您应该真正考虑使用一些服务器端解决方案,例如@Guest271314指出解决方案
2021-05-11 21:40:48

此示例创建一个由图像填充的 svg 容器。在我的示例中,图像是 svg 图像,但您应该能够放入任何类型的图像(jpg、png、gif)。首先创建容器,然后在容器内创建图像。

 // create svg
                var svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
                svg.setAttribute('class','shadowed handle_icon_sensors');
                svg.setAttribute('height','25');
                svg.setAttribute('width','25');
                svg.setAttribute('id',idAttr);
                svg.setAttribute('z-index','21000');
                document.getElementById("zones_container").appendChild(svg);

            // create svg image
            var svgimg = document.createElementNS('http://www.w3.org/2000/svg','image');
            svgimg.setAttribute('height','25');
            svgimg.setAttribute('width','25');
            svgimg.setAttributeNS('http://www.w3.org/1999/xlink','href','svg/icon_sensorYellow.svg');
            svgimg.setAttribute('x','0');
            svgimg.setAttribute('y','0');
            document.getElementById(idAttr).appendChild(svgimg);
我没有完全正确,你的意思是,我有图形,你的var svg = ...意志再次转换为 svg(我的图形)到一个新的,然后我得到我想要的图像?
2021-04-25 21:40:48
关键是您必须创建 svg 容器(// 创建 svg),然后在其中创建图像(// 创建 svg 图像)。我指的是我的图像“'svg/icon_sensorYellow.svg'”是一个 svg 图像。但是任何其他网络图像也应该可以使用。“idAttr”是由我的代码创建的,用于在 for 循环中创建多个实例,但您选择的任何 id 都可以使用。只需将变量设置为等于您的 id。
2021-04-26 21:40:48
你能帮我使用小提琴吗?我试图使用给我 svg 但没有生成图像的 url。
2021-05-11 21:40:48

注意,当转换为srcof imgelement reserved 时blue background-color

尝试后 .each()

// set namespace attributes
var svg = $("svg").attr({
    "xmlns": "http://www.w3.org/2000/svg",
    "xmlns:xlink": "http://www.w3.org/1999/xlink"
})[0];

// create `data URI` of `svg`
var dataURI = "data:image/svg+xml;charset=utf-8;base64," + btoa(svg.outerHTML.trim());    

// post `svg` as `data URI` to server
$.post("/path/to/server/", {
    html: dataURI
}, "html")
    .then(function (data) {
    // do stuff
    // `svg` `data URI`
    console.log(data);

}, function (jqxhr, textStatus, errorThrown) {
    console.log(textStatus, errorThrown);
});

jsfiddle http://jsfiddle.net/R8A8P/58/

@fsijs在 chrome、chromium、nightly 上尝试过,没有任何错误通知。如果data URI在页面加载“可视化”之前可以包含链接图像,则“条”处的图像可能不被视为“跨原点”或“跨域”。尝试pngcanvasfrom 中创建cross origin,也许具有挑战性。实际创建上面创建的png图像的一种方法svg是利用 Firefox Web 控制台创建svg. 这不能解决用链接的交叉原点图像构建条形图的问题。stackoverflow.com/q/20584355/2801559
2021-04-22 21:40:48
@guest271314,为此他实际上需要将外部图像转换为 base64 dataURL 并首先嵌入它们。之后它将起作用,至少在非 IE 浏览器上。看看我的答案,它以一种可怕的方式做到了。但实际上,正如您在对 OP 的评论中所述,服务器端解决方案是最好的解决方案,已尝试使用 Imagick 并且它也适用于外部图像。我不知道inkscape,但它也值得一试。
2021-04-30 21:40:48
@fsi 有趣,这里没有收到错误通知;无论是在打开window还是data URIconsole浏览器在 ?
2021-05-02 21:40:48
SVG 没有正确呈现,This page contains the following errors: error on line 1 at column 1: Encoding error.
2021-05-10 21:40:48
2021-05-10 21:40:48

这个 html 在react中起作用

 <a
    href={chart.getImageURI()}
    download="chart.png">
    getImage
  </a>