【问题标题】:Filters on SVG objects not being applied when they are drawn to canvas in Firefox在 Firefox 中绘制到画布时未应用 SVG 对象的过滤器
【发布时间】:2016-03-17 17:10:23
【问题描述】:

我正在尝试制作一些 SVG,然后将它们绘制到画布上。它们需要被绘制到画布上,因为之后我会进行一些像素级的操作。我正在关注MDN article 并尝试绘制一个矩形。到目前为止一切顺利(fiddle)。但是当我尝试apply a filter 时,它会中断。

(我使用的示例是一个矩形,但我稍后将使用自定义路径,因此画布 rect() 方法不合适)。

发生了什么事?我的直觉是 filter 节点在加载 svg 节点之前并不“存在”......但是,rect 节点也不存在,所以我真的不知道......

var data = '<svg ...' //the svg code
var DOMURL = window.URL || window.webkitURL || window;

var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);

img.onload = function () {
  ctx.drawImage(img, 0, 0, 223, 223);
  DOMURL.revokeObjectURL(url);
}

img.src = url;

编辑:天哪,我什至没有想到它是浏览器!我正在使用 Firefox 43。它以铬显示;我确实需要它在 Firefox/gecko 中正确显示,因为 FF 在目标受众的浏览器市场中占有很大份额。我检查了 FF 42 和 43 的 Linux 和 Windows 版本,它们都有同样的问题。

【问题讨论】:

  • 你的问题是什么,小提琴似乎工作正常。
  • 小提琴也适合我。这是特定浏览器的问题吗?

标签: canvas svg


【解决方案1】:

可能有一些字符不能很好地传递到 blob 中。
(正如 Robert Longson 所指出的,它实际上是 FF bug,但有 @987654322 @反正用这个方法报告)

解决方案是将 [string to blob to blobURI] 替换为 [encodedString to dataURI] :

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var data = '<svg xmlns="http://www.w3.org/2000/svg" width="223" height="223">' +
  '<defs>' +
  '<filter id="f1" x="0" y="0">' +
  '<feGaussianBlur in="SourceGraphic" stdDeviation="5" />' +
  '</filter>' +
  '</defs>' +
  '<rect class="shape" x="0" y="0" width="100" height="150" transform="translate(40,15) rotate(15,50,75)" fill="#006791" filter="url(#f1)"></rect></svg>';

// first encode the special characters
var svg_data = encodeURIComponent(data);
// create a dataURI version
var url = 'data:image/svg+xml; charset=utf8, ' + svg_data;

var img = new Image();
img.onload = function() {
  ctx.drawImage(img, 0, 0, 223, 223);
}

img.src = url;
<canvas id="canvas" style="border:2px solid black;" width="223" height="223">
</canvas>

Ps:一个更均匀的消毒剂解决方案,但有点重,是使用DOMParser,解析你的字符串,然后用XMLSerializer将它的结果序列化为字符串,编码这个字符串,最后使它成为一个dataURI :

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var data = '<svg xmlns="http://www.w3.org/2000/svg" width="223" height="223">' +
  '<defs>' +
  '<filter id="f1" x="0" y="0">' +
  '<feGaussianBlur in="SourceGraphic" stdDeviation="5" />' +
  '</filter>' +
  '</defs>' +
  '<rect class="shape" x="0" y="0" width="100" height="150" transform="translate(40,15) rotate(15,50,75)" fill="#006791" filter="url(#f1)"></rect></svg>';

// create a new DOMParser
var parser = new DOMParser();
// Parse our string as an svg document
var doc = parser.parseFromString(data, 'image/svg+xml');
// Serialize the new svg document
var svg_data = new XMLSerializer().serializeToString(doc.documentElement);
// set it to a dataURI
var url = 'data:image/svg+xml; charset=utf8, '+encodeURIComponent(svg_data);

var img = new Image();
img.onload = function () {
  ctx.drawImage(img, 0, 0, 223, 223);
}
img.src = url;
<canvas id="canvas" style="border:2px solid black;" width="223" height="223">
</canvas>

现在您已经有了解决方案,我们可以尝试回答“发生了什么?”

Chrome 在获取使用xlink:href 属性或&lt;funcIRI&gt; (url(#xxx)) 内部属性引用的外部元素的方式上存在问题。
例如,如果您在外部 CSS 样式表中进行此类引用,它不会引用良好的 URI 路径,并且它无法获取在外部引用中指定的引用。

请参阅this codepen 了解更多信息:

球应该用两个radialGradients 绘制,放在一个外部svg 文档中。径向渐变本身确实引用了同一文档中的linearGradient

当 chrome 只能获取直接元素(复选标记)时,Firefox 会正确获取每个请求的元素。
火狐:Chrome:

当您使用URL.createObjectURL() 时,您在浏览器的内存中创建一个新文件,具有一个特殊的位置(其location.origin 将设置为已创建的页面之一但不会设置pathNamehost。)。

我没有进入 FF 获取代码,但我猜他们在这里做了一些事情,当你在这样一个特殊的 url 方案中时,即使内部引用也会出错,这就是你的情况。

所以是的,这里的 FF 坏了,但肯定是因为其他浏览器甚至没有尝试做的事情......

【讨论】:

  • 完美运行,您对浏览器 svg/xml 机制内部发生的事情的了解程度给我留下了深刻的印象。非常感谢! (编辑:赏金有时间限制。我稍后会回来)
猜你喜欢
  • 1970-01-01
  • 2012-06-01
  • 2017-08-02
  • 2011-02-20
  • 2013-01-30
  • 2013-05-26
  • 1970-01-01
  • 2016-12-03
  • 2013-01-07
相关资源
最近更新 更多