フーノページ



SVG feImage

SVGのfeImageフィルター。








syntax。

<filter id = "「「5filtername」」">
    <feImage href = "「「2imgのURL」」" result = "「「5resultname」」"/>   
</filter> 


画像をfilterコンテナに取り込む。


こんにちは、「ふ」です。
SVGのフィルターシリーズ、今回はSVGの中に外部画像を取り込む<feImage>について紹介します。

<image>要素で良いのでは?


<image href = "...."/>

SVG領域にただ単に画像を読み込ませたいのであれば、<image>要素で事足ります。width/height、x/yプロパティで位置や大きさを調整することもできます。また<image>要素単体にぼかしなどのフィルターを施すことも可能です。

が、filterを使って取り込んだ画像と他の要素を合成させたいときには、画像をfilterコンテナの中で扱えるようにする手続きが必要です。

feImageで合成するしくみ

⬆︎はSVG要素に対し、フィルターで読み込んだ画像と合成しているイメージです。<image>要素は直接このノード(オレンジの線)に参加させることはできません。<feImage>を使って画像を読み込み、result属性で呼び出すことによってはじめて、フィルターコンテナの中で扱うことができるようになるのです。

本記事では外部画像を読み込み、フィルターコンテナ内で別の要素と合成処理を行うための手順を紹介していきます。
そしてこの<feImage>、位置やサイズ調整については一癖も二癖もあるのです💦 それについても記事の後半で詳しく解説していきますね。



このたびSVGの書籍を出版しました。
しっかりとしたSVGスキルが身に付く内容となっています。参考にしてください。





つかいかた。


<svg viewBox="0 0 400 300">
    <rect x = "100" y = "100" width = "200" height = "100" 
    fill = "turquoise"/>
</svg>

SVGの中に横長の長方形オブジェクトと、

◾️ a4.png


画像としてa4.png⬆︎を用意しました。
長方形のオブジェクトに対して、<feImage>で画像を取り入れていきます。

<svg viewBox="0 0 400 300">
    <rect x = "100" y = "100" width = "200" height = "100" 
    fill = "turquoise"/>
    
    <filter id = "「「5filter_img」」">
        <「「1feImage」」 href = "a4.png"/>」」  
    </filter>
</svg>

<filter>要素でフィルターコンテナを定義します。参照できるように「filter_img」とid名を付けておきます。
フィルターコンテナの中に<feImage>要素を配置し、href属性で画像のurlを指定します(src属性ではないので、ここ注意)。

<rect>要素にこのフィルターを掛けてみましょう。

<rect x = "100" y = "100" width = "200" height = "100" 
    fill = "turquoise" filter = "url(「「5#filter_img」」)"/>


画像が描画されました。
そして、元の長方形オブジェクトは非表示になっています。現在、filterコンテナの中には<feImage>しか接続されていないので、最終的な出力も読み込んだ画像のみになっているのです。


元オブジェクトと合成。

元の長方形オブジェクトと合成してみましょう。
ここでは2つの色をブレンドして表現するフィルター、<feBlend>を使いました。

<filter id = "filter_img">
        <feImage href = "a4.png" result = "「「5sample_img」」"/>」」 
    
        <feBlend in = "「「5sample_img」」" 
        in2 = "SourceGraphic" mode = "screen"/>
</filter>

<feImage>の結果を呼び出せるように、result属性で「filter_img」という名前を付けました。
付けた名前を、<feBlend>のin属性に指定。in2属性にはSourceGraphic(元オブジェクト)を指定します。

元オブジェクトと画像を合成することができました。
SVGの<image>要素では画像を読み込むことは可能ですが、このようにfilterの合成材料として扱うことはできません。

ところで2つの要素は縦横比が異なるため、中央で重なってはいますが、お互いがはみ出し合っています。これについては記事後半のセクションで詳しく探るとしましょう。



領域いっぱいに画像を表示。

<filter>要素のfilterUnits属性の値を「userSpaceOnUse」にすることで、画像をviewBox(SVGの表示領域)いっぱいに表示させることも可能です。

