画像をSVGで切り抜く clip-path, mask

画像をSVGで切り抜く clip-path, mask

前記事「GIMP 初期状態に戻す、パスツールを使う」では、GIMP で葉っぱの SVG ファイルをつくり画像を切り抜く過程を記事にしましたが、その際、CSS のプロパティ clip-path がなかなか思うような結果を出してくれず苦労しましたので整理してみました。

01clip-path

画像を円で切り抜くのは簡単で、

<img class="c-mask__circle" src="/wp-content/themes/leaf/images/flower.jpg">

img タグに class を指定して、

.c-mask__circle {
    clip-path: circle(50%);
}

とすれば簡単に円で切り抜くことができます。

で、これを作成した葉っぱの SVG ファイルで切り抜こうと、

.c-mask__circle {
    clip-path: path("M 76.09,292.94 C 57.82,287.82 41.50,268.68 0.45,299.04 21.19,256.05 15.59,246.00 11.19,234.82 -27.79,0.05 141.32,4.45 182.54,19.44 195.89,21.24 206.76,33.45 206.47,57.62 206.44,60.40 230.89,5.61 277.88,0.19 277.88,0.19 283.63,-0.24 281.33,4.22 239.76,0.60 211.53,62.37 213.42,61.52 232.78,52.88 260.86,65.61 269.16,76.82 366.94,215.87 207.04,314.55 76.09,292.94 Z");
}

CSS に SVG のパスを入れますと、

こうなってしまいます。

SVG ファイルの viewBox を viewBox="0 0 300 300" で作っているからかと思い、画像の領域 100px と同じ viewBox="0 0 100 100" にしてみますと、

と、思うようなサイズで切り抜くことができます。ただ、ブラウザの幅を狭くしますと、

こうなってしまいます。つまり、clip-path がレスポンシブにならないということです。

いろいろ調べますと、これは切り抜く領域が絶対値になっているためで、レスポンシブ対応にするためには相対値で指定しなくてはいけないということのようです。

02clipPath 要素の clipPathUnits 属性

切り抜く領域を相対値で指定するためには、SVG の clipPath 要素の clipPathUnits 属性に objectBoundingBox を指定する必要があります。デフォルトでは userSpaceOnUse 、つまり、クリッピングパスの作成時に定義されたユーザー座標を使っているということになります。ですので、上の例では、画像を表示する領域は狭くなっていくのに切り抜く領域は相変わらず 100px のままだということです。

ということになりますと、CSSに直接パスを書くことができなくなります。

HTMLに svg 要素を書き出し、clipPath 要素でくくり、id 属性を指定して、切り抜く画像の class にその id を url 指定します。また、objectBoundingBox を指定した場合は、パスを viewBox="0 0 1 1" で書き出す必要があります。

今回は自前の SVG ですのでパス領域の範囲を 1px × 1px にして SVG を書き出しましたらうまくいきました。それができない場合は、transform を使う方法もあります(下に記述)。

<img class="c-mask__leaf" src="/wp-content/themes/window/images/flower.jpg">

<svg>
    <clipPath id="clipshape-leaf" clipPathUnits="objectBoundingBox">
        <path d="M 0.25,0.98 C 0.19,0.96 0.14,0.90 0.00,1.00 0.07,0.85 0.05,0.82 0.04,0.78 -0.09,0.00 0.47,0.01 0.61,0.06 0.65,0.07 0.69,0.11 0.69,0.19 0.69,0.20 0.77,0.02 0.93,0.00 0.93,0.00 0.95,-0.00 0.94,0.01 0.80,0.00 0.71,0.21 0.71,0.21 0.78,0.18 0.87,0.22 0.90,0.26 1.22,0.72 0.69,1.05 0.25,0.98 Z" />
    </clipPath>
</svg>
c-mask__leaf{
    clip-path: url(#clipshape-leaf)
}

これでレスポンシブ対応になりました。

transform を使う場合は、scale で縮小します。値は、1/viewBoxの横, 1/viewBox縦 で計算します。viewBox=”0 0 100 100″ であれば、次のようになります。

<path transform="scale(0.01, 0.01)" d="M 0.25,0.98 (略)/> 

03mask

ということなんですが、なんだか面倒ですね(笑)。特に、変更したい場合に HTML を弄らなくちゃいけないのがなんだかです。

もっと簡単な方法がありました。CSS のプロパティ mask です。これですとすべて CSS で設定できますし、background-image とほぼ同じように扱えます。特別意識しなくてもレスポンシブになります。

<img class="c-mask__leaf" src="/wp-content/themes/window/images/flower.jpg">

の上と同じ画像 HTML に対して、

.c-mask__leaf{
    mask-image: url("images/leaf.svg");
/* 条件によってはいろいろ設定が必要になる
    mask-repeat: no-repeat;
    mask-position: 0 0;
    mask-size: cover;
*/
}

と、SVG ファイルを別に保存しておき url 指定するだけです。

また、SVG 要素には transform 属性があり、移動、伸縮、回転、傾斜の変形ができます。

たとえば、ひとつのパスに対して、

<path d="M 25.36,97.65 (略)" />
<path transform="translate(50, 50), rotate(-90), translate(-50, -50)" d="M 25.36,97.65 (略)" />
<path transform="translate(50, 50), scale(-0.9, 0.9), translate(-50, -50)" d="M 25.36,97.65 (略)" />

と、変化をつけることもできます。

なお、transform の複数指定は右側から適用されるらしく、2行目の場合ですと、座標の中心をパスの中央にずらし、90度回転させ、中心を 0, 0 に戻しています。 3行目の scale は scale(-1, 1) でパスが反転しますのでそれをさらに 90% に縮小しています。

そうしますとこうなります。ただ、結局 SVG ファイルを3つ作ることになりますので、ソース作成時点で3つ作ったほうが早いような気もします(笑)。

それに、もう少しセンスのいいものにしなくちゃいけないですね(笑)。

CSS の clip-path, mask プロパティについて少し理解が進みました。