feDisplacementMap 是個很有趣的東西,可以用一張平面的圖把另一張平面的圖做扭曲變形,第一次看到時覺得非常驚豔,以往只能等美術人員畫好的圖,原來可以只靠濾鏡做到,也想不到怎麼做的,是用了什麼神奇的方法來描述整個變形,直到看到這個公式才大概理解整個做法。
參考資料:這裡
先來看一個範例
灰色是本來的圖,紅框是只有紅色在左上角的濾鏡,
當座標偏移都用綠色時,因為綠色=0 所以不會偏移, 本來的 座標 (x,y) 會變成 ( x + scale * (-0.5) , y + scale * (-0.5) ) ,
當切換到 R 時,(x,y) 理論上會是 ( x + scale * (0.5) , y + scale * (0.5) ) , 所以圖形往左上移動,然而實際上移動的座標並非 scale * 0.5 ,似忽和 R 的亮度值有關,
當切換到 A 時,這裡的設定是 7f ,也就是 0.5 , 於是圖形就回到原處沒有偏移
*實測結果 Firefox 會不正常
<svg width="300" height="360" xmlns="http://www.w3.org/2000/svg" style="background-color:#fff">
<defs>
<filter id="a122301_f1" filterUnits="userSpaceOnUse">
<feImage xlink:href="https://yiharng.github.io/pic_circle.jpg" x="0" y="0" width="280" height="280" result="a122301_t1"></feImage>
<feDisplacementMap in="a122301_t1" in2="SourceGraphic" scale="40" xChannelSelector="R" yChannelSelector="R">
<set attributeName="xChannelSelector" to="R" begin="a122301_cr.click"></set>
<set attributeName="yChannelSelector" to="R" begin="a122301_cr.click"></set>
<set attributeName="xChannelSelector" to="G" begin="a122301_cg.click"></set>
<set attributeName="yChannelSelector" to="G" begin="a122301_cg.click"></set>
<set attributeName="xChannelSelector" to="A" begin="a122301_ca.click"></set>
<set attributeName="yChannelSelector" to="A" begin="a122301_ca.click"></set>
</feDisplacementMap>
</filter>
</defs>
<rect x="0" y="0" width="150" height="150" fill="#ff00007f" style="filter: url(#a122301_f1)"></rect>
<rect x="0" y="0" width="150" height="150" stroke="#f00" fill="none"></rect>
<image href="https://yiharng.github.io/pic_circle.jpg" x="0" y="0" width="280" height="280" style="opacity:0.2"></image>
<text id="a122301_cr" x="10" y="340">Channel R</text>
<text id="a122301_cg" x="100" y="340">Channel G</text>
<text id="a122301_ca" x="190" y="340">Channel A</text>
</svg>
既然知道原理,那麼就可以弄個漸層來試試效果
原圖<br>
<img src="https://yiharng.github.io/cat280.jpg" width="280"><br>
濾鏡<br>
<svg width="300" height="280" xmlns="http://www.w3.org/2000/svg" style="background-color:#fff">
<defs>
<filter id="a122301_f2" filterUnits="userSpaceOnUse">
<feImage xlink:href="https://yiharng.github.io/cat280.jpg" result="a122301_t2" x="-50" y="-20"></feImage>
<feDisplacementMap in="a122301_t2" in2="SourceGraphic" scale="40" xChannelSelector="R" yChannelSelector="R" result="a122301_d2"></feDisplacementMap>
</filter>
<radialGradient id="a122301_rad1">
<stop offset="0%" stop-color="#f00"></stop>
<stop offset="100%" stop-color="#000"></stop>
</radialGradient>
</defs>
<ellipse cx="150" cy="140" rx="110" ry="110" fill="url(#a122301_rad1)" style="filter: url(#a122301_f2)">
</ellipse>
</svg>
原圖
濾鏡
加上一些計算,可以做出水波效果
<svg width="280" height="210">
<rect x="0" y="0" width="280" height="210" fill="url(#a122303_rad1)"></rect>
</svg>
<svg width="280" height="210" xmlns="http://www.w3.org/2000/svg" style="background-color:#fff">
<defs>
<filter id="a122303_f1" filterUnits="userSpaceOnUse">
<feImage xlink:href="https://yiharng.github.io/bird.jpg" result="a122303_t1" x="-20" y="-20" width="330" height="250"></feImage>
<feDisplacementMap in="a122303_t1" in2="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="R" result="a122303_d1"></feDisplacementMap>
</filter>
<radialGradient id="a122303_rad1" cx="70%" cy="60%" r="100%">
<stop offset="0.0" stop-color="#000">
<animate attributeName="offset" to="0.2" dur="1s" repeatCount="indefinite"></animate>
</stop>
<stop offset="0.1" stop-color="#f00">
<animate attributeName="offset" to="0.3" dur="1s" repeatCount="indefinite"></animate>
</stop>
</radialGradient>
</defs>
<rect x="0" y="0" width="280" height="210" fill="url(#a122303_rad1)" filter="url(#a122303_f1)"></rect>
</svg>
<script>
$(()=>
{
let hh="";
let i,j="f";
let k=0.03;
for (i=0;i<1;i+=k)
{
if (j=="0") j="f"; else j="0"
hh+='<stop offset="'+(i.toFixed(2))+'" stop-color="#'+(j)+'00">'
+'<animate attributeName="offset" to="'+((i+k*2).toFixed(2))
+'" dur="1s" repeatCount="indefinite"></animate></stop>';
}
$("#a122303_rad1").html(hh);
})
</script>
修改一下波的行徑方向,可以變成倒影,而這個範例卻又發現了 chrome 的 bug
Firefox 是本來就看不到效果,但是倒影的圖片翻轉是正常的
Chrome 很奇怪的,圖片翻轉居然沒效......花了一些時間測試,在 svg 外層包一個 div 並旋轉 0.001 度好像可以解決這個 bug , 但是在某些情況下又不行......
<div style="transform:rotate(0.001deg)">
<svg>
.
.
.
</svg>
</div>
Safari 正常
<div style="position:absolute;width:290px;height:200px;background:url(https://yiharng.github.io/bird280.jpg)">
</div>
<div style="height:190px;"></div>
<div style="transform:rotate(0.001deg)">
<svg width="290" height="200" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="a122306_f1" filterUnits="userSpaceOnUse">
<feTurbulence type="turbulence" baseFrequency="0.01 0.1" numOctaves="2" result="a122306_tt">
</feTurbulence>
<feImage href="https://yiharng.github.io/bird280.jpg" x="-20" y="-30" width="320" height="240" preserveAspectRatio="none" result="a122306_bird"></feImage>
<feDisplacementMap in="a122306_bird" in2="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="R" result="a122306_d1">
</feDisplacementMap>
<feDisplacementMap in="a122306_d1" in2="a122306_tt" scale="20" xChannelSelector="R" yChannelSelector="G">
</feDisplacementMap>
</filter>
<linearGradient id="a122306_linear1" x1="0" y1="0" x2="0" y2="1" spreadMethod="pad">
<stop offset="0%" stop-color="#000"></stop>
<stop offset="10%" stop-color="#f00"></stop>
<stop offset="20%" stop-color="#000"></stop>
</linearGradient>
</defs>
<rect x="0" y="0" width="300" height="300" transform="scale(1,-1) translate(0,-200)" fill="url(#a122306_linear1)" filter="url(#a122306_f1)"></rect>
</svg>
</div>
<script>
$(()=>
{
let hh="";
let i,j="f";
let k=0.03;
for (i=0;i<1;i+=k)
{
if (j=="0") j="f"; else j="0"
hh+='<stop offset="'+(i.toFixed(2))+'" stop-color="#'+(j)+'00">'
+'<animate attributeName="offset" to="'+((i-k*2).toFixed(2))
+'" dur="2s" repeatCount="indefinite"></animate></stop>';
}
$("#a122306_linear1").html(hh);
})
</script>
如果看不到水波和倒影,那麼可能你用的瀏覽器是 Firefox ,自從開始玩 SVG 後就常常發現這些瀏覽器有不少東西不支援或者支援一半。
Firefox 並不是不支援 feDisplacementMap 而是無法以及時畫的圖給濾鏡使用,以下範例用的是 feTurbulence 產生的圖,在所有瀏覽器都支援
<svg width="300" height="400" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="a122305_f1" filterUnits="userSpaceOnUse">
<feTurbulence type="turbulence" baseFrequency="0.02 0.1" numOctaves="2" result="a122305_tt">
<animate attributeName="baseFrequency" values="0.02 0.1;0.022 0.13;0.02 0.1" dur="15s" repeatCount="indefinite"></animate>
</feTurbulence>
<feDisplacementMap in2="a122305_tt" in="SourceGraphic" scale="40" xChannelSelector="R" yChannelSelector="G">
</feDisplacementMap>
</filter>
</defs>
<image href="https://yiharng.github.io/bird280.jpg" x="-20" y="0" width="320" height="200" preserveAspectRatio="none" style="transform:translate(0,430px) scale(1,-1) ;filter: url(#a122305_f1)"></image>
<image href="https://yiharng.github.io/bird280.jpg" x="-20" y="0" width="320" height="240" preserveAspectRatio="none"></image>
</svg>
沒有留言:
張貼留言