<filter id = "filter_img" filterUnits = "「「1userSpaceOnUse」」">
        <feImage href = "a4.png" result = "sample_img"/> 
        <feBlend in = "sample_img" 
        in2 = "SourceGraphic" mode = "screen"/>
</filter>


viewBoxいっぱいに画像が表示されました。




preserveAspectRatio。


先ほどの元オブジェクトと画像がはみ出し合った状態。
これは、<filter>の描画範囲が対象の縦横120%となっているのと、preServeAspectRatio属性が働いているためです。

<filter&gr;要素の持つfilterUnits属性が初期値の「objectBoundingBox」の場合は、要素のバウンディングボックスに対して幅/高さともに外側10%の領域に、<filter>の効果が描画されます。
画像の上下が長方形オブジェクトからはみ出していたのはそのためです。

扨(さて)この描画範囲と、元画像の縦横比は異なります。描画範囲にむりやり画像を当てはめようとすると、画像は歪んでしまいます。

元画像の縦横比を保ったまま描画範囲に収めるには「隙間をつくる」か、「はみ出させる」 のいづれかの方法をとる必要があります。どちらの処理をするか指定するのが、preserveAspect属性です。

preserveAspect属性の初期値は「xMidYMid meet」となっており、これは「隙間を作って収める」指定です。結果、画像の横幅は長方形のものよりも小さくなりました。
横方向に長方形がはみ出しているのはそのためです。

preserveAspectRatioの値を「xMidYMid slice」とすると、画像を「はみ出させて収める」という処理になります。

<feImage href = "a4.png" result = "sample_img"
 preserveAspectRatio = "「「1xMidYMid slice」」"/>        


画像がfilterの描画領域いっぱいに広がりました。ただし上下の部分ははみ出したため、非表示となっています。

preserveAspect属性ではほかにも位置を合わせたりなどの細かい指定をすることが可能です。くわし〜く知りたい方は⬇︎の記事を参考にしてください。

SVGに画像をぴったりと配置〜
preserveAspectRatio。

〽️ 縦横比が違っても安心。





width/height属性。


<feImage href = "a4.png" result = "sample_img"
width = "「「4...」」" height = "「「4...」」"/>        

<feImage>には画像の大きさを指定する、width/height属性があります。先ほどお話ししたようにデフォルトでの画像領域は、フィルター対象の縦横120%の範囲となっています。
具体的にwidthやheightを指定した場合、どのようになるのかみていきましょう。

widthやheightは座標値や、相対値の%で指定することができます。いづれも指定した場合にはviewBoxが基準となります。

<feImage href = "a4.png" result = "sample_img"
width = "「「1100%」」"/>        

widthを100%にしてみます。

結果⬆︎です。
画像が右側に寄ってしまっています。これはどういうことなのでしょうか?

この謎を解くには、<filter>の描画範囲と<feImage>の描画範囲を個別に考える必要があります。

⬆︎はwidth指定をする前の、初期状態です。
<filter>の描画範囲をオレンジ、<feImage>の描画範囲を青の線で示しました。初期状態のとき、2つは完全に一致しています。

widthを「100%」としたとき、その基準はviewBoxとなり、<feImage>の描画領域がviewBoxの幅まで広げられます。その描画領域に対して、preserveAspectRatioの初期値である「xMidYMid meet」に基づいて画像が配置されます。

ただし描画のスタート位置はあくまで<filter>の描画範囲である、-10%,-10%の位置となります。

最終的にfilter要素自体の描画範囲の制約を受け、画像が右に寄った状態で途切れる、という結果になったのです。

画像の位置を調整するには、x/y属性を具体的に指定する必要があります。




x/y属性で位置を調整。


<feImage href = "a4.png" result = "sample_img"
width = " " height = " "
x = "「「4...」」" y = "「「4...」」"/>        

<feImage>のx/y属性は、<feImage>の描画範囲の左上の位置を指定するプロパティです。これらの属性もviewBoxに対して作用します。値も座標値または%指定が可能です。

初期状態では、<filter>要素の描画範囲と一致しています。
具体的に指定することでviewBoxに対しての位置を調整することができます。

