将SVG滤镜应用于HTML5画布?

3

目标:在html5和JavaScript中应用CSS滤镜到视频。

限制条件:解决方案必须Windows 8的Internet Explorer 10兼容。我真的在开发一个Metro应用程序。

到现在为止:我有一个<video>元素,我将其输出到一个<canvas>上。我原以为我可以直接对它应用CSS过滤器(例如invertbrightness),但这些都不兼容IE10。

想法:我希望能够在画布上应用SVG滤镜。这可行吗?我需要将<canvas>复制到一个<image>上并对其应用过滤器吗?或者,是否应该用<foreignObject>包装画布?

感谢您的所有帮助!

以下是一些代码,供有兴趣的人参考:

filters.svg:

<?xml version="1.0" standalone="no"?>
<svg width="1" height="1" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <filter id="blur">
      <feGaussianBlur in="SourceGraphic" stdDeviation="2 3" />
    </filter>
  </defs>
</svg>

style.css:

.a {
    filter: url(filter.svg#blur);
    -ms-transform: matrix(-1, 0, 0, 1, 0, 0);
}

page.html:

<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
            <canvas class="a" style="width: 180px;height:180px;margin-bottom: -5px;" data-win-bind="style.backgroundColor: bc; id: effectId" />
</div>

以下代码可以实现我的目标,尽管速度非常慢。感谢Anthony!
<html>
<head>

</head>
<body>
<svg id="svgroot"  viewbox="0 0 800 800" width="800" height="800"  preserveAspectRatio="xMidYMin">
    <defs>
<filter id="myHueRotate">
<feColorMatrix type="hueRotate" values="270"/>
</filter>
</defs>
<image id="a" filter="url(#myHueRotate)"  x="0" y="0" width="300" height="300" />
<image id="b" filter="url(#myHueRotate)"  x="300" y="0" width="300" height="300" />
<image id="c" filter="url(#myHueRotate)"  x="0" y="300" width="300" height="300" />
<image id="d" filter="url(#myHueRotate)"  x="300" y="300" width="300" height="300" />
</svg>
<canvas id="canvas" height="300" width="300"></canvas>
<video id="vid" src="movie.m4v" height="300" width="300" style="display: none" autoplay/>


<script>
  var ctx = document.getElementById('canvas').getContext('2d');
  var img = new Image();
  img.src = 'img.jpg';
  img.onload = function(){
    //ctx.drawImage(img,0,0);
    //var canvasImage = canvas.toDataURL("image/png");
    //var svgImage = document.getElementById('a');
    //svgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", canvasImage);
    draw();
  }
img.load();

function draw(){
    var ctx = document.getElementById('canvas').getContext('2d');
    var vid = document.getElementById('vid')
    ctx.drawImage(vid,0,0,300,300);
    var canvasImage = canvas.toDataURL("image/png");
    document.getElementById('a').setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", canvasImage);
    document.getElementById('b').setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", canvasImage);
    document.getElementById('c').setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", canvasImage);
    document.getElementById('d').setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", canvasImage);
    setTimeout(draw,40);
}
</script>
</body>
</html>

不要将<canvas>的帧复制到<image>中(虽然您可以通过toDataURL()实现),我建议首先尝试通过<foreignObject>直接嵌入SVG中的画布或视频,并查看是否可以将过滤器应用于它。但首先,您是否已经验证了IE10是否支持您想要的SVG过滤器,即使是在静态图像上? - Phrogz
2
很遗憾,IE9/10不支持<foreignObject> - Robert Longson
@RobertLongson - IE10支持通过CSS调用SVG滤镜,所以还有希望。IE9虽然承诺要迎头赶上,但现在已经和IE7和IE8一样不重要了,除非你的老板拒绝升级。请阅读我在答案中提到的Ma Bell的引语。直到我们作为开发人员停止担心IE用户(我知道这很难),他们将始终掌握我们的命脉,而明显应该是另一种方式,即4个主要的替代浏览器更好。 - Anthony
我想知道Internet _Explorer_是否是故意的口误? ;) - halfer
1个回答

7

首先,阅读以下文章:

在IE10中采用基于标准的web图形

请特别注意以下部分:

使用SVG,而不是VML

以及

使用CSS3,而不是DX Filters

