画像をCSSでトリミングする様々な手法まとめ
このページは約18分で読み終わります
後で読む
とりあえず画像を切り抜きたい。トリミングしたい。どうすれば?
CSS2.1の2011年頃までは、画像を切り抜く表現であればPhotoshopなどで生成した画像を使い表現をしていましたが、CSS3では様々な方法で切り抜いた表現がCSS側で行えるようになりました。ここでは主だった手法を整理したいと思います。
border-radiusを使う
CSS3から追加されたプロパティで、画像を角丸にトリミングをします。対応ブラウザはIE9〜、モダンブラウザ(Chrome / FireFox / Safari) など主だったものに対応をしています。詳細
長さの指定にはpx・em・pt・%などが利用できます。
画像を角丸とし「円」「楕円」で表現したい際に有効です。画像でも利用できますが、ブロック要素(divなど)に利用することもできます。
元画像が以下のような長方形であれば
border-radiusを使えば、以下のような図形を容易に描写できます。
適用する対象として画像のimgタグ、またはブロックタグなどに対し、以下3つのいずれかのようにCSSに記載をします。
<style>
/* ① 左上 右上 右下 左下 の時計回りで記述*/
border-radius: 10px 20px 30px 40px;
/* ② 同一の値の場合は一括で記述 */
border-radius: 50%;
/* ③ 水平方向から①同様時計回り、そして垂直方向に①同様時計回り */
border-radius: 200px 100px 50px 30px / 180px 150px 70px 30px;
</style>
①では四隅のサイズを異なった値として表現したい場合に利用し、②では一括で同一の値を適用したい場合に利用します。
③が少し複雑ですが、以下図のようにまずは水平方向(赤文字)の左上、右上、右下、左下の順に記載をし、スラッシュを挟んだ後垂直方向(黄色文字)の左上、右上、右下、左下の順に記載をすることが可能です。
あまり普段使わないかもしれませんが、少しシンプルなエンブレムのような歪な形を表現したい際に利用できます。「8 Point full control」という自動生成サービスもあります。
一部の角のみ適用する場合
単一の角だけ適用したい場合もあるかと思います。例えば左上と右下のみ適用すると下記のような表現も可能です。
この場合以下のように記述をします。
<style>
.circle {
border-top-left-radius: 100px;
border-bottom-right-radius: 100px;
}
</style>
marginを一括指定するか、個別指定(margin-left)するかと似ていますね。四隅の書き方ですが、左上であればtop-left(日本語と逆)のように下記のように記述をします。
<style>
/* 四隅を個別に記載した例 */
.circle {
border-top-left-radius: 100px;
border-top-right-radius: 100px;
border-bottom-right-radius: 100px;
border-bottom-left-radius: 100px;
}
/* 上記は以下と同じ意味です */
.circle {
border-radius: 100px;
}
</style>
実用例
サイトの中で使うタイミングとして一番多そうなのが記事のサムネイルを並べるタイミングなどがありそうですね。リンク画像であれば、img要素にopacityで透過したり、ホバーで画像拡大(transformでscale利用)する際は背景画像として扱うなどするのでそのラッパーとなるブロック要素側にborder-radiusを適用すると良さそうですね。
object-fitを使う
先のborder-radiusでは画像やブロック要素の四隅を削り角丸にすることは可能でしたが、縦横比は変えることができず、例えば長方形の画像を正方形にして横長の画像の左右をトリミングするといったことは行なえません。
object-fitは下記のように画像のトリミング処理が行えます。(※透過部分が非表示となるエリア)
oject-fitもCSS3から追加されたプロパティで、画像のトリミングを行うことができます。対応ブラウザはIEがIE11含め非対応で、Edgeを含めたその他のモダンブラウザ(Chrome / FireFox / Safari) などが対応しています。詳細
IEに対応させるにはJavaScriptの利用が必要で、注意が必要となります。(こちらは後述します。)
border-raidusプロパティでは値に数値を入れていましたが、object-fitでは5つの値(fill・contain・cover・none・scale-down)が用意されています。対象としてimg要素以外にもvideo要素(動画)にも対応しています。
実際トリミングとして利用するには「cover」の利用頻度が最も高いと思いますが、それぞれ順に確認をしていきます。 (※ 以下画像はデバッグツールでのプレビューのため透過していますが、実際には透過していません。)
1. fill
規定したボックスサイズ(300×300px)に合わせ、縦横比は維持せずリサイズして表示。
<style>
img {
width: 300px;
height: 300px;
}
.fill {
object-fit: fill;
}
</style>
<p><img src="./photo-thumb.jpg" class="fill"></p>
2. contain
規定したボックスサイズ(300×300px)に合わせ、縦横比を維持しながらリサイズ表示。今回横長の画像のため、上下余白となりますが、縦長の場合左右余白となります。
<style>
img {
width: 300px;
height: 300px;
}
.contain {
object-fit: contain;
}
</style>
<p><img src="./photo-thumb.jpg" class="contain"></p>
3. cover
規定したボックスサイズ(300×300px)に合わせ、リサイズ&トリミング表示。
<style>
img {
width: 300px;
height: 300px;
}
.cover {
object-fit: cover;
}
</style>
<p><img src="./photo-thumb.jpg" class="cover"></p>
4. none
リサイズをせず、規定したボックスサイズ(300×300px)に合わせトリミング表示。
<style>
img {
width: 300px;
height: 300px;
}
.none {
object-fit: none;
}
</style>
<p><img src="./photo-thumb.jpg" class="none"></p>
5. scale-down
規定したボックスサイズ(300×300px)に合わせ、縦横比を維持しながらリサイズして表示。containとの違いは、縮小はするものの拡大はしない為、今回でいうと300×300px以下の画像の際はそのまま原寸表示。
<style>
img {
width: 300px;
height: 300px;
}
.scale-down {
object-fit: scale-down;
}
</style>
<p><img src="./photo-thumb.jpg" class="scale-down"></p>
続いてはIEの対応について確認をしていきます。
object-fitをIEに対応させる
IEは前述の通りobject-fitが対応しておらず崩れて表示されてしまいます。
対応させるにはいくつかの手法があるようです。
・JavaScriptライブラリ「object-fit-images」を利用 (jQuery依存)
・JavaScriptライブラリ「fitie.js」を利用 (jQuery依存)
・jQuery非依存のJavaScriptフォールバックコード (参照元記事 )
・CSSのみで対応した例
手軽に導入するにはfitie.jsが楽ですが、特定状況下で不具合が発生する (他が動作しない)などの報告もあるので適宜決められると良さそうですね。
実用例
object-fitの特性上、初期構築後のCMSなど、ユーザー側で任意の画像がアップロードされる個々のサイズによしなにトリミング表示させられると便利です。利用するプロパティはcoverが多いかと思います。
以下は元々の画角がバラバラですが、自動で揃うよう表現できます。
またborder-radiusとの併用もできるので以下のようにもできます。
コードは下記となります。
<style>
div {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 1000px;
}
img {
width: 320px;
height: 320px;
object-fit: cover;
border-radius:30px;
}
</style>
<div>
<img src="./imgs/sample.jpg">
<img src="./imgs/sample2.jpg">
<img src="./imgs/sample3.jpg">
<img src="./imgs/sample4.jpg">
<img src="./imgs/sample5.jpg">
<img src="./imgs/sample6.jpg">
</div>
object-fitの注意点
ただこのobject-fit、一概にいい事ばかりではないかと思います。「リサイズ」が曲者で、もしサイズが大きい画像(5MB等)が10枚もあると計50MBもレンダリング時に読み込みが必要です。
一見、WordPressではver.5.5から画像にwidthとheightが付与された画像を対象に、自動的にlazy loadの遅延読み込みをするようになり、Chromeでもver.76からlazy loadを自動で処理してくれることになり、直接的には表示速度自体はそれほど遅いと感じないかもしれません。
一方で下までスクロールすると最終的には全てローディングをすることになり、ユーザーのパケット容量を使うことにも繋がる為、使い所は予め検討しておけるとよさそうです。
個人的には、WordPressのadd_image_sizeでカスタム画像を生成し、表示と近しい対象画像サイズ領域がAとBとCがある場合、同じ画像を利用の上でobject-fitと合わせることで効果的に活用できそうかと思います。
clip-pathを使う
角丸にトリミング(border-radius)、そして上下左右をトリミング(object-fit)に続き、clip-pathを使うと星型やひし形、五角形など自由な多角形にトリミングをすることができます。
CSS3から追加されたプロパティとなり、対応ブラウザはIEは11含め未対応、Edgeを含めたその他のモダンブラウザ(Chrome / FireFox / Safari) などが対応しています。但しSafariでは-webkit-のprefix付与が必要です。詳細 (IE11対応については後述します。)
記述は下記のように行います。
<style>
img {
-webkit-clip-path: polygon(25% 0%, 100% 0%, 75% 100%, 0% 100%);
clip-path: polygon(25% 0%, 100% 0%, 75% 100%, 0% 100%);
}
</style>
手軽に生成するにはジェネレーターのClippy (CSS clip-path maker)が便利です。
コードにある「polygon」の形状関数と呼ばれるシェイプの型には以下があります。
・inset:四角形
・circle:円
・ellipse:楕円
・polygon:多角形
これらは基本シェイプと呼ばれています。続いてはSVGのパスに沿い切り抜きをしていきます。
それでは順番に確認をしていきます。
1. inset
四角形に切り抜きをします。inset内は数値を1つ〜4つまで入れることができます。margin等と同じで1つのみであれば上下左右の切り抜きを指定、2つであれば上下・左右、3つであれば上・左右・下、4つであれば上・右・下・左の順に切り抜きを指定することとなります。
<style>
.inset {
-webkit-clip-path: inset(20px);
clip-path: inset(20px);
}
</style>
<img src="./photo.svg" class="inset">
上記は下記のように上下左右に20px切り抜きをしています。
2つの場合で、上下・左右を指定すると下記のようになりますね。(書き方は半角スペースを入れます)
<style>
.inset {
-webkit-clip-path: inset(20px 60px);
clip-path: inset(20px 60px);
}
</style>
<img src="./photo.svg" class="inset">
2. circle
正円に切り抜きをします。書き方は「半径 at 中心点のx座標 中心点のy座標」となり少しややこしいですが、対象画像が500×300pxであれば、半径がまず100px(円の直径が200px)、そして中心のx座標が50%(つまり横軸真ん中を起点)、y座標が50%(つまり縦軸真ん中を起点)として書くと下記のようになります。
<style>
.circle {
-webkit-clip-path: circle(100px at 50% 50%);
clip-path: circle(100px at 50% 50%);
}
</style>
<img src="./photo.svg" class="circle">
3. ellipse
ellipse(エリプス)は楕円を意味し、楕円の形に切り抜きをします。書き方はcircleと似ており、「x軸半径 y軸半径 at 中心点のx座標 中心点のy座標」となります。対象画像が500×300pxであれば、x軸半径を20%とすると半径100px相当、y軸半径を40%とすると120px相当となるので下記のようになります。
<style>
.ellipse {
width: 500px;
height: 300px;
clip-path: ellipse(20% 40% at 50% 50%);
}
</style>
<img src="./photo.svg" class="ellipse">
4. polygon
polygonは多角形を意味し、四角形、正円、楕円以外の図形で描写をする際に利用します。三角形や五角形などの基本図形以外にも自由な形で切り抜きをすることができます。
polygonでは点の座標軸をx軸・y軸で記述し、左上をx軸0・y軸0とし、右下をx軸100%・y軸100%として書きます。x軸y軸はそれぞれ半角スペースで空けて書き、1点を「0% 20%」のように書きます。
例えば、囲う点の数が3点(つまり三角形)であれば、「50% 0%, 0% 100%, 100% 100%」のように記述をすることで3点をプロットし三角形を生成して切り抜きをすることができます。
以下は矢印の形に切り抜きをしており、7点となるので下記のようになります。
<style>
.polygon {
width: 500px;
height: 300px;
clip-path: polygon(0% 20%, 50% 20%, 50% 0%, 100% 50%, 50% 100%, 50% 80%, 0% 80%)
}
</style>
<img src="./pohot.svg" class="polygon">
5. SVGパスによる切り抜き
① 最近はデザインをXDで行うケースが増えていますが、XDからマスク処理したいパスをsvg取得するには、該当のパスファイルを選択の上、ファイル > 書き出し > 選択したオブジェクト から取得を行います。
※ 試しにXDから取得したtwitterロゴを形どったsvgファイルを元にマスク処理をしていきます。(こちらの記事を参考にIEに対応しているやり方で進めていきます。)
② まずはSVGファイルをエディタで開きます。(このSVGファイルはマスク用として利用します)
<svg xmlns="http://www.w3.org/2000/svg" width="362" height="294" viewBox="0 0 362 294">
<path id="パス_934" data-name="パス 934" d="M152.934,296.391c137.113,0,211.719-112.917,211.719-211.719V74.59c14.115-10.082,26.213-24.2,36.295-38.311a167.386,167.386,0,0,1-42.344,12.1c16.131-10.082,26.213-24.2,32.262-40.328A184.947,184.947,0,0,1,344.489,26.2C330.375,10.066,310.211,2,290.047,2c-40.328,0-74.606,34.278-74.606,74.606,0,6.049,0,12.1,2.016,16.131C154.95,88.7,100.508,60.475,64.213,14.1c-6.049,10.082-10.082,24.2-10.082,38.311,0,26.213,14.115,48.393,32.262,62.508-12.1,0-24.2-4.033-34.278-10.082h0a73.692,73.692,0,0,0,60.491,72.59c-6.049,2.016-12.1,2.016-20.164,2.016-4.033,0-10.082,0-14.115-2.016,10.082,30.246,36.295,50.409,70.573,52.426-26.213,20.164-58.475,32.262-92.753,32.262-6.049,0-12.1,0-18.147-2.016,32.262,24.2,72.59,36.295,114.934,36.295" transform="translate(-38 -2)" fill="#1da1f2" fill-rule="evenodd"/>
</svg>
③ マスク側から不要なタグを消していきます。この際、svgタグに記述されているwidthとheight、viewBoxは後ほど使うのでコピーしておきます。削除すると以下のようになります。
<svg>
<path d="M152.934,296.391c137.113,0,211.719-112.917,211.719-211.719V74.59c14.115-10.082,26.213-24.2,36.295-38.311a167.386,167.386,0,0,1-42.344,12.1c16.131-10.082,26.213-24.2,32.262-40.328A184.947,184.947,0,0,1,344.489,26.2C330.375,10.066,310.211,2,290.047,2c-40.328,0-74.606,34.278-74.606,74.606,0,6.049,0,12.1,2.016,16.131C154.95,88.7,100.508,60.475,64.213,14.1c-6.049,10.082-10.082,24.2-10.082,38.311,0,26.213,14.115,48.393,32.262,62.508-12.1,0-24.2-4.033-34.278-10.082h0a73.692,73.692,0,0,0,60.491,72.59c-6.049,2.016-12.1,2.016-20.164,2.016-4.033,0-10.082,0-14.115-2.016,10.082,30.246,36.295,50.409,70.573,52.426-26.213,20.164-58.475,32.262-92.753,32.262-6.049,0-12.1,0-18.147-2.016,32.262,24.2,72.59,36.295,114.934,36.295" transform="translate(-38 -2)" fill="#1da1f2" fill-rule="evenodd"/>
</svg>
④ 同SVGファイルはマスク用のため、「width=“0” height=“0” style=“position: absolute; top: 0; left: 0;”」をsvgタグに付与します。(CSSを直接記述する形となるのでclass化してclass=”svg_mask”等に記載するのもよさそうです。
<svg width="0" height="0" style="position: absolute; top: 0; left: 0;">
<path d="M152.934,296.391c137.113,0,211.719-112.917,211.719-211.719V74.59c14.115-10.082,26.213-24.2,36.295-38.311a167.386,167.386,0,0,1-42.344,12.1c16.131-10.082,26.213-24.2,32.262-40.328A184.947,184.947,0,0,1,344.489,26.2C330.375,10.066,310.211,2,290.047,2c-40.328,0-74.606,34.278-74.606,74.606,0,6.049,0,12.1,2.016,16.131C154.95,88.7,100.508,60.475,64.213,14.1c-6.049,10.082-10.082,24.2-10.082,38.311,0,26.213,14.115,48.393,32.262,62.508-12.1,0-24.2-4.033-34.278-10.082h0a73.692,73.692,0,0,0,60.491,72.59c-6.049,2.016-12.1,2.016-20.164,2.016-4.033,0-10.082,0-14.115-2.016,10.082,30.246,36.295,50.409,70.573,52.426-26.213,20.164-58.475,32.262-92.753,32.262-6.049,0-12.1,0-18.147-2.016,32.262,24.2,72.59,36.295,114.934,36.295" transform="translate(-38 -2)" fill="#1da1f2" fill-rule="evenodd"/>
</svg>
⑤ clip-pathとして扱うためsvgタグ内に<clipPath>として囲み、またこの後マスク対象にしたい先と紐付けを行うためid名を付与します。
<svg width="0" height="0" style="position: absolute; top: 0; left: 0;">
<clipPath id="clip01">
<path d="M152.934,296.391c137.113,0,211.719-112.917,211.719-211.719V74.59c14.115-10.082,26.213-24.2,36.295-38.311a167.386,167.386,0,0,1-42.344,12.1c16.131-10.082,26.213-24.2,32.262-40.328A184.947,184.947,0,0,1,344.489,26.2C330.375,10.066,310.211,2,290.047,2c-40.328,0-74.606,34.278-74.606,74.606,0,6.049,0,12.1,2.016,16.131C154.95,88.7,100.508,60.475,64.213,14.1c-6.049,10.082-10.082,24.2-10.082,38.311,0,26.213,14.115,48.393,32.262,62.508-12.1,0-24.2-4.033-34.278-10.082h0a73.692,73.692,0,0,0,60.491,72.59c-6.049,2.016-12.1,2.016-20.164,2.016-4.033,0-10.082,0-14.115-2.016,10.082,30.246,36.295,50.409,70.573,52.426-26.213,20.164-58.475,32.262-92.753,32.262-6.049,0-12.1,0-18.147-2.016,32.262,24.2,72.59,36.295,114.934,36.295" transform="translate(-38 -2)" fill="#1da1f2" fill-rule="evenodd"/>
</clipPath>
</svg>
⑥ 続けてマスクしたい画像を準備します。
<svg>
<image xlink:href="./photo.svg" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" clip-path="url(#clip01)"/>
</svg>
⑦ この画像に先ほどコピーしておいた「width=”362″ height=”294″ viewBox=”0 0 362 294″」をsvgタグに適用します。
<svg width="362" height="294" viewBox="0 0 362 294">
<image xlink:href="./photo.svg" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" clip-path="url(#clip01)"/>
</svg>
以上で完成となります。以下のようになれば正常に動作をしています。
全体のコードは以下となります。
<svg width="0" height="0" style="position: absolute; top: 0; left: 0;">
<clipPath id="clip01">
<path d="M152.934,296.391c137.113,0,211.719-112.917,211.719-211.719V74.59c14.115-10.082,26.213-24.2,36.295-38.311a167.386,167.386,0,0,1-42.344,12.1c16.131-10.082,26.213-24.2,32.262-40.328A184.947,184.947,0,0,1,344.489,26.2C330.375,10.066,310.211,2,290.047,2c-40.328,0-74.606,34.278-74.606,74.606,0,6.049,0,12.1,2.016,16.131C154.95,88.7,100.508,60.475,64.213,14.1c-6.049,10.082-10.082,24.2-10.082,38.311,0,26.213,14.115,48.393,32.262,62.508-12.1,0-24.2-4.033-34.278-10.082h0a73.692,73.692,0,0,0,60.491,72.59c-6.049,2.016-12.1,2.016-20.164,2.016-4.033,0-10.082,0-14.115-2.016,10.082,30.246,36.295,50.409,70.573,52.426-26.213,20.164-58.475,32.262-92.753,32.262-6.049,0-12.1,0-18.147-2.016,32.262,24.2,72.59,36.295,114.934,36.295" transform="translate(-38 -2)" fill="#1da1f2" fill-rule="evenodd"/>
</clipPath>
</svg>
<svg width="362" height="294" viewBox="0 0 362 294">
<image xlink:href="./photo.svg" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" clip-path="url(#clip01)"/>
</svg>
ばっちりIEでも崩れずに動作していますね!
IE対応させる方法
通常の記述ではclip-pathはIEで未対応のため、対応するにはpolyfill (互換実装を提供するライブラリ)を利用します。使い方はシンプルですので、Githubリンク先のHow to useをご参考ください。
実用例
上記などのおしゃれな世界観や有機的な印象などに有効できそうですね。
またファーストビューで以下のような柔らかい印象を与えるのにも一役買います。
その他の方法
・background-size
・transfrom
による対応方法など。
適宜状況に応じて使い分けできると良さそうですね!