標籤:utf-8 透明 round get matrix 瀏覽器設定 cts scale profile
題記 前端需求之高斯模糊圖片
最近工作中有一個需求,客戶提交圖片,伺服器根據圖片產生內容,並將內容顯示,要求高斯模糊處理使用者的圖片並作為作品展示的背景,類似於蘋果裝置上的高斯模糊背景。使用者提交的圖片分網狀圖片地址、終端裝置上傳兩種。要求相容各大瀏覽器。
解決方案一: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(/*使用者圖片地址*/); |
在Google瀏覽器、Firefox瀏覽器、Edge 瀏覽器中展示,效果不錯,但是在IE 中不行。
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); |
這六個參數對應的矩陣就是:
座標轉換的過程如下:
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> 寬高屬性以完美展示。
至此,問題得以解決。
Web前端之高斯模糊圖片記