Web前端之高斯模糊圖片記


題記 前端需求之高斯模糊圖片

   最近工作中有一個需求,客戶提交圖片,服務器根據圖片生成內容,並將內容顯示,要求高斯模糊處理用戶的圖片並作為作品展示的背景,類似於蘋果設備上的高斯模糊背景。用戶提交的圖片分網絡圖片地址、終端設備上傳兩種。要求兼容各大瀏覽器。

解決方案一:CSS3濾鏡

   在CSS3 中規定了一個新的圖形特效:filter ,可以對元素進行模糊、銳化或者元素變色。 filter 目的是用來調整圖片、背景和邊界的渲染。

   在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 的介紹 

其中blur() 正是對元素進行高斯模糊,順便添加了brightness() 函數增加前景背景明暗對比度。

  -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 中不行。

CSS3 filter 的瀏覽器兼容列表如下:

CSS3 filter瀏覽器支持情況一覽表

IE的CSS 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 中的高斯模糊實現:

filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius=10, MakeShadow=false); /* IE6~IE9 */

   到這里發現並沒有萬事大吉,大家會發現IE filter 代碼中的注釋是IE6~IE9。IE10、IE11是不支持CSS 中 filter 的語法的,想來可能是Microsoft 想在IE 10 后支持CSS3 中的filter 卻發現與之前的實現有沖突,然后不得不舍棄,最終也沒拿出方案吧。所以只得尋找新的方案。

解決方案二:HTML5 之 canvas

   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

   CSS3 提供了很多變換,其中 transform 就可以對元素進行旋轉(rotate)、位移(translate)、縮放(scale)、傾斜(skew)等2D3D轉換(MDN講解:transform),當然,這些轉換斗是以 matrix(矩陣) 為基礎方法在坐標系統中對可視化模型的坐標空間進行操作。既然如此,若有合適的矩陣是否能達到元素“高斯模糊的效果”呢?於是對 matrix(矩陣) 進行探究。

   matrix(矩陣) 主要原理是對元素點集合的各個點坐標進行線性代數轉換,以達到元素變形的目的。CSS3 transform 的2D轉換 matrix() 方法寫法如下:

transform: matrix(a,b,c,d,e,f);

這六個參數對應的矩陣就是:

矩陣參數與矩陣對應關系 張鑫旭-鑫空間-鑫生活

坐標轉換的過程如下:

CSS3中矩陣位置計算公式 張鑫旭-鑫空間-鑫生活

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高斯模糊

   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> 寬高屬性以完美展示。

至此,問題得以解決。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM