単純なフィルターであればそれほど問題は無いのですが,原始フィルターの掛け合わせが複雑になるに連れ,ブラウザの実装度合いにより動いたり動かなかったりと悩ましい問題が露見してきます.今回紹介するさざなみフィルターもアイディア自体はかなり前に思いついたものなのですが,クロスブラウザの問題から1ファイルで実現することが出来ず頓挫したものの一つです.
今回,ようやくその問題を解消することが出来たのでまとめて見ることとします.
さざなみフィルターは次のようなアイディアから構成されています.
- 波のようなグラデーションを作ってアニメーションさせてみよう
- そのグラデーションをフィルタの入力として任意の画像にさざなみのような効果をつけよう.
通常filterに画像を与える方法としては次の3つの方法があります.
- 図形・画像要素にfilter属性を指定し,SourceGraphicを取得する.
- svg要素にenable-background=newを指定し,filter要素からBackgroundImageを取得する.
- feImage要素を使って直接画像を取得する.
- 1の画像入力は通常単一の図形要素のみが取得可能です.これはどのブラウザでも動作します.
- 2はoperaでしか動作しません.
- 3は通常任意の画像ファイルと図形要素を参照することができるはずなのですが,firefoxでは図形要素の参照がバグで無効になっています.またoperaでは外部の画像を参照した際にアニメーションが停止してしまうといった現象が出ます.
この問題は無理に単一のfilterで凌ぐのではなく,filter処理を2つに分け,switch要素でブラウザ毎にfilterを切り替えることで解決しました.
svgではswitch要素というブラウザでのsvg対応状況に応じてグラフィックの内容を切り替えるための機構が備わっています.これは通常アニメーションに対応していない環境で静止画を適切に表示させるといった用途のために定められているのですが,現状ブラウザ毎にバラバラの処理結果となります.firefox,chrome,operaいずれもsvgアニメーションに対応しているにもかかわらず,firefoxでは「非対応」,それ以外では「対応」として扱われます.
つまり,この動作を逆手に取ればブラウザ毎に描画内容を切り替えることが可能となるのです.もちろんこの問題が解消されてしまえば元も子もないのですが.
従ってブラウザ毎に次のようにfilterを構成しました.
- chrome/opera
さざなみグラデーション画像をfeImageで取得し,それをfilterのSourceGraphicと合成した. - firefox
さざなみグラデーション画像を同一のファイルから取得したいところだが,feImageが動作しないので,SourceGraphicから直接取得できるようにソース画像の隣に配置し,一括でfilterをかける.その後feOffsetでさざなみグラデーション画像を切り抜き,後はchrome/operaと同様に処理を行う.
これで単一のファイルで画像にさざなみフィルターを掛けることが可能となりました.このようにフィルターのクロスブラウザ化は非常に面倒であり,実装には多くのテクニックが必要となる場合がある点を憶えておきましょう.
<?xml version="1.0" standalone="no"?>
<svg width="200px" height="200px" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<defs>
<!--さざなみグラデーション-->
<radialGradient id="rg" cx="0.5" cy="0.5" r="0.25" spreadMethod="repeat">
<stop offset="0" stop-color="red">
<animate id="anim" attributeName="stop-opacity"
calcMode="linear" begin="0s" dur="6s"
values="0;1;0" keiTimes="0;0.5;1" repeatCount="indefinite"/>
</stop>
<stop stop-color="red">
<animate attributeName="offset"
calcMode="linear" begin="0s" dur="6s"
values="0;0;0.5" keiTimes="0;0.5;1" repeatCount="indefinite"/>
<animate attributeName="stop-opacity"
calcMode="linear" begin="0s" dur="6s"
values="0;1;1" keiTimes="0;0.5;1" repeatCount="indefinite"/>
</stop>
<stop stop-color="red" stop-opacity="0">
<animate attributeName="offset"
calcMode="linear" begin="0s" dur="6s"
values="0;1" keiTimes="0;1" repeatCount="indefinite"/>
</stop>
<stop stop-color="red">
<animate attributeName="offset"
calcMode="linear" begin="0s" dur="6s"
values="0.5;1;1" keiTimes="0;0.5;1" repeatCount="indefinite"/>
<animate attributeName="stop-opacity"
calcMode="linear" begin="0s" dur="6s"
values="1;1;0" keiTimes="0;0.5;1" repeatCount="indefinite"/>
</stop>
<stop offset="1" stop-color="red">
<animate attributeName="stop-opacity"
calcMode="linear" begin="0s" dur="6s"
values="0;1;0" keiTimes="0;0.5;1" repeatCount="indefinite"/>
</stop>
</radialGradient>
<!--周辺を暗くするグラデーション-->
<radialGradient id="rg2" cx="0.5" cy="0.5" r="1">
<stop offset="0%" stop-opacity="0"/>
<stop offset="100%" stop-opacity="1"/>
</radialGradient>
<!--さざなみフィルター(chrome/opera用)-->
<filter id="sazanami" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feImage xlink:href="#wave" result="wave"/>
<feGaussianBlur in="wave" stdDeviation="8" result="wave2"/>
<feDiffuseLighting in="wave2" lighting-color="white"
surfaceScale="50" diffuseConstant="0.3" result="wave3">
<feDistantLight azimuth="-90" elevation="60"/>
</feDiffuseLighting>
<feDisplacementMap in="SourceGraphic" in2="wave2" scale="-10" result="wave4"/>
<feBlend in="wave3" in2="wave4" mode="screen"/>
</filter>
<!--さざなみフィルター(firefox用)-->
<filter id="sazanamiF" filterUnits="userSpaceOnUse" x="0" y="0" width="400" height="200">
<feOffset in="SourceGraphic" result="wave" dx="-200"
x="0" y="0" width="200" height="200"/>
<feGaussianBlur in="wave" stdDeviation="8" result="wave2"
x="0" y="0" width="200" height="200"/>
<feDiffuseLighting in="wave2" lighting-color="white"
surfaceScale="50" diffuseConstant="0.3" result="wave3"
x="0" y="0" width="200" height="200">
<feDistantLight azimuth="-90" elevation="60"/>
</feDiffuseLighting>
<feDisplacementMap in="SourceGraphic" in2="wave2" scale="-10" result="wave4"
x="0" y="0" width="200" height="200"/>
<feBlend in="wave3" in2="wave4" mode="screen"
x="0" y="0" width="200" height="200"/>
</filter>
<g id="wave">
<rect width="200" height="200" fill="url(#rg)"/>
<rect width="200" height="200" fill="url(#rg2)"/>
</g>
</defs>
<!--ブラウザの種類で分岐-->
<switch>
<!--SVG-animation=true…chrome/opera-->
<g filter="url(#sazanami)" requiredFeatures="http://www.w3.org/TR/SVG11/feature#SVG-animation">
<rect x="0" y ="0" width="200" height="200" fill="blue"/>
<image xlink:href="http://jsrun.it/assets/p/S/G/q/pSGqB.png" width="200" height="200"/>
</g>
<!--SVG-animation=false…firefox-->
<g filter="url(#sazanamiF)">
<rect x="0" y ="0" width="200" height="200" fill="blue"/>
<image xlink:href="http://jsrun.it/assets/p/S/G/q/pSGqB.png" width="200" height="200"/>
<use x="200" y="0" xlink:href="#wave"/>
</g>
</switch>
</svg>
0 件のコメント:
コメントを投稿