Lopan.jp

SVGでやることのまとめ。
SVGのフィルター効果。

SVGには、要素にフィルター効果を与えるための特別な要素が用意されていて、CSSのfilterプロパティよりも豊富なフィルターが用意されています。けれど、パッと見ごちゃっとしてて、手順とか手続きがなんだかややこやしそう…。そんな「SVGのフィルター効果」について、わかってきたところまで、個人的なまとめです:)

SVGのフィルター効果のための要素

SVGはXMLに基づくマークアップ言語なので、フィルターを適用する方法も、XMLのタグ要素として用意されていて、フィルター効果のための要素は、下記ページの「Filter primitive elements」と「Light source elements」という項目にまとまっています。

SVG element reference - SVG: Scalable Vector Graphics | MDN

すべてfeという接頭辞プレフィックスfilter effectの略)が付いていて、分かりやすいですね:D
SVGのフィルター効果は、これらフィルター要素を使って作ってゆくことになります。

フィルター要素の種類

SVGに用意されているフィルター要素は、ぜんぶで25個!
けど、中にはふたつでひとつみたいな要素もあるので、フィルターの種類的にはだいたい17種類くらい。
大きく、以下の4つのタイプに分けられるかなと思います:)

重なりを制御するフィルター
  • <feComposite/>
    重ねたレイヤーの合成方法を指定する
  • <feMerge/><feMergeNode/>
    レイヤーを順に重ねる
レイヤーを追加するフィルター
  • <feFlood/>
    矩形範囲を塗りつぶしたレイヤーを追加する
  • <feImage/>
    画像を配置したレイヤーを追加する
  • <feTile/>
    矩形範囲に画像を敷き詰めたレイヤーを追加する
状態を操作するフィルター
  • <feColorMatrix/>
    要素の色をRGBチャンネルごとに調整する
  • <feComponentTransfer/><feFuncR/>, <feFuncB/>, <feFuncG/>, <feFuncA/>
    要素の明るさ、コントラスト、カラーバランス、しきい値を調整する
  • <feConvolveMatrix/>
    エンボス、エッジ検出、ぼかし、シャープネスなどを調整する
  • <feBlend/>
    重なるレイヤーの色の混ざり方を指定する
  • <feOffset/>
    要素の位置をずらす
  • <feGaussianBlur/>
    要素をぼかす
  • <feDropShadow/>
    要素の不透明部分に影を落とす
  • <feMorphology/>
    画素を侵食(暗い画素を広げる)・膨張(明るい画素を広げる)させる
  • <feTurbulence/>
    ノイズ、揺らぎによるごく自然な紋様を生成する(パーリンノイズによる描画)
  • <feDisplacementMap/>
    明暗によって、要素が重なっている部分の画素をずらす
照明効果フィルター
  • <feDiffuseLighting/><fePointLight/>, <feDistantLight/>, <feSpotLight/>
    照明効果をシミュレートする(不透明な箇所が盛り上がっているものとして扱う)
  • <feSpecularLighting/><fePointLight/>, <feDistantLight/>, <feSpotLight/>
    鏡面反射効果をシミュレートする(透明な箇所が盛り上がっているものとして扱う)

状態を操作するフィルター」と「照明効果フィルター」が、具体的にフィルター効果を適用してゆくもので、「重ね方を制御するフィルター」と「レイヤーを追加するフィルター」は、フィルター効果を補助する役割、という印象です。
これらのフィルター要素を組み合わせることで、Photoshopのフィルターとかレイヤー効果とか色調補正みたいな効果を、ブラウザ上で再現することができます:)

Web上で、Photoshopのフィルターやレイヤー効果、色調補正みたいなことができるようになる

ページトップへ戻る

SVGのフィルターの仕方

まずはSVGでフィルターを適用する仕組み方法をおさらい。ここでは、SVGフィルターをHTML要素に適用するための方法を見てゆきます。
HTML内でSVGフィルターを使う手順は以下の通り。シンプル:D

  1. filter要素で、フィルター効果を定義して、名前を付ける
  2. その名前を、CSSのfilterプロパティで指定すれば、フィルターが適用される

