我正在尝试制作一些 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 )
解决方案是替换 [ string
至blob
至blobURI
] 进入[encodedString
至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:一个均匀的 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 确实会正确获取每个请求的元素。
火狐: Chrome :
当您使用URL.createObjectURL()
时,您正在浏览器内存中创建一个新文件,具有特殊位置(其 location.origin
将设置为创建它的页面之一,但没有 pathName
也没有 host
将被设置。)。
我没有进入 FF 获取代码,但我猜想他们在这里做了一些事情,当您处于这样一个特殊的 url 方案中时,甚至会导致内部引用错误,这就是您的情况下发生的情况。
所以,是的,这里 FF 被破坏了,但肯定是因为其他浏览器甚至没有尝试做的事情......
关于canvas - 在 Firefox 中将 SVG 对象绘制到 Canvas 时,不会应用对 SVG 对象的过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34229454/