POPSブログ

SVG.js プラグインsvg.filter.jsのFilter処理

392

  Category:  javascript2016/09/19 pops 

SVG.js プラグインsvg.filter.js(v2.0.2)にバージョンアップに伴い各種Filter処理がやり易くなりました。マニュアルも充実されています。但しまだ未対応の部分も有りますが工夫しながらテストします。

 

SVG.js プラグインsvg.filter.jsのFilter処理テスト

各種メソッド、クラスなど明らかになりましたので、および各種filterの表示とDropShadowの追加などのテストを行ってみます。
DropShadowのみの場合は「前ページ」を参照ください。


ご注意、Chrome.Firefox.IE11(一部機能しません)などSVGに対応したブラウザでご覧ください。Chromeを推奨します。

 

簡単な説明


新しいsvg.filter.js(v2.0.2)を使用する


SVG.js プラグインsvg.filter.jsは svg.filter.js で取得ください。同ページにマニュアルもあります。
当方の都合で(過去記事全ての動作確認が未完了のため)、このページでは、svg.filter.v2.jsと名前を変えて使用しています。


マニュアルも充実してきましたので、DropShadowも形態の違うもの、また新しいFilter処理も作ってみました。

SVG.js実行上、基礎的なSVGの知識が必要ですので、ぜひ下記サイトの解説記事とも参考にして下さい。

【参考】www.h2.dion.ne.jpの記事: svg要素の基本的な使い方まとめ、画像効果

【参考】triple-underscoreの記事: (SVG)1.1 第2版 /15 フィルタ効果


各種Filterなどの処理サンプル


従来の画像Filterの動作確認

従来の hue colorize posterize 等の画像処理Filterが有りましたが、新しいバージョンでも全て正常動作を確認しました。
【参照】当方の記事: SVG.js filterとDropShadow


 

DEMO 各種Filterのデモ


pakuri

Chromeでの閲覧推奨

DEMO、svg.filter.jsの Effect Classes などの説明と実際表示デモ

DEMO-042




パクリで綴るSVG

SVGは出来上がった以前のSVGを再利用し易くなっていますので、その利点を活用して他サイトのサンプルなどをパクリ、一旦SVG.js上で動作表示させることが大切です。その内に理解できるようになります。


色々な効果のFilterも簡単に作れるかも知れない

従来の画像処理Filterに無い、色々な効果のFilterも比較的簡単に作れるます。なるべく、SVG.js形式に変換して記述していますが、出来ないものはsvg()形式で作りました。但し、Firefoxは厳格ですから扱い難いです。なるべくFirefoxで処理出来る様に一部修正しました。


1. プラグインsvg.filter.js全てのフィルタークラスが完全では有りませんので、修正など必要です。
2. 既存のHTML形式で記述されたSVGをsvg()形式で処理します。とりあえず簡単に動作できます。
3. svg.filter.jsのフィルタークラスの記述に書き換えます。(まだ、必ず完全ではない)。
4. 実行上SVGに関する知識が必要になります。
5. 確認のため、DEMOサンプルの大半にドラッグを設定しています。


全てのフィルタークラスが完全に網羅されている訳では有りませんので、順次時間経過と共に追加されると期待しています。
骨折しない程度アタマをひねればナントカなりそうです。


svg()利用の処理

プラグインsvg.filter.js全てのフィルタークラスが完全では有りませんので、簡単にSVG.js形式に記述出来ないものも多数有ります。一旦、完動するSVGをその辺からパクリ、svg()形式で動作させるのが簡単な順序です。


defsに収容して動作させる

● defsに収容して動作させる方法です。
defsにあるフィルター類はurl()で再利用できますので便利です。ほとんどのSVGはHTMLで書かれurl()で呼び出していますので、余り問題は生じない。但し、ドラッグ処理、陰影処理などが機能しない場合が発生します。(グループ化すれば少しは解決します)

単なる収容場所のためか「SVG-HTML記述部分」が曖昧でも、Firefoxで処理してくれる場合が多い。
もし、filterタグに必要な属性があれば事前に下記の様に設定しておく。
どちらかと言えばこの方式が一応安心です。


