Bulletproof 20150917-0

9月17日凌晨的脑洞,有可能能优化 Canvas2D 版的实现

又是脑洞时刻。查找“css filter canvas”之后发现了一个 StackOverflow 上的回答

问题:

I have applied some CSS3 filters to an image on a canvas like this:

-webkit-filter brightness(0%) grayscale(100%) contrast(1000%)

But when I save the image to my computer, the original image is being saved, not the one with applied filters. Is there a way to save the modified version of it?

(很明显,<canvas> 要作为整个 DOM 元素参与样式计算,而不是其上的像素内容,相当于缓冲区内容。所以直接应用样式是不可以的,我以前试过。)

二楼的回答:

Apply the filter in the canvas using a shader, rather than using a CSS style.

Alternately, make a hidden canvas, draw your content to that, apply the CSS3 filter to it, and then use that canvas to output to the visible one.

二楼评论中一位ID为 katspaugh 的同志写道:

Put the filtered canvas into SVG foreignObject, then insert the serialised SVG as a Base64-encoded image into the visible canvas. (Just kidding)

有趣的是,在回答评论中提到了 <foreignObject>。看 MDN 上的示例,能嵌入其他命名空间的元素。所以我做了一个实验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<img src="http://pic19.nipic.com/20120308/7491614_141057681000_2.png" width="300" height="300" id="csimg"></img>

<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="feglow0">
<feColorMatrix type="matrix" values="0 0 0 255 0 0 0 0 255 0 0 0 0 0 0 0 0 0 1 0">
</feColorMatrix>
<feGaussianBlur stdDeviation="40 40" result="coloredBlur">
</feGaussianBlur>
<feMerge>
<feMergeNode in="coloredBlur"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
</svg>

<svg width="500px" height="500px" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
<switch>
<foreignObject width="500" height="500" requiredExtensions="http://www.w3.org/1999/xhtml">
<!-- XHTML content goes here -->
<body xmlns="http://www.w3.org/1999/xhtml">
<canvas style="filter: url(#feglow0);" id="canvas" width="500" height="500">
</canvas>
</body>
</foreignObject>
<text font-size="10" font-family="Verdana">
<tspan x="10" y="10">You see no canvas.</tspan>
</text>
</switch>
</svg>

<a href="javascript:;" onclick="redraw()">REDRAW</a>
<script type="text/javascript">
function redraw() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.drawImage(document.getElementById("csimg"), 0, 0, 300, 300);
}
</script>
</body>
</html>

这次示例应该不存在安全性的问题,保存到本地直接就可以打开预览。注意我在 SVG 内部为 canvas 指定了样式。

但是这引发了浏览器兼容问题。对于 XML 命名空间的声明(见 <foreignObject> 元素和之后的 <body> 元素),Firefox 和 Edge 要求二者都指定命名空间 http://www.w3.org/1999/xhtml,Chrome 有时候要求二者都不指定命名空间(为空/无属性),IE11 不支持。而且,Chrome 45 和 Maxthon 4(内核为 Chrome 30)行为还不一样,前者要求不指定,后者不支持(Chrome 本来支持,不知道是不是傲游的bug)。


Video Destruction 的示例如果将定时函数换为 requestAnimationFrame(),会引发严重的效率问题。狂点一通,帧率就从60直降到不到10。相比之下,稳定调用帧率30效果好一些。——直觉上来讲,和 Bulletproof 最初的三维球示例是相反的。


继续。Firefox 和 Edge 支持如下的声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
<defs>
<filter id="feglow0">
</filter>
</defs>
<g filter="url(#feglow0)">
<foreignObject width="500" height="500" requiredExtensions="http://www.w3.org/1999/xhtml">
<!-- XHTML content goes here -->
<body xmlns="http://www.w3.org/1999/xhtml">
<canvas id="canvas" width="500" height="500">
</canvas>
</body>
</foreignObject>
</g>
</svg>

这么写的时候:

1
2
3
4
5
6
7
8
9
<g filter="url(#feglow0)">
<foreignObject width="500" height="500">
<!-- XHTML content goes here -->
<body>
<canvas id="canvas" width="500" height="500" style="-webkit-filter: url(#feglow0); -moz-filter: url(#feglow0); -ms-filter: url(#feglow0); -o-filter: url(#feglow0); filter: url(#feglow0);">
</canvas>
</body>
</foreignObject>
</g>

在 Firefox、Edge、Chrome 45 上显示正常,Maxthon 4 上无法显示滤镜效果(显示为原图),IE11 不支持。(说明 Chrome 看 filter 属性。)


话说,9月15日(就在前天)的 W3C SVG 2 推荐标准中就明确指明:

Additionally SVG allows embedded content using HTML5 ‘video’, ‘audio’, ‘iframe’ and ‘canvas’ elements.

要是标准早日实现就好了,我就不用那么头疼了……


Pixi?

分享到 评论