<feImage href = "a4.png" result = "sample_img"
width = "100%" x = "「「10」」"/>        

x = "0"と指定すると、<feImage>の描画範囲の左上が、viewBoxの水平方向0の位置に固定されます。

実際の結果⬆︎です。
右に寄っていた画像が中央にもどってきました。



指定後は固定される。

xy属性を具体的に指定すると、<feImage>のの描画範囲はviewBoxに対して固定され、<filter>の描画範囲を追尾しなくなります。

まづはxy属性指定なし〜初期状態で試してみます。

<svg viewBox="0 0 400 300">

    <rect x = "100" y = "100" width = "200" height = "100" 
    fill = "turquoise"/>
    
    <filter id = "filter_img">
        <feImage href = "a4.png" result = "sample_img"/> 
        <feBlend in= "sample_img" 
        in2="SourceGraphic" mode="screen"/>
    </filter>
    
</svg>


x/y属性は指定なし(= 初期状態)での描画です。これまで何度も見てきたものですね。

現在、<feImage>の描画範囲と<filter>の描画範囲は一致しています。
ここで元オブジェクトである長方形を移動させてみましょう。

    <rect x = "「「1150」」" y = "「「1150」」" width = "200" height = "100" 
    fill = "turquoise"/>
    
    <filter id = "filter_img">
        <feImage href = "a4.png" result = "sample_img"/> 
        <feBlend in= "sample_img" 
        in2="SourceGraphic" mode="screen"/>
    </filter>


元オブジェクトの移動に合わせて、そのまま画像も移動しました。

x/y属性が初期状態の場合は、オブジェクトの位置に合わせて<feImage>と<filter>、双方の描画範囲がそのまま描画されます。

ここで、<feImage>のx属性を「0」としてみましょう。

    <rect x = "150" y = "150" width = "200" height = "100" 
    fill = "turquoise"/>
    
    <filter id = "filter_img">
        <feImage href = "a4.png" x = "「「10」」" result = "sample_img"/> 
        <feBlend in= "sample_img" 
        in2="SourceGraphic" mode="screen"/>
    </filter>


画像が左にずれてしまいました。

x属性に「0」という具体的な値を指定、その時点で<feImage>の描画範囲は、viewBox水平方向の「0」に固定されてしまったのです。元オブジェクトの場所を追尾することなく、この位置でフィルター結果が描画されます。
いっぽうy属性は指定なしの初期状態です。よって垂直方向に関しては<filter>の描画範囲と一致する形に。元オブジェクトの位置と大きさを追尾したことになります。




完全一致させるには?


ではオブジェクトに対して読み込んだ画像を完全に同じサイズで表示させるにはどのようにすればいいのか?と思う方もいらっしゃるでしょう。
その場合は<filter>のほうの描画範囲を調整します。

    <filter id = "filter_img"
    width = "「「1100%」」" height = "「「1100%」」" x = "「「10」」" y = "「「10」」"
    >
        <feImage href = "a4.png" result = "sample_img"/> 
        <feBlend in= "sample_img" 
        in2="SourceGraphic" mode="screen"/>
    </filter>


<filter>のfilterUnitsが初期値の「objectBoundingBox」のときには、width/heightやx/y属性はオブジェクトのバウンディングボックスを基準に働きます。
幅と高さ、描画範囲のスタートをオブジェクトのバウンディングボックスと一致させます。<feImage>の描画範囲も、指定なしの状態であれば<filter>の描画範囲と一致します。

結果です。
画像が完全にオブジェクトの大きさになりました。AspectRatioの「xMidYMid meet」」が作用しているため、横方向には余白ができています。
縦横を完全に一致させたいのであれば、オブジェクトのバウンディングボックスと同じ縦横比の画像を用意する必要があります。

最後までお読みくださり、 ありがとうございました。
今回はSVGのfeImageフィルターについて紹介してきました。画像読み込みそのものよりも、位置や大きさの調整でとても苦労しましたね。実際に使用する際には、このページの内容を参考にしてください。

ではまた〜 🎵




「ふ」です。

ふ

ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。