//defs収容方式
var defs=draw.defs()
defs.svg('<filter id="papipupepo" color-interpolation-filters="sRGB"> SVG-HTML記述部分 </filter>')

//呼び出し
.attr({'filter':'url(#papipupepo)'})

1番、ネットパクリのgrayscale filterです。陰影は付けれません。登録済みなら他でも利用可能です。
2番、grayscaleに陰影を付けると二重になり前のfilterがなくなりますのでグループ化してそれぞれに処理します。
3番、陰影つきBevel filterです。lighting操作の為画像は薄くなります。
このように簡単にfilter処理出来ます。ブラウザ毎に表示を確認して下さい。

1番、grayscale filter / 2番、grayscaleと陰影 / 3番、陰影つきBevel filter


サンプルJS


	//対象エレメントID
	var draw=SVG('svg-test-filter01').size(160,160)

	var text=draw.text('grayscale filter').font({leading:0.8})

	var defs=draw.defs()
	defs.svg('<filter id="inner-shadow01" x0="-50%" y0="-50%" width="200%" height="200%"><feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/><feOffset dy="-3" dx="-3"/><feComposite in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowDiff"/><feFlood flood-color="black" flood-opacity="1"/><feComposite in2="shadowDiff" operator="in"/><feComposite in2="SourceGraphic" operator="over"/></filter>')
	defs.svg('<filter id="grayscale"><feColorMatrix type="saturate" values="0.5"/></filter>')

	var image=draw.image('/main/images/testImage102.jpg').size(100,100).center(80,80)
	.attr({'filter':'url(#grayscale)'})

	//-------------

	//対象エレメントID
	var draw=SVG('svg-test-filter02').size(160,160)

	var text=draw.text('grayscale group化').font({leading:0.8})

	var img_group2=draw.group()
	var image=draw.image('/main/images/testImage102.jpg').size(100,100).center(80,80)
	.attr({'filter':'url(#grayscale)'})
	img_group2.add(image)
	.attr({'filter':'url(#dropShadow2)'})//陰影

	//-------------

	//対象エレメントID
	var draw=SVG('svg-test-filter03').size(160,160)
	var defs=draw.defs()
	defs.svg('<filter id="bevel" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/><feOffset in="blur" dx="4" dy="4" result="offsetBlur"/><feSpecularLighting surfaceScale="5" specularConstant=".75" specularExponent="20" lighting-color="#bbbbbb" in="blur" result="highlight"><fePointLight x="-5000" y="-10000" z="20000"/></feSpecularLighting><feComposite in="highlight" in2="SourceAlpha" operator="in" result="highlight"/><feComposite in="SourceGraphic" in2="highlight" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="highlightText"/><feMerge><feMergeNode in="offsetBlur"/><feMergeNode in="highlightText"/></feMerge></filter>')

	var text=draw.text('bevel filter').font({leading:0.8})

	var image=draw.image('/main/images/testImage102.jpg').size(100,100).center(80,80)
	.attr({'filter':'url(#bevel)'})


filter関数処理内で動作させる

● svg.filter.jsのfilter関数処理内で動作させる方法です。
上記の「defs収容方式」で問題の無かったものが動作しない、ブラウザによっては振る舞いが違うなどが起こる場合があります。「パクリSVG」などは簡略して書かれている場合が多いので、その辺を修正すれば動作します。filterタグに必要な属性があれば関数内で書き込む。filterタグおよびIDは自動で作られdefsに収容されます。
「SVG-HTML記述部分」が曖昧では、特にFirefoxで処理出来ない場合が多い。

filter関数 .filter(function(add){}) でのsvg()の中はfilterタグを除いたHTMLを記述します。
問題が無ければ処理されます。Firefoxでの表示が変な場合は、HTML記述に問題があると考えてよい。font処理ではFirefoxのみ他と違う場合も有ります(将来同じになるかなど不明)。


.filter(function(add) {
	this.svg('filterタグを除いたSVG-HTML記述部分')//thisは重要です
})

filterタグ設定が必要なら、.attr()で追加します。thisはfilterです。


