知らなきゃ損!CSSのmaskとSVGを駆使して画像を自在に切り抜く方法

2017/03/07

Maria Antonietta Perna

254

Articles in this issue reproduced from SitePoint
Copyright © 2017, All rights reserved. SitePoint Pty Ltd. www.sitepoint.com. Translation copyright © 2017, KADOKAWA ASCII Research Laboratories, Inc. Japanese syndication rights arranged with SitePoint Pty Ltd, Collingwood, Victoria,Australia through Tuttle-Mori Agency, Inc., Tokyo

CSSで画像をマスクしたり切り抜いたりする方法にはborder-radius、clip、clip-pathなどがありますが、maskプロパティとSVGのmask要素を使った方法なら、もっと自由な形にマスクができます。

※本記事は2016年6月17日に掲載した記事を再編集したものです。執筆時点の情報をベースにしており、最新ではない可能性があります。

マスクは、画像または要素の不要な部分を隠し、見せたい部分のみを表示するテクニックです。Webデベロッパーは、CSSのmaskプロパティとSVGのmask要素を使うことで、画像編集ソフトを使わずにブラウザー上でマスクをかけられます。

この記事では、ブラウザーのサポート問題も含め、CSSとSVGを実際に試してみましょう。

記事を執筆している時点では、ほとんどのサンプルコードはWebKit系ブラウザーでのみ動きます。ただし、SVGベースのマスクは、より多くのブラウザーが対応しています。とはいえ、サンプルを実際に試す場合は、ChromeのようなWebKit系ブラウザーで使ってください。

Webでマスクする2つの方法

Webでマスクをかけるには、クリッピングもしくはマスクを使います。

クリッピングとは、たとえば円や多角形など、ベクトル画像のシェイプをマスクしたい対象の画像または要素の上に重ねる方法です。マスクの後ろの画像が見えるようになり、境界線の外の部分は隠れます。境界線はクリップパスとよび、CSSのclip-pathプロパティで作成できます。

マスクは、PNG画像、CSSグラデーション、またはSVG要素を使って、対象の画像またはページの要素を隠します。CSSのmaskプロパティを使って実現します。

今回は、CSS maskプロパティとSVG <mask>要素を使ったマスクの方法に限定して進めます。

CSS maskプロパティ

maskは、CSSのショートハンドプロパティです。以下で、詳細を説明します。

mask-imageプロパティ

mask-imageプロパティを使って、要素のマスクレイヤーを設定できます。

値がnoneのときは、値を設定しないという意味ではなく、透明な黒画像レイヤーとして認識されます。

URL値にmask-imageを設定できます。PNG画像ファイル、SVGファイル、またはSVGの<mask>要素を参照しているファイルへのパスなども設定できます。また、対応するURL値をコンマで区切って、マスク画像レイヤーを複数設定できます。

/* masking with two comma-separated values */
.masked-element {
  mask-image: url(mask.png), none;
}


/* using external svg graphic as mask */
.masked-element {
  mask-image: url(mask.svg);
}

以下のように、SVG要素をmask1のidと一緒に参照できます。

