canvas - 在 Firefox 中将 SVG 对象绘制到 Canvas 时,不会应用对 SVG 对象的过滤器

标签 canvas svg

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

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

发生什么事了?我的直觉是,在加载 svg 节点之前,filter 节点并不“存在”……但是,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。它以 Chromium 显示;我确实需要它在 Firefox/gecko 中正确显示,因为 FF 在目标受众的浏览器市场中占有很大的份额。我检查了 FF 42 & 43 的 Linux 和 Windows 版本,它们都有同样的问题。

最佳答案

可能有一些字符无法很好地传递到 blob 中。
(正如 Robert Longson 所指出的,它实际上是一个 FF bug ,但无论如何都用这种方法报告了 were other issues )

解决方案是替换 [ stringblobblobURI ] 进入[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:一个均匀的 sanitizer 解决方案,但有点重,是使用 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 s,放置在外部 svg 文档内。径向渐变本身引用 linearGradient在同一个文档中。

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

当您使用URL.createObjectURL()时,您正在浏览器内存中创建一个新文件,具有特殊位置(其 location.origin 将设置为创建它的页面之一,但没有 pathName 也没有 host将被设置。)。

我没有进入 FF 获取代码,但我猜想他们在这里做了一些事情,当您处于这样一个特殊的 url 方案中时,甚至会导致内部引用错误,这就是您的情况下发生的情况。

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

关于canvas - 在 Firefox 中将 SVG 对象绘制到 Canvas 时,不会应用对 SVG 对象的过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34229454/

相关文章:

javascript - 如何确定 Raphael text() 的准确高度?

javascript - Css 动画 - 显示/隐藏带有手写 svg 效果的标志

javascript - 如何使用嵌套数据通过 d3.chart 制作多个图表

javascript - Snap.svg - 如何将各种元素转换为绝对中心?

javascript - 随着质量增加,速度下降

javascript - 有什么方法可以快速确定浏览器是否支持支持 CORS 的图像而不污染浏览器?

javascript - 使用JS逐渐褪色

svg - 在 deck.js 部分绘制 dimple(d3.js) 图表

javascript - html5音频GUI开发

javascript - 在 Fabric.js 中反序列化一个 JSON 对象