//svg()方式
.filter(function(add) {
	this.svg('SVG-HTML記述部分')

	//filterタグ内部の属性設定
	this.attr({'x':'-100%','y':'-100%','width':'400%','height':'400%'})//IE OK
	//color-interpolation-filtersの設定は
	this.attr({'color-interpolation-filters':'sRGB'})
})

svg()の中はSVG.jsの管理外ですので、children()など取得できませんし、通常は中の要素にアクセスも出来ません。指定位置に挿入されマチガイがなければ実行すると言う事です。svg()は使用しても管理上の痕跡すら残っていません。


filter関数内の記述方法の注意点

1. 最初のメソッド実行は add をつけます。その後に続くメソッドチェーンには付けません。
2. 最初のメソッドに指定がなければインスタンスのSourceGraphic(source)が入ります。SourceAlpha(sourceAlpha)を入れたければ、in()で変更します。
(明示的に常に、in()で指定すればマチガイはありません)
3. メソッドチェーンの場合、前の結果であるresult値が、次のinに自動的に入ります。
(composite()など、第一引数にin指定のものなどは、記述を省きます、但しmerge()は除く)
4. 処理を分離(分割)する場合は変数に取り込みます。effectに成ります。次に受け渡しできます。
5. source、sourceAlphaは前にaddをつけます。effect、resultは付けません。
6. merge()をメソッドチェーンの中に入れないでください。上手く処理しない。
7. thisはfilterタグをめざす。filterの属性設定が必要なら、this.attr()で設定できます。
(filter関数をかけば、固有IDを付与されたfilterタグが生成されますので、filterのIDは this.attr('id')で取得できます)
8. var effect=this.svg()はマチガイ。effect変数ではなく単なるオブジェクト。
9. マチガイなく設定が記述されれば、対象インスタンスのfilter属性にurl(#)が書き込まれます。
10. Firefoxは規格に厳密ですから注意のこと。反対に Chrome、IE は甘いので少し間違っても動作する。


var effect=this.svg()等の記述はマチガイです

filter処理関数内でsvg()と通常処理を混同しても、Firefoxは認識せず処理し無い。
Chrome IEは処理する場合が多いので勘違いしてしまう。
そもそも、nanda_korya はエフェクトでなくオブジェクトですから、Firefoxから見て「何だコリャ」に過ぎない。混同処理は極力しない方が良いと思います。必要があればresultを受け渡しできるように修正の必要があります。


//Firefox処理しません
.filter(function(add){
	var nanda_korya=this.svg('SVG-HTML記述部分')
	add.composite(nanda_korya,add.sourceAlpha,'in')//Firefox認識せず
})

ここでは最終のresult値を取り出して、代入すれば正常になります。当然Firefoxも問題ない。
もし、result属性がなければ、HTMLに直接に「手書き」で書けばよい。
filterタグ直下に構成されますから、該当result属性のタグは「filter子要素の何番目」か指定すれば、result値が取れます。
(注意、最初の1番目のは「子要素」は0番です、子の又中の子要素は把握できません)


//result値で処理すればOKです
.filter(function(add){
	var nanda_korya=this.svg('SVG-HTML記述部分')
	var result=this.get(何番目).attr('result')//result取得
	add.composite(result,add.sourceAlpha,'in')//OK
})

4番、svg()処理、簡単な例ですから問題は出ません。
5番、svg()処理の値を取り出し、陰影と重ね処理。
6番、簡単な例なのでcolorMatrix()で処理してみました。


4番、grayscale filter / 5番、grayscaleと陰影 / 6番、colorMatrix()と陰影


サンプルJS


	//対象エレメントID
	var draw=SVG('svg-test-filter04').size(160,160)

	var text=draw.text('grayscale filter').font({leading:0.8})

	var image=draw.image('/main/images/testImage102.jpg').size(100,100).center(80,80)
	.filter(function(add) {
		//svg()処理
		this.svg('<feColorMatrix type="saturate" values="0.5"/>')

	})

	//-------------

	//対象エレメントID
	var draw=SVG('svg-test-filter05').size(160,160)

	var text=draw.text('grayscale filter').font({leading:0.8})
	var image=draw.image('/main/images/testImage102.jpg').size(100,100).center(80,80)
	.filter(function(add) {
		//svg()処理/shadow追加
		this.svg('<feColorMatrix type="saturate" values="0.2" result="result1"/>')
		var result=this.get(0).attr('result')
		//shadow
		var shadow=add.offset(5,5).in(add.sourceAlpha).gaussianBlur(2)
		add.merge(shadow,result)

		this.attr({'x':'-100%','y':'-100%','width':'400%','height':'400%'})//IE OK
	})

	//-------------

	//対象エレメントID
	var draw=SVG('svg-test-filter06').size(160,160)

	var text=draw.text('grayscale filter').font({leading:0.8})

	var image=draw.image('/main/images/testImage102.jpg').size(100,100).center(80,80)
	.filter(function(add) {
		//SVG.js形式/effect値で受け渡し
		var matrix=add.colorMatrix('saturate',0)
		//shadow
		var shadow=add.offset(5,5).in(add.sourceAlpha).gaussianBlur(2)
		add.merge(shadow,matrix)

		this.attr({'x':'-100%','y':'-100%','width':'400%','height':'400%'})//IE OK
	})


svg.filter.jsの書式(SVG.js形式)で記述する。

誰しも始めからSVGをスラスラ書けませんから、パクリもので動作確認して覚えてゆきます。
当初はsvg()形式で処理してきたが、原始フイルターなどは、svg.filter.jsの形式でほぼ記述できる。但し、まだ対応していない部分も有りますから、多少工夫が必要になります。


1. diffuseLighting()で fePointLight feDistantLight feSpotLightタグがまだ挿入できない。(暫定的に.svg()で挿入できますが、チェーンメソッドではメンドウなので上手く分離して処理します)
2. diffuseLighting()で lighting-color属性が挿入できない。設定なきは白色。(.attr()で挿入できます)
3. specularLighting()もdiffuseLighting()の反対の処理で、1.同様です。
4. composite()で k1-k4 属性が挿入できない。(.attr()で挿入できます)
5. 他のクラスでも足りない部分があれば、attr()で属性設定が出来ます。
6. チェーンメソッドでまだ不具合があるので注意ください。(順序を変えるか、分離するかなどの処置で確認します)
7. SVGはHTMLですから、未対応部分などは工夫すれば全て対処できます。


svg.filter.jsの書式記述の実際

処理用のSVGは下記サイト掲載のものを使用しました。都合よくfeDiffuseLighting、feCompositeで構成されていますので、説明に都合よい為サンプルにします。

参考にするSVGが必ずしも、svg.filter.jsのfilter処理関数の中で動作するとは限りません。問題があれば調べて属性の変更、追加などを行いますが、慣れないと意外と判りません。InkscapeのSVGで在っても起こり得ます。


https://developer.mozilla.org/ja/docs/Web/SVG/Element/feDiffuseLighting

<feDiffuseLighting in="SourceGraphic" result="light" lighting-color="white"><fePointLight x="50" y="50" z="40"/></feDiffuseLighting><feComposite in="SourceGraphic" in2="light" operator="arithmetic" k1="1" k2="" k3="0" k4="0"/>

● diffuseLighting()設定の注意。
svg.filter.jsはまだ作りかけであり、fePointLight feDistantLight feSpotLightタグが自動挿入されません(困ったモンダ)。
そのため暫定的にsvg()で挿入して利用することにしますので、何時の日か、正式に対応する様になれば「書き換え」が必要と予想されます(別段マニュアルにアナウンスはありませんが、これで最終では無いと思う...)。この点了承ください。


書式
diffuseLighting(surfaceScale,diffuseConstant,kernelUnitLength)
diffuseLighting('1',1,'1')

diffuseLighting()等で属性を組み込まない場合は null 設定にします
diffuseLighting('1',null,'1')

kernelUnitLengthがマイナスまたは0ではエラーになりますので '1' を入れています
surfaceScaleは文字型で挿入しないと、Firefoxでエラー

● diffuseLighting()に fePointLight feDistantLight feSpotLight タグを入れる。
diffuseLighting()記述直後にsvg()で挿入しますので、分離して処理します。
属性設定はHTMLで直接記述します。ここではfePointLightの例。
lighting-color属性の設定なき場合は、「白色光」として処理されます。


//fePointLightタグをfeDiffuseLightingタグの中に挿入
var diffuse=add.diffuseLighting('1',1,null).in(add.source).svg('<fePointLight x="50" y="50" z="40"/>')

//lighting-color属性挿入
var diffuse=add.diffuseLighting('1',1,null).in(add.source).attr({'lighting-color':'white'})

注意、feDiffuseLightingタグの中に入れますので、thisをつけないでください。


//マチガイです
this.svg('<fePointLight x="50" y="50" z="40"/>')

中央のサンプル例 (SVG.js形式に変換)
変数、diffuse、compositeは「effect」名で有り、他の処理に受け渡しできます。「effect」名の妙名は自由です。



.filter(function(add) {

	//(surfaceScale,diffuseConstant,kernelUnitLength)
	var diffuse=add.diffuseLighting('1',1,null).in(add.source).attr({'lighting-color':'white'}).svg('<fePointLight x="50" y="50" z="40"/>')
	//(in1,in2,operator)
	var composite=add.composite(add.source,diffuse,'arithmetic').attr({'k1':1,'k2':0,'k3':0,'k4':0})

})
--------------------------------------------

処理を継続しないならば最後は下記でも良い
add.composite(add.source,diffuse,'arithmetic').attr({'k1':1,'k2':0,'k3':0,'k4':0})

svg.filter.js形式に直してしまえば、svg()で追加した子要素にもアクセス可能です。1番目サンプルとの違いです。

但し、filterに対してget()が機能しますので、filterの中の何番目の子要素かわかれば(子の又中の子要素は条件により把握できない場合も有ります)、attr()で属性の変更取得など行えまが、管理外ですからお勧めしません。必要なら直接手書きで変更は出来ます。

サンプル7番、8番のコードをご覧ください。svg()の使用方法の違いをヨク理解して下さい。



//子要素が管理内のためfePointLightが認識できる
var pointlight=this.get(0).get(0)
.attr({'x':'65','y':'65','z':'20'})

「エフェクト」は判り易い名前を付ければ良い、この辺は事由です。


.filter(function(add) {
	var lighting=add.diffuseLighting....
	var effect=add.composite...
})

in()、merge()、composite()、等の引数はマニュアルではeffectまたはresult値とはなっていますが。実際result()でresult値を挿入してcomposite()等を実行すれば、function{}を含む値が代入され、Firefox NGになり易い。(svg()の中のresultは例外)
よって、常に上記の様に「エフェクト」宣言して処理を進めた方が良いと思います。


● 陰影処理の追加
3番目の様に、各種filter処理したあとに陰影を追加する場合は、merge()で重ねれば簡単です。url(#)でも可能ですが、グループ化しなければなりません。またfilter処理によっては、url(#)で陰影が付かない場合も有ります。
要は、merge()に受け渡しできるように記述すればよい。

重なり順からすれば、shadowから記述するのが正当かもしれないが、get()などの処理をすれば「位置の把握がメンドウ」になるので、後で書いています。merge()で重なりの順番は修正できますので...


.filter(function(add) {

	//メインのfilter処理
	var effect=add..........

	//shadowの追加
	var shadow=add.offset(2,2).in(add.sourceAlpha).gaussianBlur(3)
	add.merge(shadow,effect)

	this.attr({'x':'-100%','y':'-100%','width':'400%','height':'400%'})//IE OK
})

7番、svg()形式で仮表示 / 8番、SVG.js形式に変換 / 9番、SVG.js形式に変換と陰影


サンプルJS



	//対象エレメントID
	var draw=SVG('svg-test-filter07').size(160,160)

	var text=draw.text('svg()形式で仮表示').font({leading:0.8})

	//var image=draw.image('/main/images/testImage102.jpg').size(100,100).center(80,80)
	//var circle07=draw.circle(100).attr({'fill':'#F00','stroke':'#DC143C','stroke-width':4}).center(80,80)
	var circle07=draw.circle(100).attr({'fill':'#DC143C'}).center(80,80)
	.filter(function(add) {

		//サンプル例
		this.svg('<feDiffuseLighting in="SourceGraphic" result="light" lighting-color="white"><fePointLight x="50" y="50" z="40"/></feDiffuseLighting><feComposite in="SourceGraphic" in2="light" operator="arithmetic" k1="1" k2="" k3="0" k4="0"/>')

		//get()でlighting-color変更も可能のようですが/fePointLightget()では認識できない
		//var fediff=this.get(0)
		//fediff.attr({'lighting-color':'#AAA'})
	})

	//-------------

	//対象エレメントID
	var draw=SVG('svg-test-filter08').size(160,160)

	var text=draw.text('SVG.js形式に変換').font({leading:0.8})

	var circle08=draw.circle(100).attr({'fill':'#DC143C'}).center(80,80)
	.filter(function(add) {

		//(surfaceScale,diffuseConstant,kernelUnitLength)
		var diffuse=add.diffuseLighting('1',1,null).in(add.source).attr({'lighting-color':'white'}).svg('<fePointLight x="50" y="50" z="40"/>')
		//(in1,in2,operator)
		var composite=add.composite(add.source,diffuse,'arithmetic').attr({'k1':1,'k2':0,'k3':0,'k4':0})

		//子要素が管理内のためfePointLightが認識できる
		//var pointlight=this.get(0).get(0)
		//.attr({'x':'65','y':'65','z':'20'})

	})

	//-------------

	//対象エレメントID
	var draw=SVG('svg-test-filter09').size(160,160)

	var text=draw.text('SVG.js形式、陰影追加').font({leading:0.8})

	var circle09=draw.circle(100).attr({'fill':'#DC143C'}).center(80,80)
	.filter(function(add) {

		//(surfaceScale,diffuseConstant,kernelUnitLength)
		var diffuse=add.diffuseLighting('1',1,null).in(add.source).attr({'lighting-color':'white'}).svg('<fePointLight x="50" y="50" z="40"/>')
		//(in1,in2,operator)
		var composite=add.composite(add.source,diffuse,'arithmetic').attr({'k1':1,'k2':0,'k3':0,'k4':0})
		//shadow
		var shadow=add.offset(2,2).in(add.sourceAlpha).gaussianBlur(3)
		add.merge(shadow,composite)

		this.attr({'x':'-100%','y':'-100%','width':'400%','height':'400%'})//IE OK
	})


作ったFilterの使い回し

● 作ったFilterは url(#) で使い回しが可能です。
簡単にFilterを作れますが、同じFilterを複数個所で使用する場合、その都度書くのもメンドウです。
1. 作ったFilterは、即座に「defs」に登録されますので、以後、Filterの「ID名」が判れば再利用出来ます。
2. グローバルな変数に、Filter「ID名」を保存しておけば、url(#)で簡単に再利用できます。
3. 直接、出力のID名は使用しないでください。コードなど書き換えた際、名前が変わり易い。



グローバルな変数にID名を保存
var filter_id//ID保存用

Filter処理の中でID名を取得
.filter(function(add){

	filter処理は略す

	//ID保存
	filter_id=this.attr('id')
})

Filter使い回し、画像  レクト などを描画する
var rect=draw.circle(60,60).......
.attr({'filter':'url(#'+ filter_id +')'})

svg.filter.jsの Effect Classes

以前は無かった、Effect Classes等のマニュアル説明が付きました。全てに対処はまだ出来ませんが、工夫すればほぼsvg.filter.jsの記述に出来ます。
説明など長くなります。このページ容量の制約により、別途デモページで説明します。


DEMO、Filterクラスなどの説明と実際表示デモ

DEMO-042


サンプルJSの全表示

Chromeは文字化けしますのでエンコード(UTF-8)してご覧ください。

● サンプルJSの全表示1(解説ページ用) svg-sample19.js

使用画像

画像は原則使用者が用意ください。デモなどで使用した画像は下記に有ります。

使用画像サンプル

 


 

新しいDropShadowについては「前ページ」を参照ください。

【参照】当方の記事: SVG.js filterとDropShadow


 

svg()は便利です。以上



[ この記事のURL ]


タグ:javascript , SVG

 

ブログ記事一覧



[1]