まず、filter要素でフィルターを定義して、定義したフィルターを、さらにCSSのfilterプロパティを使ってHTML要素に適用する、という流れになります。
詳しく見てゆきます!

フィルター効果を定義する

filter要素<filter>〜</filter>の中に、前述のフィルター要素feで始まる要素)を書いていくことで、フィルター効果を定義できる仕組みになっています。フィルターの名前は、filter要素のid属性で指定します。

※filter要素は、HTMLではなくSVGの要素なので、svg要素で括るのも忘れずに。

例えば、<feGaussianBlur/>というフィルター要素を使って以下のように書けば、「ぼかしフィルター」の出来上がり。ここでは分かりやすく、フィルター要素と同じfeGaussianBlurという名前を付けました。

ぼかしのフィルターの定義
<svg>
	<filter id="feGaussianBlur">
		<feGaussianBlur stdDeviation="8 0"/>
	</filter>
</svg>

これを、HTMLの</body>の直前あたりに書いておきます。

フィルターを適用する

フィルターを適用するには、CSSのfilterプロパティを使います。値にはurl()関数を使って、括弧の中に、さっき作ったぼかしフィルターの名前に#を付けて指定します。ここでは、フィルターを適用する要素にfe-blurというclass名を付けています。

CSS
.fe-blur {
	filter: url(#feGaussianBlur);
}
HTML
<figure class="fe-blur">
	<img src="pan.svg" alt="">
</figure>

フィルターが適用されて、パンちゃんがぼけましたXD
けれど画像の下に、なにやら大きな余白がありますね…。これは、フィルターを定義しているsvg要素が表示されちゃってる状態なのですけど、フィルターを定義しているだけなので表示させたくないですね…:(
表示されないように、display: noneしておくことにします!

<svg style="display:none">
	<filter id="feGaussianBlur">
		︙

大きな余白がなくなりましたー。
…けどあれれ、Firefoxではパンちゃんまで消えちゃってますね…。Firefoxでは、定義しているフィルターをdisplay: noneすると、適用している要素まで消えちゃうみたいです…X((2020.9現在)

Chrome/Safari/Firefoxでの表示

display: noneじゃない方法でsvg要素だけ表示されないように、svg-filterというclass名を付けて、以下のように念入りにスタイルを指定しておくことにします。

.svg-filter {
	position: absolute;
	overflow: hidden;
	width: 0;
	height: 0;
	visibility: hidden;
}

大きな余白がなくなったまま、Firefoxでもブレたパンちゃんが表示されるようになりました;D

ページトップへ戻る

複数のフィルター要素を組み合わせる

フィルター要素をいろいろ組み合わせることで、もっと凄いフィルターを作ることもできます。
複数のフィルター要素を組み合わせる場合は、フィルター要素ごとの手続きがあったりして、こんがらがりがちなので、以下で、影を落とすドロップシャドウフィルターの作り方を通して、慎重に見てゆくことにします:)

複数のフィルター要素を使ってドロップシャドウを再現する

下サンプルでは、それぞれ違うフィルター要素を使って、青白い影を落としています。
けれど、Safariで見てみると左側のパンちゃんには影が落ちてませんね…。

Safari/Chromeでの表示

左側のパンちゃんは、<feDropShadow/>というフィルター要素を使ってるのですけれど、Safariでは、<feDropShadow/>HTML要素に適用することができないみたいなんですね…:((2020.9現在)
そんなSafariのために、右側のパンちゃんでは、<feDropShadow/>以外のフィルター要素を使って、青白い影を落としています:)

右側のパンちゃんに適用しているフィルター要素たちは以下の通り。

<svg>
	<filter id="filter">
		<feColorMatrix in="SourceAlpha" type="matrix" values="1 0 0 0 0 0 1 0 .2 0 0 0 1 1 0 0 0 0 .2 0"/>
		<feGaussianBlur stdDeviation="8"/>
		<feOffset dx="-8" dy="8"/>
		<feComposite in="SourceGraphic" operator="over"/>
	</filter>
</svg>

なにやら複雑ですね…、順番に見てゆきます:D

色と透明度を調整

<feColorMatrix in="SourceAlpha" type="matrix" values="1 0 0 0 0 0 1 0 .2 0 0 0 1 1 0 0 0 0 .2 0"/>

<feColorMatrix/>というフィルター要素で、「SourceAlpha(=元のグラフィックのアルファチャンネル」に対して色変換をおこない、適用されたレイヤー生成します。ここでは、黒色を青っぽくなるように調整して、透明度を0.2に調整しています(※アルファチャンネルは不透明部分を黒色で表示します)

上サンプルの、ひとつめはin属性がないもの、ふたつめはin属性SourceAlphaを指定しているもの。
ひとつめは、パンちゃんの画像自体にフィルターを適用しているけれど、ふたつめは、画像のアルファチャンネルのみがフィルターの対象になってるのが分かりますね:o

  • filter要素最初のフィルター要素は、元のグラフィックに適用される(ここで元のグラフィックとは、フィルターを適用しているfigure要素のことを指す)
  • in属性には、SourceGraphicSourceAlphaという特別な値が用意されていて、同じfilter要素内でいつでも「元のグラフィック」と「元のグラフィックのアルファチャンネル」を呼び出すことができる。
  • ※表示上、色を変えたレイヤーしか表示されてないが、内部的に「元の要素レイヤー」と「色を変えたレイヤー」どちらも保持された状態として、ある。

以降のフィルター要素は、この「生成されたレイヤー」に対して適用されることになります。
SVGでフィルターを適用するときには、Photoshopとかと同じような、レイヤー構造を意識すると分かり良いです:)

要素をぼかす

<feGaussianBlur stdDeviation="8"/>

レイヤーをぼかすのは、さっきも使った<feGaussianBlur/>フィルター。ここでは8(8px)ぼかします。

  • ※フィルター要素を続けて書くと、そのフィルター効果は、直前に生成されたレイヤーが対象となる<feGaussianBlur/>は、色を変えたレイヤーに適用されている)
  • ※ただし、result属性明示的に付けた名前in属性の値とした場合は、その名前のレイヤーが対象となる(詳しくは後述)。

要素を左下へズラす

<feOffset dx="-8" dy="8"/>

<feOffset/>というフィルター要素で、ぼかしたレイヤーを左下へ移動させます。

重なる要素を「over」で合成

最後に、ここまで作った「影のレイヤーと元のグラフィックを合成する」ということをします。

<feComposite in="SourceGraphic" operator="over"/>

<feComposite/>というフィルター要素は、in属性に指定したレイヤーと、in2属性に指定したレイヤーを、operator属性に指定した方法(全7種)合成します。
ここでは「in2のレイヤーの上にinのレイヤーを重ねる」という効果のoverという方法を使って、影のレイヤーの上に元のグラフィックを重ねています。

  • ※in属性にSourceGraphicを指定して、元のグラフィックを呼び出している。
  • in属性に値を指定することで、直前までのレイヤーin2属性の値として使われる。(in2属性に値を指定すれば、直前までのレイヤーはin属性の値として使われる。)
  • ※合成方法はover/in/out/atop/xor/lighter/arithmeticの7種類。

元のグラフィックに順々にフィルターを適用してゆき、最終結果と元のグラフィックを合成している、というわけです:)

