SVG对象在Firefox中绘制到画布时未应用的过滤器

时间:2015-12-11 17:43:48

标签: canvas svg

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

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

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

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的Linux和Windows版本。 43他们都有同样的问题。

1 个答案:

答案 0 :(得分:3)

可能有一些角色无法很好地传递到blob中。
(正如罗伯特·朗森所指出的那样,它实际上是一个FF bug,但无论如何都报告了were other issues

解决方案是将[string替换为blobblobURI]为[encodedStringdataURI]:

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属性或<funcIRI>url(#xxx))内部属性引用的外部元素方面存在错误。
例如,如果您在外部CSS样式表中进行此类引用,则它不会引用良好的URI路径,并且它无法获取外部引用中指定的引用。

有关详情,请参阅this codepen

球应涂上两个radialGradient,放在外部svg文件内。径向渐变本身引用同一文档中的linearGradient

当chrome只能获取直接元素(复选标记)时,Firefox会正确获取每个请求的元素。
Firefox:FF screenshot Chrome:enter image description here

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

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

所以是的,这里的FF被打破了,但肯定是因为其他浏览器甚至没有尝试做过......