在第二个部分中,他们提到:

DX Filters与SVG Filter Effects不同,尽管两者都使用CSS属性名称filter。

第二篇文章:

滤镜和过渡简介

他们给出了如何使用invert的具体示例,但是,假设它是IE中唯一的方法,我可以理解为什么不容易找到并且在您的情况下可能有效,但css将如下:

#yourTargetElement {
     filter: DXImageTransform.Microsoft.BasicImage(invert=1);
}

他们没有提到亮度,但是他们提到了其他几个滤镜和转换,并且第一篇文章提到了使用SVG。更多细节(希望有帮助的细节)请参见:IE10中的SVG过滤器效果
这看起来像关键部分的第一部分:
一个过滤器通过filter属性应用于SVG元素,形式为 filter="url(#filterId)",或者它可以作为CSS属性filter:url(#filterId)应用。
这是第二部分:
有16种不同的过滤原语。
现在,我相信他们所指的16种是SVG的完整集合,但是知道微软公司,它也可能意味着以下任何一种:
这些是我们支持的16种,或者
这些是我们发明的16种,以便继续我们声称使IE符合标准并支持SVG/MathML,但比在任何其他浏览器中都要困难...因为我们可以。
或者,引用莉莉·汤姆林(Lily Tomlin)的话:“我们不在乎,我们不必...我们是电话公司。”
但是,假设微软公司最终意识到他们需要赶上时,阅读16种原始过滤器的进一步内容,据说你只需要嵌入SVG,并将过滤器放在正确的位置(defs)并通过css调用它们。这是他们的一个例子(我稍微修改和简化了一下):
<div id="svg_wrapper">
<svg xmlns="http://www.w3.org/2000/svg" id="svgroot" viewBox="0 0 800 533" preserveAspectRatio="xMidYMin">
    <defs>
     <filter id="filtersPicture">
             <feComposite operator="arithmetic" k1="0" k2="1" k3="0" k4="0" 
               in="SourceGraphic" in2="SourceGraphic" result="inputTo_6">
             </feComposite>      
             <feColorMatrix type="saturate" id="filter_6" values="2" 
                            data-filterId="6">
             </feColorMatrix>
             </filter>
   </svg>
  </div>

CSS(他们使用JS使其动态化,因此请注意):

<style type="text/css">
     #yourTargetElement{
             filter:url(#filtersPicture);
      }
 </style>

我提醒大家要谨慎看待他们所展示的“简单”,因为他们是通过js和交互式表单来添加样式的(也许你心中有同样的想法),但我想这会带来与在脚本中调用DOM之前的元素相同的风险,即它找不到过滤器并抛出错误。因此,如果您想保持简单(非动态)且仍然无法正常工作,请尝试将过滤器/svg放在样式之上(即使这会导致闪烁)。


感谢你的帮助,Anthony!我根据你的回答编辑了我的问题。我基本上是将视频复制到画布中,每秒钟复制25次,将画布复制到数据URL,然后将每个图像的src设置为该URL。(然后将SVG应用于图像)。有没有更好的方法来做到这一点?即使只有一个视频,速度也非常慢,我希望能够同时处理9个视频(我知道这有点过分要求)。我的方法基本上是你推荐的吗? - nosirrahcd
自从我转向Linux / Mac后,老实说我从未使用过IE。如果可以选择,我可能仍然会用它进行测试,但是幸运的是这不是一个选项。所以你的意思是在每个图像加载时都应用svg滤镜,而你不确定这是否是导致速度变慢的原因,或者你是否想知道逐帧渲染视频的方法是否成为瓶颈?我倾向于选择后者,因为我只能想象每帧获取数据URL是内存消耗极大的。我甚至不知道Canvas有这个选项。你使用的是base64数据URL还是临时URL? - Anthony
上一个问题是基于IE/JS处理生成类似于HTML5文件API的base64数据URL的假设,它提供了完整的数据URL(非常大,仅输出它可能会减慢速度)和临时URL(这是一个短的本地URL,基本上指向缓存数据的位置)。后者更快,因为它不必预加载和读取整个文件来生成base64。不确定您正在使用的canvas技术是否支持此选项,但要深入挖掘的关键字是“对象URL”与“数据URL”(后者是base64)。 - Anthony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接