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 上的示例,能嵌入其他命名空间的元素。所以我做了一个实验:

<!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 支持如下的声明:

<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>

这么写的时候:

<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?

分享到 评论