最近工做中有一个需求,客户提交图片,服务器根据图片生成内容,并将内容显示,要求高斯模糊处理用户的图片并做为做品展现的背景,相似于苹果设备上的高斯模糊背景。用户提交的图片分网络图片地址、终端设备上传两种。要求兼容各大浏览器。 css
在CSS3 中规定了一个新的图形特效:filter ,能够对元素进行模糊、锐化或者元素变色。 filter 目的是用来调整图片、背景和边界的渲染。 html
在CSS3 中已经实现了filter 的一些预约义函数,MDN 中介绍以下: 前端
filter: url("filters.svg#filter-id"); filter: blur(5px); filter: brightness(0.4); filter: contrast(200%); filter: drop-shadow(16px 16px 20px blue); filter: grayscale(50%); filter: hue-rotate(90deg); filter: invert(75%); filter: opacity(25%); filter: saturate(30%); filter: sepia(60%); /* Apply multiple filters */ filter: contrast(175%) brightness(3%); /* Global values */ filter: inherit; filter: initial; filter: unset;
详见:MDN中对 filter 的介绍 html5
其中blur() 正是对元素进行高斯模糊,顺便添加了brightness() 函数增长前景背景明暗对比度。css3
-webkit-filter: blur(10px) brightness(.5); /* Chrome, Opera */ -moz-filter: blur(10px) brightness(.5); -ms-filter: blur(10px) brightness(.5); filter: blur(10px) brightness(.5);background-image: url(/*用户图片地址*/);
在谷歌浏览器、火狐浏览器、Edge 浏览器中展现,效果不错,可是在IE 中不行。web
CSS3 filter 的浏览器兼容列表以下:算法
IE 没有实现CSS3 的filter ,由于它们原本就有本身的filter 滤镜实现。IE 中的filter 实现了和CSS3 中 filter 相似的方法,可是filter 方法的调用却与CSS3 中的filter 方法截然不同。Microsoft 早在 IE 4.0 中就开始了filter 的支持,很明显CSS3 中的filter 借鉴了IE 的思想却用了比IE 更切合的方式实现了这些方法,为IE 点赞。关于IE 中的CSS-filter 的知识详见关于IE中CSS-filter滤镜小知识介绍。因而添加上IE 中的高斯模糊实现:canvas
filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius=10, MakeShadow=false); /* IE6~IE9 */
到这里发现并无万事大吉,你们会发现IE filter 代码中的注释是IE6~IE9。IE十、IE11是不支持CSS 中 filter 的语法的,想来多是Microsoft 想在IE 10 后支持CSS3 中的filter 却发现与以前的实现有冲突,而后不得不舍弃,最终也没拿出方案吧。因此只得寻找新的方案。跨域
canvas 中有一个getImageData() 方法,能够获取图片上的像素点信息,还有一个方法putImageData() 能够将图像的像素点信息修改后写入到canvas 上,canvas 也提供了方法toDataURL() 将图像信息导出成路径供其它用处(譬如做为其余元素的背景),将canvas 画图做为其余元素的背景可查看使用canvas 绘制背景图-Jerry Qu 的介绍。咱们获取图片的信息后对图片信息进行转化后写回canvas ,就能获得想要的效果。固然,使用不一样的算法会获得不一样的结果,canvas 生成马赛克图片 介绍了不一样的图片处理插件,有兴趣的能够在上面详细了解。咱们要使用的是高斯模糊的插件,对高斯模糊算法,阮老师的一篇译文——高斯模糊算法 介绍的很不错,主要涉及正态分布。不过咱们现成的实现,在网上找到这个JS插件——StackBlur.js,Demo地址:http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html 。浏览器
该插件能够实现高斯模糊效果,主要使用里面的方法
function stackBlurImage( imageID, canvasID, radius, blurAlphaChannel )
其中, imageID 是html 页面中要高斯模糊的原图片标签ID,canvasID 是canvas 画图的ID,radius 是要高斯模糊的半径,blurAlphaChannel 涉及半透明(未详细研究)。
因而敲定解决方案:在html 页面中插入隐藏标签<img />和隐藏的标签<canvas></canvas>,使用插件中方法设置做品展现的背景。方案敲定开始编码,写完后上传了一张图片,服务器保存后将图片地址保存后返回图片地址,前端处理。测试,经过,完美兼容各大浏览器。
问题出如今项目改造,使用独立的图片服务器后,图片服务器和Web 服务器在不一样的域下,因此在运行 stackBlurImage() 是浏览器报出了以下错误:
Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. |
形成这个问题的缘由是图片跨域,在canvas 修改图片信息的时候,像其余数据同样,图片信息的访问也有域的限制,跨域图片的详细介绍可参见MDN 上的讲解:CORS enable image 。跨域限制是html 规范上要求的,各个浏览器是否实现虽有差异,但至少Chrome 上是不行的(存在其余浏览器并未对canvas 中的这一点作限制),看到一些博客介绍修改浏览器设置也能够突破这个限制,但显然这不是产品级的解决方案。
存在这个问题的不只仅是由于文件服务器独立出来,若是用户提交的是第三方网站的图片地址,同样存在这个问题,只是当时未发现而已。因而考虑使用JS 将图片下载到本地再使用,但是JS 也有跨域的限制,仍然不可行。
CSS3 提供了不少变换,其中 transform 就能够对元素进行旋转(rotate)、位移(translate)、缩放(scale)、倾斜(skew)等2D3D转换(MDN讲解:transform),固然,这些转换斗是以 matrix(矩阵) 为基础方法在坐标系统中对可视化模型的坐标空间进行操做。既然如此,如有合适的矩阵是否能达到元素“高斯模糊的效果”呢?因而对 matrix(矩阵) 进行探究。
matrix(矩阵) 主要原理是对元素点集合的各个点坐标进行线性代数转换,以达到元素变形的目的。CSS3 transform 的2D转换 matrix() 方法写法以下:
transform: matrix(a,b,c,d,e,f); |
这六个参数对应的矩阵就是:
坐标转换的过程以下:
3*3矩阵每一行的第1个值与后面1*3的第1个值相乘,第2个值与第2个相乘,第3个与第3个,而后相加。2D转换使用了3*3矩阵,3D 转换多了一个Z轴,使用的是4*4矩阵。两种转换的本质是同样的,只是复杂度不一样。上面这一段介绍来自张旭鑫的博客,理解CSS3 transform中的Matrix(矩阵) 。他的另外一篇博客对 3D 转换进行了详细而又个性的介绍,好吧,CSS3 3D transform变换,不过如此 。感谢大神们的分享。
也就是说 transform 转换的实质是对坐标点的变化,并不能对图片的像素点数据进行操做,能进行各类变形,却改变不了元素的本质,高斯模糊改变了图片的像素点,transform 并不能解决咱们的问题。
SVG 是用XML格式定义在Web 平台上的矢量图,它是一个开放标准,它将图像信息以XML 文本形式进行保存和传输,SVG 里也提供了滤镜来该表元素的显示,其中包括高斯模糊。咱们先来看看SVG 的浏览器兼容性:
回顾咱们使用的过的解决办法,方案二有同源策略的限制,方案三不可用,方案一兼容了除IE10+外的主流浏览器,若是咱们使用SVG滤镜将IE10+ 的坑填上,便获得一个完美的解决方案。根据 SVG 的教程 中的介绍,SVG 滤镜主要使用了<defs> 和<filter> 标记。保存一个名为 blur.svg 的SVG 文件,文件内容以下:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" baseProfile="full"> <defs> <filter id="blur"> <feGaussianBlur stdDeviation="10" /> </filter> </defs> <image xlink:href="mm1.jpg" x="0" y="0" height="191" width="265" filter="url(#blur)" /> </svg> |
红色部位的代码便提供了一个高斯模糊滤镜(模糊半径为10),<image> 标记提供了要模糊的图片,属性 xlink:href 是图片的地址,属性 filter 根据ID 应用了红色代码定义的滤镜,而后SVG 做为背景图片载入:
.blur {
background-image: url(blur.svg);
}
这样就达到了咱们的目的——高斯模糊图片,但仍然存在一个问题,以上的SVG 文件单独于html 页面,须要额外的维护,要命的是图片的地址很难改变,因而咱们把以上 SVG 标签的内容做为内联元素放在了 html 页面中,并和做品展现的容器同级。而后将他们的父元素 position 设为 relative ,做品容器背景色设为透明。对 <svg> 标签应用如下样式做为做品容器的背景,
width: 120%;
height: 120%;
position:absolute;
top:-10%;
left:-10%;
最后是使用 JS 调节 <svg> 标签中的 <iamge> 宽高属性以完美展现。
至此,问题得以解决。