result属性について

フィルター要素にresult属性で値を指定すれば、その時点でのレイヤーの状態を保持しておくことができます。
フィルター要素を続けて書けば、直前に生成されたレイヤーがそのフィルター効果の対象となるので、前述のサンプルの場合は特にresult属性は必要ありませんでした。
けれども、例えば前述のサンプルを、以下のように書いても、結果は同じことになります:)

<svg>
	<filter id="filter">
		<feColorMatrix in="SourceAlpha" type="matrix" values="1 0 0 0 0 0 1 0 .2 0 0 0 1 1 0 0 0 0 .2 0" result="color"/>
		<feGaussianBlur in="color" stdDeviation="8" result="colorBlur"/>
		<feOffset in="colorBlur" dx="-8" dy="8" result="colorBlurOffset"/>
		<feComposite in="SourceGraphic" in2="colorBlurOffset" operator="over"/>
	</filter>
</svg>

色と透明度を変えた時点ではcolorという名前にして、次のフィルター要素のin属性でその名前を使っています。それ以降も同じように、colorBlurcolorBlurOffsetとそれぞれ名付けました。

上サンプルの右側は、<feComposite/>のin2属性の値をcolorBlurとしているため、左下へ動かすの状態が、影のレイヤーとして合成されています:o

result属性は、その時の状態を保持しておける