.masked-element {
  mask-image: url(#mask1);
}

グラデーション画像は、mask-imageプロパティに適しています。

.masked-element {
  mask-image: linear-gradient(black 0%, transparent 100%);
}

mask-modeプロパティ

mask-modeを使うと、マスクレイヤー画像をアルファマスクルミナンスマスクに設定できます。

アルファマスクは、アルファチャンネルの画像です。もっと詳しく説明すると、アルファチャンネルには、透明度の情報が各ピクセルに含まれています。mask-modeプロパティがalphaに設定されている場合、その画像のアルファ値をマスクの値としてマスキングします。

アルファチャンネルの例として分かりやすいのが、黒と透明の領域があるPNG形式の画像です。黒い領域のマスク要素(アルファ値1)に重なった要素が表示される部分になり、透明の領域のマスク要素(アルファ値0)に重なった要素が隠される部分になります。

このPNG画像をアルファマスクとして使ってみましょう。

01

次のJPG画像をマスキングします。

02

おまじないを書きましょう。

.masked-element {
  mask-image: url(alpha-mask.png);
  mask-mode: alpha;
}

すると、ブラウザーには以下のように表示されます。

03

ルミナンスマスクは、画像のルミナンスキー(輝度値)がマスク値として使われます。アルファマスクで使ったPNG形式のマスク画像はルミナンスマスクにちょうど良い例です(ただし、黒かった部分を白にしています)。

04

マスクの白い部分に重なる要素が表示される部分になり、透明な部分に重なる要素が隠される部分になります。

mask-modeプロパティをluminanceに設定し、上のPNG画像をマスクとして使うと、アルファマスクと同じ結果になります。

コードはこのようになります。

.masked-element {
  mask-image: url(luminance-mask.png);
  mask-mode: luminance;
}

mask-repeatプロパティ

mask-repeatプロパティは、background-repeatプロパティとほぼ同じで、サイズと位置を指定したマスクレイヤー画像を繰り返しタイリングできます。

指定可能な値は次のようになります。

  • no-repeat:マスクレイヤーの画像を領域内で繰り返し配置しません。
  • repeat-x:マスクレイヤーの画像がX軸に沿って繰り返し配置します。
  • repeat-y:マスクレイヤーの画像がY軸に沿って繰り返し配置します。
  • space:マスクレイヤーの画像を領域全体に間隔を空けて繰り返し配置します。
  • round:マスクレイヤーの画像が指定した回数分、領域内で繰り返し配置します。もし指定回数のタイリングで領域内に収まらない場合は、収まるように画像のスケールも自動的に変更されます。

たとえば、以下の画像をマスクに使うとします。

05

以下抜粋したコードでは、mask-repeatプロパティをspaceに設定しています。

.masked-element {
  mask-image: url(imgs/trapeze.png);
  mask-repeat: space;
}

マスクの結果は次のようになります。
06

mask-positionプロパティ

mask-positionプロパティを使って、マスクレイヤー画像を配置できます。使い慣れたCSSのbackground-imageプロパティの設定と同じように設定できます。初期の値はcenterになっています。

たとえば、マスク画像レイヤーをビューポートの左上に置きたい場合、mask-positionプロパティの値を0 0に設定します。

.masked-element {
  mask-position: 0 0;
}

このコードは、次のようにブラウザーに表示されます。

07

上の例のmask-positionプロパティの値を、100% 100%に変更すると、マスクレイヤー画像がビューポイントの右下に表示されるようになります。

08

mask-sizeプロパティ

マスクレイヤー画像サイズは、CSSで使い慣れているbackground-sizeプロパティと同じ値で設定可能なmask-sizeプロパティを使って、簡単に設定できます。

たとえば、mask-size50%に設定すると、マスクレイヤー画像の最大幅の50%のサイズを表示します。

09

mask-sizecontainに設定すると、マスクレイヤー画像をマスクの配置領域内の縦横に入りきる最大サイズで表示します。

10

CodePenで、これまでのライブデモが見られます。

マスクレイヤー構築

これまで説明してきたように、mask-imageプロパティの各値をコンマで別々に分けて設定することで、同じ要素に対して複数のマスクレイヤーをかけることができます。各レイヤーはお互いに重なり合う形になっており、最後のレイヤーがスクリーンに最初に表示されるレイヤーとなります。

コードの例は次のようになります。

.masked-element {
  mask-image: url(mask1.png), url(mask2.png);
}

上記のコードでは、mask2.pngはmask1.pngの上に重なっている状態です。

mask-compositeプロパティは、別々のマスクレイヤーを次のキーワードの値に準じて組み合わせられます。

  • add:mask1.pngの上にmask2.pngを描きます。
  • subtract:mask1.pngに重なっていない部分のmask2.pngを表示します。現時点ではブラウザーがサポートしていないので、独自キーワードのsource-out(編注:-webkit-mask-compositeのキーワード)使う必要があります。
  • intersect:mask1.pngに重なっている部分のmask2.pngを表示します。ただし、現時点ではmaskプロパティをサポートしているWebKit系のブラウザーでも、(独自キーワードであるsource-inが使われている場合でも)何も表示されません。
  • exclude:mask1.pngとmask2.pngの重なり合っていない部分を表示します。現時点ではブラウザーサポートがないので、WebKit独自のXORを使ったほうがよいでしょう。

CodePenで、これまでのライブデモが見られます。

maskショートハンドプロパティ

maskプロパティを使うと、CSSでのマスクキングのすべてのプロパティを一括で設定できます。

以下がmaskを使ってすべてのプロパティの値を一括設定した例です。

.masked-element { 
  mask: image mode position / size repeat origin clip composite; 
}

mask-originmask-clipは、見慣れたbackground-originプロパティとbackground-clipプロパティと同様の機能です。

maskショートハンドでプロパティの並べ替えはできますが、mask-sizeプロパティはmask-positionプロパティのあとに、「/」の記号を使って区別して設定しなければなりません。また、mask-sizemask-positionなしで設定すると、無効になります。

最後に、maskプロパティで設定しなかった値は自動的にデフォルトの値に置き換えられるため、もし個々のプロパティの設定をリセットしたい場合は、maskを使うと本当に便利です。

SVG mask要素

SVG(Scalable Vector Graphics)は、XMLがベースのグラフィックスマークアップ用の言語です。

HTML要素とその他のSVGグラフィックスにマスク効果を追加するには、SVG文書内で<mask>要素を使います。

SVGマスクでもう1つ良いことは、文字を使ってページの要素をマスクできることです。

では1つずつ、説明しましょう。

SVG <mask>要素をHTMLに使う

SVG文書内で<mask>要素を使用したHTML要素のマスキングは、記事執筆時ではFirefoxのみ対応しています。たとえば以下のようになります。

<!-- SVG markup -->
<svg class="svg-mask">
 <defs>
  <mask id="mask1" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
   <linearGradient id="grad" gradientUnits="objectBoundingBox" x2="0" y2="1">
    <stop stop-color="white" offset="0"/>
    <stop stop-color="black" stop-opacity="0" offset="1"/>
   </linearGradient>
   <circle cx="0.50" cy="0.50" r="0.50" id="circle" fill="url(#grad)"/>
  </mask>
 </defs>
</svg>

<!-- masked HTML img element -->
<img class="masked-element" src="backdrop.jpg" alt="masked element">

次のように設定したCSSも一緒に使います。

/* CSS */
.masked-element {
  mask: url(#mask1);
  ...more styles
}

上記コードでは、<mask>要素のidmask1としています。このマスク内で、idgradの白黒のグラデーションを置き、そのグラデーションを丸形の塗り色に設定しています。

最後に、SVG <mask>要素のidの値をCSS maskプロパティで参照しました。これでWebページの画像にマスク効果が反映されます。

また、ページの背景用にその他のスタイル宣言を追加することで、次のような印象的な効果も得られます。

11

丸形のフィルターとして使われているグラデーションの白のところの画像が表示されているのにお気づきでしょうか。逆に、グラデーションの黒いところの画像は隠れています。

CodePenでライブデモが見られます(Firefoxしか対応していないことをお忘れなく!)。

SVGグラフィックにSVG <mask>要素をかける

先の例で使ったSVG <mask>要素と同じものをそのまま使うことができますが、今度はHTML要素の代わりにSVGグラフィックを使ってみましょう。この方法は、最新のIEとWebKit系ブラウザー含め、ブラウザーサポートが充実しているのがメリットです。

以下の抜粋したコードでは、SVG要素の中にマスクしたい画像を置き、それにCSSの maskプロパティを適用しています。この maskプロパティは、先の例と同じように、id mask1のSVG <mask>要素を参照しています。マスクされたSVGグラフィックのコードは以下のようになります。

<svg class="masked-element" width="300" height="200" viewBox="0 0 300 200">
 <image xlink:href="backdrop.jpg" width="300" height="200" />
</svg>

以下がCSSでのマスキング作業の抜粋コードです。

.masked-element image {
  mask: url(#mask1);
}

結果としては前の例と同じように表示されますが、今度はメジャーなブラウザーであればほぼ網羅されているはずです。

CodePenでライブデモが見られます。

SVGテキストのマスク

SVGマスク内でテキスト要素を使ってもマスクができます。

<svg class="text-demo" viewBox="0 0 600 400" width="600" height="400">
  <defs>
    <mask id="myMask">
      <rect width="100%" height="100%" fill="#fff" />
      <text x="125" y="200" id="myText" transform="rotate(5)">Text Mask</text>
      <text x="125" y="293" id="mySubtext">SVG</text>
    </mask>
  </defs>
  <ellipse class="masked" cx="300" cy="200" rx="300" ry="150" fill="rgba(173, 216, 230, 0.8)" />
</svg>
/* Adding background image to body element in CSS */
body {
  background: url('backdrop.jpg') center center / cover no-repeat;
}

/* CSS for text element */
#myText {
  font-size: 100px;
  font-family: impact;
  stroke: #F59DD9;
  stroke-width: 5px;
  fill: #000;
  transform: rotate(5deg);
}

/* CSS for masking */
.masked {
 mask: url("#myMask");
}

上の抜粋コードでは、黒いSVGテキスト要素をSVGマスク内に追加し、水色の楕円形のSVGにCSSmaskプロパティを使ってマスクをかけています。この楕円の背景にある画像(この例ではbodyの背景画像)がテキストの形でマスクされて見えます。

CodePenでコード全体も確認できます。

マスクのアニメーション

CSSトランジションとキーフレームアニメーションを使って、mask-positionmask-sizeをアニメーションできます。

以下は、星型のPNG画像の、基本のキーフレームアニメーションの例です。対応するコードの抜粋のみ示します。

マスクされている要素はHTML <img>タグです。

<img src="masked.jpg">

マスク作業はmaskプロパティのショートハンドを使っています。

img {
  mask: url(star.png) 50% 10% / 80% no-repeat;
}

<img>要素の.animateクラスは、CSSトランスフォームとアニメーションを使って星型の画像に動きを与えています。

.animate {
 transform: rotate(360deg);
 animation: move 0.5s ease;
}

12

ChromeなどWebKit系ブラウザーを起動して、CodePenでライブデモを見てください。

CSSでSVG Mask要素をアニメーション化

SVG <mask>要素を使って<img>タグにマスク効果を追加し、CSSでアニメーション化できます。

ここで簡単なCodePenデモを見てください。どのブラウザーでもアニメーションは表示されますが、マスクに関してはFirefoxのみで表示されます。

幸い、SVGマスクをインラインSVGグラフィックに適用すると、とたんにブラウザーサポートは急増します。同じアニメーションをSVGだけを使った場合のデモはこちらです。

maskプロパティのブラウザーサポート

ブラウザーサポートに関して、ところどころで触れてきましたが、執筆時点のブラウザーサポート状況をまとめると次のようになります。

  • CSS maskプロパティを使ったHTML要素へのPNGまたは外部SVG画像マスクは、-webkit-ベンダープレフィックスがあるWebKit系ブラウザーでのみ対応。
  • HTML要素へのインラインSVGマスク要素は、Firefoxのみ対応。
  • SVG要素へのインラインSVGマスク要素は、WebKit系ブラウザーをはじめ、FirefoxとInternet Explorerにも対応。

YokselによるCodePenデモでは、視覚的にブラウザーサポートについて分かります。

Alan GreenblattGitHubでは、CSSのグラフィックス関係のプロパティがどのブラウザーに対応しているか、詳細に触れています。

Can I Useサイトのブラウザーサポート対応表では、詳細とさらなるリソースへのリンクが提供されています。

現状ではCSS maskのブラウザーサポートは十分ではありませんが、マスク機能をサイトの装飾目的で少しだけ使うのであれば、サポートされていないブラウザーのユーザーは何が欠けているのか気づくこともないでしょう。

最後に、SVGグラフィックにSVG <mask>要素でマスクを適用すれば、幅広いブラウザーサポートを得られ、また見た目のよいサイトになることでしょう。

リソース

(原文:Masking in the Browser with CSS and SVG

[翻訳:Eri Noda]
[編集:Livit

Copyright © 2017, Maria Antonietta Perna All Rights Reserved.

Maria Antonietta Perna

Maria Antonietta Perna

BootstrapやjQueryでの作業を楽しむフロントエンドデベロッパー。HTMLテンプレートやカスタムWordPressテーマの構築に強い興味を持ち、WPThemeMakeover.comで幅広く執筆しています。

Loading...