cssを使って画像を立体的に見せる方法
X(旧Twitter)で以下のものが流れてきて実装方法など気になったので、それを実装してみた内容になります。
A realistic 3D Card hover effect using only CSS 🤩
— T. Afif @ CSS Challenges (@ChallengesCss) 2023年5月9日
Demo: https://t.co/YFuQiTGH6r via @CodePen
✅ Minimal HTML
✅ Responsive
✅ Featuring the best video game ever
Inspired by @gggayane 👇#CSS https://t.co/2sZEwS09Rm pic.twitter.com/x2ALDjH4XA
実装を行ったもの
See the Pen hero-card by miwa_shuntaro (@miwashutaro0611) on CodePen.
<!-- @see https://codepen.io/t_afif/pen/mdzxJaa --> <figure class="hero"> <!-- NOTE: change image path --> <!-- https://assets.codepen.io/1480814/necro.png --> <img class="hero-image" src="https://placehold.jp/437x1000.png" alt="character image"> <figcaption class="hero-figcaption">character1</figcaption> </figure> <figure class="hero"> <img class="hero-image" src="https://placehold.jp/437x1000.png" alt="character image"> <figcaption class="hero-figcaption">character2</figcaption> </figure>
今回CSSについては「CSS nesting」を使用しているため、FireFoxでは動かない可能性があります。 (Mac & Chromeでは動作することを確認済みです。)
/* @see https://codepen.io/t_afif/pen/mdzxJaa */ /* reset css */ *, *::before, *::after { margin: 0; padding: 0; } body { width: 100%; min-height: 100vh; display: grid; grid-auto-flow: column; grid-auto-columns: min(230px,35vmin); place-content: end center; gap: 50px; } /* content css */ :root { --time: .5s } .hero { width: 100%; aspect-ratio: 1; margin: 0 0 60px; padding: 5px 20px 0; display: grid; grid-template-rows: 100%; cursor: pointer; position: relative; filter: drop-shadow(0 0 20px rgb(0 0 0/50%)); &::before { content: ""; position: absolute; z-index: -1; inset: 0; background: top/cover; transform-origin: bottom; filter: brightness(.9); transition: var(--time); background-image: url('https://placehold.jp/97e605/ffffff/680x980.png') } &:hover::before { filter: brightness(.3); transform: perspective(500px) rotateX(60deg); } } .hero-image { grid-area: 1/1; width: 100%; height: 100%; object-fit: cover; object-position: top; filter: contrast(.8) brightness(.7); place-self: end center; transition: var(--time); .hero:hover & { width: 130%; height: 255%; filter: contrast(1); } } .hero-figcaption { grid-area: 1/1; width: calc(100% + 40px); color: #fff; font-size: min(32px,5vmin); text-align: center; place-self: end center; transform: perspective(500px) translateY(100%) rotateX(-90deg); backface-visibility: hidden; transform-origin: top; background: #000; transition: var(--time); .hero:hover & { transform: perspective(500px)translateY(100%) rotateX(-30deg); } }
制作手順
今回使用している画像としては以下になります。
1. HTMLの作成
キャラクター用の画像
キャラクターの背景にある画像
<figure class="hero"> <!-- NOTE: change image path --> <img class="hero-image" src="https://placehold.jp/437x1000.png" alt="character image"> <figcaption class="hero-figcaption">character1</figcaption> </figure>
こちらは通常の画像で使うHTMLと同じになります。
画像については、ホバー後の全体のものを使用したいため、縦長の画像を使用しています。
今回使用している画像サイズについては437x1000
のものになります。
2. 背景画像部分の表示
- 画像の縦横比を1:1に変更
- 影を使用して立体感の演出
- 画像を少しだけ暗くする
画像の縦横比を1:1に変更
要素に対して、aspect-ratio
を設置することで横幅を設定するだけで高さも比率に合わせて調整してくれます。
影を使用して立体感の演出
画像の部分に影を適応させていきたいので、drop-shadow
を使用しています。
(今回検証で制作したものについては透過されていない画像なので、box-shadow
でも問題はないと思います。)
box-shadowとdrop-shadowの違いについては以下の記事が参考になります。
画像を少しだけ暗くする
色味についても、少し落とした感じにしたいため、brightness
を使用しています。
今回のcssの内容について
.hero { width: 100%; aspect-ratio: 1; margin: 0 0 60px; padding: 5px 20px 0; display: grid; grid-template-rows: 100%; cursor: pointer; position: relative; filter: drop-shadow(0 0 20px rgb(0 0 0/50%)); &::before { content: ""; position: absolute; z-index: -1; inset: 0; background: top/cover; transform-origin: bottom; filter: brightness(.9); transition: var(--time); background-image: url('https://placehold.jp/97e605/ffffff/680x980.png') } }
3. img
タグで表示した画像の表示エリアの調整
- gridのエリアに縦1・横1の割合で配置を行う
img
タグで配置した画像のサイズ・配置箇所の調整- コントラスト・明るさの調整
gridのエリアに縦1・横1の割合で配置を行う
親要素で指定したgrid
の要素に対してどの場所に配置を行うか指定を行う際に使用します。
今回の場合、親要素のgrid
全体に対して表示を行うため、1/1
のように記載しています。
img
タグで配置した画像のサイズ・配置箇所の調整
img
タグに対して、以下を使用することで
object-fit: コンテンツと画像の比率が合わない場合、全体を見えるように・横幅いっぱいなどの指定を行うことができます。 object-position: 画像の配置する場所を配置場所を指定することができます。
コントラストの調整
画像のコントラストを調整することができます。
今回のcssの内容について
.hero-image { grid-area: 1/1; width: 100%; height: 100%; object-fit: cover; object-position: top; filter: contrast(.8) brightness(.7); place-self: end center; transition: var(--time); }
4. caption
タグのスタイルの調整
- gridのエリアに縦1・横1の割合で配置を行う(イメージと同じ箇所に配置を行う)
- コンテンツの位置調整
- 奥行きの設定
- コンテンツの裏側を見せないようにする
gridのエリアに縦1・横1の割合で配置を行う(イメージと同じ箇所に配置を行う)
親要素で指定したgrid
の要素に対してどの場所に配置を行うか指定を行う際に使用します。
今回の場合、親要素のgrid
全体に対して表示を行うため、1/1
のように記載しています。
girdでの表示の場合、同じエリアに配置することでposition: absolute
と同じようなことを行うことができます。
詳細については以下の記事を参考にしてください。
コンテンツの位置調整
gridの中で要素をどの位置に表示を行うか指定することができます。
奥行きの設定
perspective
を追加すると3次元での表現を行うことができます。
コンテンツの裏側を見せないようにする
要素が立体的な時にbackface-visibility: hidden;
を指定することで裏側にある要素を隠すことができます。
今回のcssの内容について
.hero-figcaption { grid-area: 1/1; width: calc(100% + 40px); color: #fff; font-size: min(32px,5vmin); text-align: center; place-self: end center; transform: perspective(500px) translateY(100%) rotateX(-90deg); backface-visibility: hidden; transform-origin: top; background: #000; transition: var(--time); }
5. 2から4で作成したものに対して、ホバーのインタラクションをつける
設定した各種各種内容について
hero全体で行っていることについて
背景画像用として作成して擬似要素の奥行きを追加し、要素の回転を行う
heroの画像で行っていることについて
コントラストを通常に戻して、元のサイズの画像の表示を行う
heroのキャプションで行っていることについて
位置を移動させて、要素を回転させる
今回のcssの内容について
.hero { &:hover::before { filter: brightness(.3); transform: perspective(500px) rotateX(60deg); } } .hero-image { .hero:hover & { width: 130%; height: 255%; filter: contrast(1); } } .hero-figcaption { .hero:hover & { transform: perspective(500px) translateY(100%) rotateX(-30deg); } }