result属性の有意義な使い方

result属性は、<feMerge/><feMergeNode/>というフィルター要素と一緒に使うと、本領発揮するかなと思います。
例えば、下サンプルみたいな使い方で重宝します。

<feColorMatrix in="SourceAlpha" type="matrix" values="1 0 0 0 0 0 1 0 .2 0 0 0 1 1 0 0 0 0 .2 0"/>
<feGaussianBlur stdDeviation="4"/>
<feOffset dx="-4" dy="4" result="shadow"/>

<feColorMatrix/><feOffset/>までは前述のサンプルと同じ、青白い影を作っていて、一旦それをshadowという名前で保持しておきます。

<feComponentTransfer in="SourceGraphic" result="afterimage">
	<feFuncA type="table" tableValues="0 .2"/>
</feComponentTransfer>

続いて、<feComponentTransfer>というフィルター要素で、新たに呼び出した元のグラフィックの透明度を0.2にして、afterimageという名前で保持。

<feOffset in="afterimage" dx="8" dy="-16" result="afterimage1"/>
<feOffset in="afterimage" dx="24" dy="-8" result="afterimage2"/>
<feOffset in="afterimage" dx="-16" dy="8" result="afterimage3"/>
<feOffset in="afterimage" dx="16" dy="16" result="afterimage4"/>

<feOffset/>フィルターで、「afterimageを呼び出して位置をずらす」というのを4回、バラバラな配置になるように繰り返して、それぞれ別々の名前を付けておきます。

<feMerge>
	<feMergeNode in="shadow"/>
	<feMergeNode in="afterimage1"/>
	<feMergeNode in="afterimage2"/>
	<feMergeNode in="afterimage3"/>
	<feMergeNode in="afterimage4"/>
	<feMergeNode in="SourceGraphic"/>
</feMerge>	

最後は<feMerge/>フィルターを使って、保持しておいたレイヤーを「影→残像1→残像2→残像3→残像4→元のグラフィック」の順に重ねて出来上がり!

<feMerge/>は、保持しておいたレイヤーを並び替えながら合成する

<feMerge/>の中に、<feMergeNode/>in属性で、保持しておいたレイヤーを呼び出して、レイヤーを順に上に重ねてゆきます(1行めが最背面レイヤー、最後の行が最前面レイヤーになる)

ページトップへ戻る

あとがき

できれば、フィルター効果を「filter.svg」みたいな個別のSVGファイルとして用意することができたら便利なんですけれど、対応してるのはFirefoxだけの様子…(2020.9現在、Chromeで適用されない効果があったり、Safariでは未対応):(

今のところ、HTML内にSVGでfilter要素を書いておかなければならない分、CSSフィルターよりも使い勝手は良くないけれど、その分、多彩な表現ができたり、フィルター効果を自由に作り込むことができるのが、SVGフィルター良いところ;)。CSSフィルターにはできない効果もたくさんあるので、CSSフィルターとうまく併用していきたいですね!
個人的には、Photoshopのチャンネルミキサーと同じことができる<feColorMatrix/>フィルターとか、要素を波模様に歪めたりできる<feTurbulence/>フィルターとかを、Webサイトの演出として使うのが楽しそう。

以下のサイトで、SVGフィルターを使ったいろいろなアイデアや、デモが紹介されてます。JavaScriptと併用すれば、もっとユニークな演出が作れちゃいますねー。

SVGのフィルター要素の、ブラウザごと対応状況(2020.9現在)を、下記ページにまとめましたので、ご参考まで。

SVGのフィルター効果の対応状況 - Lopan.jp

以上、「SVGのフィルター効果について」でした!
最後まで読んでいただきありがとうございました;D


参考文献。

Comment & Pingback

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください