【问题标题】:How can I get png(base64) with images inside of svg in Google Charts?如何在 Google Charts 中使用 svg 内的图像获取 png(base64)?
【发布时间】:2015-07-10 14:13:32
【问题描述】:

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

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())

【问题讨论】:

  • fc03.deviantart.net/fs27/i/2011/193/9/f/… 似乎是顶部图表中的图像?是否要求将相同的图像应用于较低的图表?似乎没有尝试在 jsfiddle jsfiddle.net/R8A8P/51 检索 base64
  • 是的,图像似乎是顶级图表。是的,它的要求,因为我想要图像而不是颜色,所以我放在顶部。但是当我检索base64时,图像不存在。
  • 不能使用已经填充栏的imagebase64 和现有的图片链接有什么区别?
  • 见帖子。重新查看问题后,认为要求是从svg 元素创建png 图像?尝试过,但没有保留条形颜色的链接图像。创建了svgdata URI,当在浏览器中加载时,它会在条形图上保留链接图像。

标签: javascript svg google-visualization


【解决方案1】:

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

编辑

我重写了原来的 sn-ps (仍然可以在编辑历史中找到)

  • 我将<image> 标签更改为<use>。这会减少内存使用量。

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

  • 一些注意事项:
    它在 Safari 7 中不起作用,可能在其他过时的 webkit 浏览器中也不起作用。这是一个奇怪的错误,因为它在 localhost 中确实像一个魅力,但不会在任何其他网络上(即使在我的家庭网络上,使用 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" />

【讨论】:

  • 但是要小心,这可能非常消耗:它首先从服务器获取所有图像,然后对它们进行编码,然后再将最终图像重新编码为最后一个 dataURL。对于生产,您真的应该考虑使用一些服务器端解决方案,例如@Guest271314 的the one pointed
  • 你是什么意思?重物?加载整个 png 的时间太长?更多的内存?因为我注意到我看到 png 不断在里面添加图像。
  • 是的,我猜是这三个中的一小部分。但这主要是由于您使用的图像尺寸很大。第一个 sn-p 也可以是数据消耗的,这意味着如果您在 svg 中链接了不同的图像,则可以完成很多网络请求。但是“png不断在里面添加图像”是什么意思?
  • 对于第一个示例,当我刷新页面时,我可以看到 &lt;img&gt; 更新自身以添加 tex.jpg,因为在生成图形之后。但第二个,不是。
  • 你的意思是 svg &lt;image&gt; 标签?我在我的电脑上看不到它,但我现在可能禁用了缓存。但无论如何,这两个示例都应该使用相同的浏览器缓存。
【解决方案2】:

此示例创建一个由图像填充的 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(我的图形)到一个新的,然后我得到我想要的图像?
  • 关键是你必须创建 svg 容器(// 创建 svg),然后在其中创建图像(// 创建 svg 图像)。我指的是我的图像“'svg/icon_sensorYellow.svg'”是一个 svg 图像。但是任何其他网络图像也应该可以工作。 “idAttr”由我的代码创建,用于在 for 循环中创建多个实例,但您选择的任何 id 都可以使用。只需将变量设置为等于您的 id。
  • 你能帮我用小提琴吗?我尝试使用给我 svg 但没有生成图像的 url。
【解决方案3】:

注意,当转换为imgsrc 时保留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/

【讨论】:

  • SVG 渲染不正确,This page contains the following errors: error on line 1 at column 1: Encoding error.
  • @fsi 有趣,这里没有收到错误通知;既不是在windowdata URIconsole 上打开的?浏览器试过了?
  • @fsi 在上面的 chrome、chromium、nightly 上尝试了js,没有任何错误通知。如果可以在页面加载“可视化”之前将链接图像包含为data URI,则“条”处的图像可能不会被视为“跨域”或“跨域”。尝试从 cross origin 创建 png canvas 可能具有挑战性。实际创建上面创建的svgpng 图像的一种方法是利用Firefox Web 控制台创建svg 的屏幕截图。这不能解决使用链接的交叉原点图像构建条形图的问题。见stackoverflow.com/q/20584355/2801559
  • @guest271314 ,为此他实际上需要将外部图像转换为base64 dataURL并首先嵌入它们。之后它将工作,至少在非 IE 浏览器上。请参阅my answer,它以一种可怕的方式做到了。但实际上,正如您在对 OP 的评论中所说,服务器端解决方案是最好的,与 Imagick 一起尝试过,它也适用于外部图像。我不知道inkscape,但它也值得一试。
【解决方案4】:

这个 html 在反应中起作用

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-01
    • 2022-08-21
    • 2011-04-01
    • 2021-11-11
    • 2015-05-22
    • 1970-01-01
    相关资源
    最近更新 更多