要素の移動や拡大、回転、さらには3Dの立体表現まで、Webサイトに多彩な動きを与えるtransformがあります。
本記事では、基本の2D・3D変形から複数指定、アニメーション連携時のトラブル解決まで解説します。
また、CSSに関するカテゴリーページから学びたい内容を決めたい人は、以下のCSSページをご確認ください。
transformとは:役割とtext-transformの違い
Webサイトに動きや立体感を持たせ、UIを構築するのに欠かせないのがtransformです。
transformとは、HTML要素の形状や位置を変化させるプロパティです。
transformの使い方をマスターすれば、JavaScriptを使わなくても、ホバー時の拡大アニメーションや絶対配置と組み合わせた中央寄せなど多彩な表現が可能です。
また、様々な値を指定すると、平面的な2Dや立体的な3Dの変形も可能です。
ここでは、要素自体を変形させるtransform、混同してしまう文字の変換プロパティtext-transformの違いを解説します。
- 要素を変形(移動・拡大・回転・傾斜)させるプロパティ
- 文字の大文字・小文字変換は
text-transform
要素を変形(移動・拡大・回転・傾斜)させるプロパティ
transformを扱う際、主に使われる値は以下の4つです。
translate():要素の位置を移動させる。scale():要素を拡大・縮小する。rotate():要素を回転させる。skew():要素を傾斜させる。
ボタンやテキストをtransformで変形・アニメーションさせたい場合は、対象の要素にdisplay: inline-block;またはdisplay: block;を指定してください。
また、複数の変形を同時に行いたい場合、transform: translate(10px) scale(1.2);のように半角スペース区切りで1行にまとめて記述します。
❌ 失敗:インライン要素(span)にはtransformが効かない
⭕️ 正解:inline-blockにすればtransformが効く
.wrong-span:hover {
transform: scale(1.2); /* 💡 無視される */
}
/* ⭕️ inline-blockにしてから変形させる */
.correct-span {
display: inline-block; /* 💡 これが必須! */
transition: transform 0.3s ease;
}
.correct-span:hover {
transform: scale(1.1) rotate(5deg); /* 💡 拡大と回転を同時に適用 */
}
HTMLコード表示
<div class="tf-sec1-wrapper">
<p class="tf-caption">❌ 失敗:インライン要素(span)にはtransformが効かない</p>
<div class="tf-box-wrong">
<span class="tf-inline-wrong">ホバーしても拡大しません</span>
</div>
<p class="tf-caption" style="margin-top: 30px; color:#198754;">⭕️ 正解:inline-blockにすればtransformが効く</p>
<div class="tf-box-correct">
<span class="tf-inline-correct">ホバーで拡大&回転します</span>
</div>
<div class="tf-code">
/* ❌ インラインのまま変形させようとする */<br>
<span class="hl-blue">.wrong-span:hover</span> {<br>
<span class="hl-red">transform: scale(1.2);</span> /* 💡 無視される */<br>
}<br><br>
/* ⭕️ inline-blockにしてから変形させる */<br>
<span class="hl-blue">.correct-span</span> {<br>
<span class="hl-green">display: inline-block;</span> /* 💡 これが必須! */<br>
<span class="hl-green">transition: transform 0.3s ease;</span><br>
}<br>
<span class="hl-blue">.correct-span:hover</span> {<br>
<span class="hl-red">transform: scale(1.1) rotate(5deg);</span> /* 💡 拡大と回転を同時に適用 */<br>
}
</div>
</div>CSSコード表示
.tf-sec1-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tf-caption {
font-size: 13px;
font-weight: bold;
margin-bottom: 10px;
color: #dc3545;
}
.tf-box-wrong, .tf-box-correct {
background-color: #e9ecef;
padding: 30px;
text-align: center;
border: 1px dashed #adb5bd;
border-radius: 4px;
}
/* ❌ 失敗例 */
.tf-inline-wrong {
background-color: #dc3545;
color: white;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
/* 💡 インラインのまま */
transition: transform 0.3s ease;
}
.tf-inline-wrong:hover {
/* 💡 効かない */
transform: scale(1.2);
}
/* ⭕️ 成功例 */
.tf-inline-correct {
background-color: #198754;
color: white;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
/* 💡 インラインブロックに変換 */
display: inline-block;
/* アニメーションを滑らかにする */
transition: transform 0.3s ease;
}
.tf-inline-correct:hover {
/* 💡 拡大しつつ少し回転させる */
transform: scale(1.2) rotate(5deg);
}
.tf-code {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
margin-top: 20px;
border-left: 4px solid #0d6efd;
}
.hl-blue {
color: #61afef;
font-weight: bold;
}
.hl-red {
color: #e06c75;
font-weight: bold;
}
.hl-green {
color: #98c379;
font-weight: bold;
}文字の大文字・小文字変換はtext-transform
CSSを学び始めの初心者がGoogle検索をする際、検索ワードを間違えやすいのがアルファベットの大文字・小文字変換です。
文字の大文字・小文字をCSSでコントロールしたい場合は、transformではなくtext-transformプロパティを使用するのが正解です。
HTML上ではhelloと小文字で入力されていても、画面上ではHELLOと大文字で表示されます。
実務では、HTMLのセマンティクス(意味合い)を保ったまま、デザイン上の理由で大文字にする場合に必須のプロパティです。
❌ 失敗:要素の変形(transform)に大文字変換を指定している
hello world (小文字のまま)
⭕️ 正解:文字の変換(text-transform)を正しく指定している
hello world (大文字に変換される)
.wrong-text {
transform: uppercase; /* 💡 エラーになり無視される */
}
/* ⭕️ 文字の変換は text-transform を使う */
.correct-text {
text-transform: uppercase; /* 💡 すべて大文字(HELLO WORLD)になる */
}
HTMLコード表示
<div class="tf-sec2-wrapper">
<p class="tf-caption">❌ 失敗:要素の変形(transform)に大文字変換を指定している</p>
<div class="txt-box-wrong">
<p class="txt-wrong-demo">hello world (小文字のまま)</p>
</div>
<p class="tf-caption" style="margin-top: 30px; color:#198754;">⭕️ 正解:文字の変換(text-transform)を正しく指定している</p>
<div class="txt-box-correct">
<p class="txt-correct-demo">hello world (大文字に変換される)</p>
</div>
<div class="tf-code">
/* ❌ プロパティ名を勘違いしている */<br>
<span class="hl-blue">.wrong-text</span> {<br>
<span class="hl-red">transform: uppercase;</span> /* 💡 エラーになり無視される */<br>
}<br><br>
/* ⭕️ 文字の変換は text-transform を使う */<br>
<span class="hl-blue">.correct-text</span> {<br>
<span class="hl-red">text-transform: uppercase;</span> /* 💡 すべて大文字(HELLO WORLD)になる */<br>
}
</div>
</div>CSSコード表示
.tf-sec2-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.txt-box-wrong, .txt-box-correct {
background-color: #e9ecef;
padding: 15px;
border: 1px solid #ced4da;
border-radius: 4px;
}
/* ❌ 失敗例 */
.txt-wrong-demo {
margin: 0;
font-size: 20px;
font-weight: bold;
color: #dc3545;
/* 💡 存在しない値なので無視される */
transform: uppercase;
}
/* ⭕️ 成功例 */
.txt-correct-demo {
margin: 0;
font-size: 20px;
font-weight: bold;
color: #198754;
/* 💡 正しいプロパティで大文字に変換 */
text-transform: uppercase;
}2D変形関数の使い方(移動・拡大縮小・回転・傾斜)
CSSのtransformプロパティには、要素を平面的(2D)に変形・移動させる様々な「関数(機能)」が用意されています。
これらを使いこなすことで、要素を自由自在に配置したり、ユーザーの目を引くリッチなアニメーションを実装することが可能です。
ここでは、実務で使用される4つの2D変形関数(移動・拡大縮小・回転・傾斜)について、具体的な使い方を解説します。
- 要素を移動する
translate()と上下左右の中央寄せ - 要素を拡大・縮小する
scale() - 要素を回転させる
rotate()と反転の作り方 - 要素を歪ませる
skew()とmatrix()
要素を移動するtranslate()と上下左右の中央寄せ
要素を現在の位置から移動させる関数がtranslateです。
X軸(横方向)に動かす「translateX」、Y軸(縦方向)に動かす「translateY」、あるいは同時に指定する方法があります。
指定方法としては、transform: translateX(50px);、transform: translateY(50px);、あるいは transform: translate(50px, 100px);のように記述します。
(※補足:モダンブラウザではtransformプロパティを介さずに直接translate: 50px 100px;と書く独立プロパティも使用可能になっています。)
要素をアニメーションで移動させる場合は、GPUで高速処理されるtransform: translate()を使用してください。
また、positionのabsoluteと組み合わせて画面の中央に配置する際、top: 50%; left: 50%;だけだと右下にズレるため、自身のサイズの半分を戻すtransform: translate(-50%, -50%);をセットで記述するのがよいです。
⭕️ ホバーで上へ移動(marginとtranslateの決定的な違い)
(下の緑ボタンまで巻き込んで動く)
(周りに影響を与えず独立して動く)
.wrong-btn {
transition: margin-top 0.3s ease;
}
.wrong-btn:hover {
margin-top: 0; /* 💡 周りの要素まで引きずられて動いてしまう! */
}
/* ⭕️ プロの鉄則:アニメーションによる移動はtranslateを使う */
.correct-btn {
transition: transform 0.3s ease;
}
.correct-btn:hover {
transform: translateY(-20px); /* 💡 別のレイヤーとして自分だけ滑らかに動く! */
}
HTMLコード表示
<div class="tr-v3-wrapper">
<p class="tr-v3-caption">⭕️ ホバーで上へ移動(marginとtranslateの決定的な違い)</p>
<div class="tr-v3-demo-area">
<div class="tr-v3-btn-wrong">
❌ marginで移動<br>(下の緑ボタンまで巻き込んで動く)
</div>
<div class="tr-v3-btn-correct">
⭕️ translateで移動<br>(周りに影響を与えず独立して動く)
</div>
</div>
<div class="tr-v3-code">
/* ❌ marginを変えると、周りのレイアウトを巻き込んで再計算される */<br>
<span class="hl-blue">.wrong-btn</span> {<br>
<span class="hl-green">transition: margin-top 0.3s ease;</span><br>
}<br>
<span class="hl-blue">.wrong-btn:hover</span> {<br>
<span class="hl-red">margin-top: 0;</span> /* 💡 周りの要素まで引きずられて動いてしまう! */<br>
}<br><br>
/* ⭕️ アニメーションによる移動はtranslateを使う */<br>
<span class="hl-blue">.correct-btn</span> {<br>
<span class="hl-green">transition: transform 0.3s ease;</span><br>
}<br>
<span class="hl-blue">.correct-btn:hover</span> {<br>
<span class="hl-red">transform: translateY(-20px);</span> /* 💡 別のレイヤーとして自分だけ滑らかに動く! */<br>
}
</div>
</div>CSSコード表示
.tr-v3-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tr-v3-caption {
font-size: 13px;
font-weight: bold;
margin-bottom: 15px;
color: #198754;
text-align: center;
}
.tr-v3-demo-area {
background-color: #e9ecef;
border: 1px dashed #adb5bd;
border-radius: 4px;
margin-bottom: 20px;
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
}
/* 💡 ボタンの共通デザイン */
.tr-v3-btn-wrong, .tr-v3-btn-correct {
padding: 15px 30px;
border-radius: 50px;
color: white;
font-weight: bold;
text-align: center;
cursor: pointer;
width: 90%;
max-width: 350px;
line-height: 1.5;
}
/* ❌ 失敗例 */
.tr-v3-btn-wrong {
background-color: #dc3545;
/* 意図的に上に20pxの余白を持たせておく */
margin-top: 20px !important;
margin-bottom: 20px !important;
transition: margin-top 0.3s ease !important;
}
.tr-v3-btn-wrong:hover {
/* ホバーで余白を0にすることで、上に20px移動(下の要素も巻き込む) */
margin-top: 0px !important;
}
/* ⭕️ 成功例(translateによる移動:独立して動く) */
.tr-v3-btn-correct {
background-color: #198754;
margin-top: 0 !important;
margin-bottom: 0 !important;
transform: translateY(0) !important;
transition: transform 0.3s ease !important;
}
.tr-v3-btn-correct:hover {
/* 自分だけが上に20pxスライドする */
transform: translateY(-20px) !important;
}
.tr-v3-code {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
}
.hl-blue {
color: #61afef;
font-weight: bold;
}
.hl-red {
color: #e06c75;
font-weight: bold;
}
.hl-green {
color: #98c379;
font-weight: bold;
}positionプロパティの使い方を詳しく知りたい人は「【html&css】positionの使い方とabsolute・fixed・relativeの使い分け」を一読ください。
要素を拡大・縮小するscale()
要素のサイズを拡大・縮小したい時に使うのがscaleです。 標準のサイズscale(1)を基準とし、scale(1.2)なら1.2倍に拡大、scale(0.8)なら0.8倍に縮小します。
横幅だけを変える「scaleX」や、縦幅だけの「scaleY」もあります。
要素を拡大縮小する際はtransform: scale()を使用してください。
レイアウトの領域を変えずに、その場で見た目だけを拡大・縮小できます。
例えば、カード内のサムネイル画像をホバーでズーム(拡大)させる演出は、親要素にoverflow: hidden;をかけ、中の画像にtransform: scale(1.1);を適用するのがよいです。
⭕️ ホバーで画像をズームさせる(カードUIの定番)
News Title
.image-wrapper {
overflow: hidden;
}
/* 💡 画像本体の基本設定 */
.image {
width: 100%;
transform: scale(1); /* 💡 基本は等倍 */
transition: transform 0.4s ease;
}
/* ⭕️ 親要素をホバーした時に中の画像を拡大する */
.card:hover .image {
transform: scale(1.1); /* 💡 レイアウトを崩さずに1.1倍へ拡大 */
}
HTMLコード表示
<div class="tr-sec2-wrapper">
<p class="tr-caption">⭕️ ホバーで画像をズームさせる(カードUIの定番)</p>
<div class="scale-card">
<div class="scale-img-wrap">
<img src="https://images.unsplash.com/photo-1472214103451-9374bd1c798e?auto=format&fit=crop&w=400&q=80" alt="風景" class="scale-img">
</div>
<div class="scale-body">
<p style="margin:0; font-weight:bold;">News Title</p>
</div>
</div>
<div class="tr-code">
/* 💡 画像を囲む親要素ではみ出しを隠す */<br>
<span class="hl-blue">.image-wrapper</span> {<br>
<span class="hl-red">overflow: hidden;</span><br>
}<br><br>
/* 💡 画像本体の基本設定 */<br>
<span class="hl-blue">.image</span> {<br>
<span class="hl-green">width: 100%;</span><br>
<span class="hl-red">transform: scale(1);</span> /* 💡 基本は等倍 */<br>
<span class="hl-green">transition: transform 0.4s ease;</span><br>
}<br><br>
/* ⭕️ 親要素をホバーした時に中の画像を拡大する */<br>
<span class="hl-blue">.card:hover .image</span> {<br>
<span class="hl-red">transform: scale(1.1);</span> /* 💡 レイアウトを崩さずに1.1倍へ拡大 */<br>
}
</div>
</div>CSSコード表示
.tr-sec2-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
display: flex;
flex-direction: column;
align-items: center;
}
.tr-caption {
font-size: 13px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
}
.scale-card {
width: 250px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
/* 💡 カーソルをポインターに */
cursor: pointer;
/* 💡 カード全体のはみ出しをカット */
overflow: hidden;
}
.scale-img-wrap {
width: 100%;
height: 150px;
/* 💡 画像のはみ出しをカット */
overflow: hidden;
}
.scale-img {
width: 100%;
height: 100%;
object-fit: cover;
/* 💡 等倍設定となめらかなアニメーション */
transform: scale(1);
transition: transform 0.4s ease;
}
/* ⭕️ カード全体がホバーされたら、中の画像を拡大する */
.scale-card:hover .scale-img {
transform: scale(1.1);
}
.scale-body {
padding: 15px;
text-align: center;
}
.tr-code {
width: 100%;
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
margin-top: 20px;
border-left: 4px solid #0d6efd;
}要素を回転させるrotate()と反転の作り方
要素をクルッと回転させるrotateは、アコーディオンメニューの矢印アイコン(▼を▲にする等)などで使用されます。
少しだけ傾けるrotate(45deg)や直角に倒すrotate(90deg)、上下逆さまにするrotate(180deg)など、角度(deg)を指定して回転させます。
左右を鏡のように反転させたい場合は、回転ではなく、拡大縮小のマイナス値であるtransform: scaleX(-1);を使用してください。
上下反転の場合はtransform: scaleY(-1);です。
⭕️ rotate(180deg)
になる
⭕️ scaleX(-1) で鏡反転
鏡のように反転
.rotate-180 {
transform: rotate(180deg);
}
/* ⭕️ 左右反転(ミラー)は scaleX のマイナスを使う */
.flip-horizontal {
transform: scaleX(-1); /* 💡 X軸をマイナス1倍にする=左右反転 */
}
HTMLコード表示
<div class="tr-sec3-wrapper">
<div class="rotate-demo-flex">
<div class="rotate-box">
<p class="tr-caption">⭕️ rotate(180deg)</p>
<div class="rotate-element is-rotate">上下逆さま<br>になる</div>
</div>
<div class="rotate-box">
<p class="tr-caption" style="color:#0d6efd;">⭕️ scaleX(-1) で鏡反転</p>
<div class="rotate-element is-flip">左右だけが<br>鏡のように反転</div>
</div>
</div>
<div class="tr-code">
/* 💡 要素を180度回転させる(上下逆さまになる) */<br>
<span class="hl-blue">.rotate-180</span> {<br>
<span class="hl-red">transform: rotate(180deg);</span><br>
}<br><br>
/* ⭕️ 左右反転(ミラー)は scaleX のマイナスを使う */<br>
<span class="hl-blue">.flip-horizontal</span> {<br>
<span class="hl-red">transform: scaleX(-1);</span> /* 💡 X軸をマイナス1倍にする=左右反転 */<br>
}
</div>
</div>CSSコード表示
.tr-sec3-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.rotate-demo-flex {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.rotate-box {
flex: 1;
background-color: #e9ecef;
padding: 20px;
border: 1px dashed #adb5bd;
border-radius: 4px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.rotate-element {
background-color: #ffc107;
color: #333;
width: 120px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 14px;
line-height: 1.4;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.5s ease;
}
/* 💡 180度回転 */
.rotate-box:hover .is-rotate {
transform: rotate(180deg);
}
/* 💡 左右反転(ミラー) */
.rotate-box:hover .is-flip {
transform: scaleX(-1);
}要素を歪ませるskew()とmatrix()
スピード感のある斜めのデザインを作る際に使用するのが、要素を歪ませるskewです。
また、これまで紹介したtranslate、scale、rotate、skewなどの全ての変形を数学的な1つの行列関数としてまとめて記述できるmatrixというプロパティも存在します。
親要素をskewで歪ませた場合、中にある子要素には「逆の角度」を指定したskewをかけ直して、まっすぐに戻すのがよいです。
なお、matrix(a, b, c, d, e, f)はJavaScriptからCSSを動的に生成する際などに使われますが、人間が見て直感的に理解できないため、手書きのCSSで記述するのは保守性の観点から避けるべきです。
⭕️ 親要素を歪ませつつ、子要素をまっすぐに戻すテクニック
テキストは歪みません
.skew-section {
transform: skewY(-5deg); /* 💡 Y軸方向に-5度歪ませる */
}
/* ⭕️ 子要素には逆の角度を入れて相殺する */
.skew-content {
transform: skewY(5deg); /* 💡 +5度でまっすぐに戻す(重要) */
}
HTMLコード表示
<div class="tr-sec4-wrapper">
<p class="tr-caption">⭕️ 親要素を歪ませつつ、子要素をまっすぐに戻すテクニック</p>
<div class="skew-section">
<div class="skew-content">
斜めの背景でも<br>テキストは歪みません
</div>
</div>
<div class="tr-code">
/* 💡 親要素(背景)を斜めに傾斜させる */<br>
<span class="hl-blue">.skew-section</span> {<br>
<span class="hl-red">transform: skewY(-5deg);</span> /* 💡 Y軸方向に-5度歪ませる */<br>
}<br><br>
/* ⭕️ 子要素には逆の角度を入れて相殺する */<br>
<span class="hl-blue">.skew-content</span> {<br>
<span class="hl-red">transform: skewY(5deg);</span> /* 💡 +5度でまっすぐに戻す(重要) */<br>
}
</div>
</div>CSSコード表示
.tr-sec4-wrapper {
background-color: #f8f9fa;
padding: 40px 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
overflow: hidden; /* はみ出し防止 */
}
/* 💡 親要素を斜めにカット */
.skew-section {
background: linear-gradient(135deg, #6f42c1, #0d6efd);
padding: 40px 20px;
/* Y軸方向に-5度傾斜 */
transform: skewY(-5deg);
border-radius: 8px;
}
/* 💡 子要素をまっすぐに戻す */
.skew-content {
color: white;
font-size: 18px;
font-weight: bold;
text-align: center;
/* 逆方向(+5度)に傾斜させて相殺する */
transform: skewY(5deg);
}transformの複数指定と基準点
CSSのtransformを使いこなす上で、重要なのが「複数の動きを同時に実行したい時」と「変形する中心点がズレてしまう時」です。
単純に移動するだけ、拡大するだけなら簡単ですが、実務では「移動しながら回転させる」「右下を起点にして拡大させる」といった複合的な表現が求められます。
ここでは、複数の変形をコントロールする記述方法とアニメーションの要となる「基準点(アンカー)」の操作方法を解説します。
- 複数関数を同時に組み合わせる方法
transform-originで回転や拡大の基準点を変える
複数関数を同時に組み合わせる方法
実務では、ホバー時に要素を「上に浮かせながら少し大きくする」や「移動させつつ回転させる」といったように、複数の関数を同時に組み合わせることがあります。
複数の変形を同時に行いたい場合は、1つのtransformの中に半角スペース区切りで記述してください。
また、記述する「順番」も極めて重要です。
「移動してから回転」と「回転してから移動」では、全く異なる結果になります。
rotateを先に書くと「移動する方向の座標軸自体が回転してしまう」ため、斜めの変な方向へ移動して制御不能に陥ります。
基本は「translate(移動)を先に書き、その後にscaleやrotateを書く」のがルールです。
⭕️ 複数指定はスペース区切り(移動+拡大)
移動&拡大
❌ 順番のミス:回転を先に書くと斜めに飛んでいく
.wrong-order {
transform: rotate(45deg) translateX(50px);
}
/* ⭕️ 移動を先に書いてから、その場で回転・拡大させる */
.correct-order {
transform: translateX(100px) rotate(45deg);
}
/* 💡 移動と拡大の組み合わせ */
.scale-translate {
transform: translateY(-10px) scale(1.1);
}
HTMLコード表示
<div class="tf-multi-wrapper">
<p class="tf-caption">⭕️ 複数指定はスペース区切り(移動+拡大)</p>
<div class="tf-multi-box1">
<div class="tf-item is-scale-translate">ホバーで<br>移動&拡大</div>
</div>
<p class="tf-caption" style="margin-top: 30px;">❌ 順番のミス:回転を先に書くと斜めに飛んでいく</p>
<div class="tf-multi-box2">
<div class="tf-item is-rotate-first">rotate -> translate</div>
<div class="tf-item is-translate-first" style="background-color:#198754;">⭕️ translate -> rotate</div>
</div>
<div class="tf-code">
/* ❌ 回転を先に書くと、X軸が傾いて変な方向へ進む */<br>
<span class="hl-blue">.wrong-order</span> {<br>
<span class="hl-red">transform: rotate(45deg) translateX(50px);</span><br>
}<br><br>
/* ⭕️ 移動を先に書いてから、その場で回転・拡大させる */<br>
<span class="hl-blue">.correct-order</span> {<br>
<span class="hl-red">transform: translateX(100px) rotate(45deg);</span><br>
}<br><br>
/* 💡 移動と拡大の組み合わせ */<br>
<span class="hl-blue">.scale-translate</span> {<br>
<span class="hl-red">transform: translateY(-10px) scale(1.1);</span><br>
}
</div>
</div>CSSコード表示
.tf-multi-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tf-caption {
font-size: 13px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.tf-multi-box1, .tf-multi-box2 {
background-color: #e9ecef;
padding: 30px;
border-radius: 4px;
border: 1px dashed #adb5bd;
display: flex;
gap: 20px;
height: 150px;
}
.tf-item {
width: 120px;
height: 80px;
background-color: #0d6efd;
color: white;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-weight: bold;
font-size: 12px;
border-radius: 8px;
cursor: pointer;
transition: transform 0.4s ease;
}
/* 💡 移動と拡大の正しい組み合わせ */
.is-scale-translate:hover {
transform: translateY(-15px) scale(1.1);
}
/* ❌ 回転を先に書く(右へ移動させたいのに、45度傾いた右下方向へ進んでしまう) */
.is-rotate-first:hover {
transform: rotate(45deg) translateX(50px);
}
/* ⭕️ 移動を先に書く(右に移動してから、その場で45度回転する) */
.is-translate-first:hover {
transform: translateX(100px) rotate(45deg);
}
.tf-code {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
margin-top: 20px;
border-left: 4px solid #0d6efd;
}
.hl-blue {
color: #61afef;
font-weight: bold;
}
.hl-red {
color: #e06c75;
font-weight: bold;
}transform-originで回転や拡大の基準点を変える
要素を変形させる際、デフォルトでは「要素の中央」を基準にして拡大や回転が行われます。
しかし、ハンバーガーメニューの「バツ印」を作る場合や画像を「左上から右下へ」拡大させたい場合、基準点をずらす必要があります。
そこで登場するのが、基準点や中心をコントロールするtransform-originプロパティです。
変形の中心を変えたい場合は、transform-originを使用してください。
指定方法はtransform-origin: top left;(左上)やtransform-origin: right bottom;(右下)、またはパーセンテージでtransform-origin: 100% 100%;のようにX軸・Y軸の順で指定します。
これを変えるだけで、アニメーションの「見え方(起点)」が変化し、意図通りの動きを実現できます。
⭕️ 基準点(transform-origin)の違いによるアニメーションの変化
(デフォルト)
(左上起点)
(右下起点)
.box-center {
transform-origin: center;
transform: scale(1.2) rotate(10deg);
}
/* 💡 左上(0% 0%)を基準にして、右下へ向かって変形 */
.box-top-left {
transform-origin: top left;
transform: scale(1.2) rotate(10deg);
}
/* 💡 右下(100% 100%)を画鋲で刺したように変形 */
.box-bottom-right {
transform-origin: 100% 100%;
transform: scale(1.2) rotate(10deg);
}
HTMLコード表示
<div class="tfo-wrapper">
<p class="tf-caption">⭕️ 基準点(transform-origin)の違いによるアニメーションの変化</p>
<div class="tfo-container">
<div class="tfo-box">
<div class="tfo-origin-dot" style="top:50%; left:50%; transform:translate(-50%, -50%);"></div>
<div class="tfo-item tfo-center">center<br>(デフォルト)</div>
</div>
<div class="tfo-box">
<div class="tfo-origin-dot" style="top:0; left:0; transform:translate(-50%, -50%);"></div>
<div class="tfo-item tfo-top-left">top left<br>(左上起点)</div>
</div>
<div class="tfo-box">
<div class="tfo-origin-dot" style="bottom:0; right:0; transform:translate(50%, 50%);"></div>
<div class="tfo-item tfo-bottom-right">bottom right<br>(右下起点)</div>
</div>
</div>
<div class="tf-code">
/* 💡 デフォルト(指定なしと同じ:ど真ん中から変形) */<br>
<span class="hl-blue">.box-center</span> {<br>
<span class="hl-red">transform-origin: center;</span><br>
<span class="hl-green">transform: scale(1.2) rotate(10deg);</span><br>
}<br><br>
/* 💡 左上(0% 0%)を基準にして、右下へ向かって変形 */<br>
<span class="hl-blue">.box-top-left</span> {<br>
<span class="hl-red">transform-origin: top left;</span><br>
<span class="hl-green">transform: scale(1.2) rotate(10deg);</span><br>
}<br><br>
/* 💡 右下(100% 100%)を画鋲で刺したように変形 */<br>
<span class="hl-blue">.box-bottom-right</span> {<br>
<span class="hl-red">transform-origin: 100% 100%;</span><br>
<span class="hl-green">transform: scale(1.2) rotate(10deg);</span><br>
}
</div>
</div>CSSコード表示
.tfo-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tfo-container {
display: flex;
gap: 30px;
justify-content: center;
padding: 40px 20px;
background-color: #e9ecef;
border-radius: 4px;
border: 1px dashed #adb5bd;
}
@media (max-width: 600px) {
.tfo-container {
flex-direction: column;
align-items: center;
}
}
.tfo-box {
position: relative;
width: 100px;
height: 100px;
background-color: rgba(200, 200, 200, 0.3);
border: 1px solid #ced4da;
}
/* 基準点を表す黄色いドット */
.tfo-origin-dot {
position: absolute;
width: 10px;
height: 10px;
background-color: #ffc107;
border-radius: 50%;
z-index: 10;
box-shadow: 0 0 0 2px rgba(255,255,255,0.8);
}
.tfo-item {
width: 100%;
height: 100%;
background-color: #198754;
color: white;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-weight: bold;
font-size: 11px;
cursor: pointer;
transition: transform 0.4s ease;
}
/* 💡 デフォルトの中央 */
.tfo-center {
transform-origin: center;
}
/* 💡 左上が基準 */
.tfo-top-left {
transform-origin: top left;
}
/* 💡 右下が基準 */
.tfo-bottom-right {
transform-origin: right bottom;
}
/* 共通のホバーアクション */
.tfo-box:hover .tfo-item {
transform: scale(1.2) rotate(15deg);
}立体感・奥行きを作る!3D変形の基本
X軸(横)とY軸(縦)の平面的な動きに加え、Z軸(奥行き)の概念を取り入れることで、Webサイトに立体感をもたらすのが「3D」です。
カードがパタンと裏返るアニメーションや奥行きのあるダイナミックな表現は、Webデザインにおいて人気があります。
複雑な3Dマトリクスを手書きするのは難しいため、実務ではジェネレーターツールを使ってベースの数値を作成することも多いですが、根本的な「軸」と「遠近感」のルールを理解していないと、レイアウトに組み込むことはできません。
ここでは、3D変形の基本となる軸の考え方と「なぜか立体的に見えない」問題の解決策を解説します。
- X軸・Y軸・Z軸の3D回転と移動
- 遠近感とpreserve-3dの指定方法
X軸・Y軸・Z軸の3D回転と移動
3D変形を扱う際、重要になるのが「どの軸を基準に回すか」という概念です。
これらを組み合わせることによって、要素を立体的に動かします。
rotateX:鉄棒を軸にして前回りのように回転する動き。rotateY:縦の軸を中心に横回転する動き。rotateZ:ハンドルを回すように回転する動き(実は2Dのrotateと同じです)。translateZ:画面の手前(ユーザー側)に近づいたり、奥へ引っ込んだりする奥行きの移動。
まとめて指定する「translate3d」というプロパティもあります。
実務でパフォーマンス(描画速度)を向上させるテクニックとして、平面的な移動(2D)であっても、あえてtransform: translate3d(x, y, 0);やtranslateZ(0);を指定するテクニックがあります。
これにより、ブラウザに「これは3Dの処理だ」と認識させ、PCやスマホのGPUを強制的に使わせることができ、アニメーションのカクつきが解消されてなめらかになります。
⭕️ ホバーして、さまざまな3Dスピンを体験してください!
rotateX
(鉄棒・前回り)
rotateY
(ドア・横開き)
rotateZ
(ハンドル・風車)
rotate3d
(無重力スピン!)
.box-x:hover {
transform: rotateX(180deg);
}
/* 🚪 Y軸を基準に180度回転(裏返る・鏡文字になる) */
.box-y:hover {
transform: rotateY(180deg);
}
/* 🎡 Z軸を基準に180度回転(平面のまま回転する) */
.box-z:hover {
transform: rotateZ(180deg);
}
/* 🛸 X・Y・Z軸を同時に使って360度大回転させる */
.box-3d:hover {
transform: rotate3d(1, 1, 1, 360deg);
}
HTMLコード表示
<div class="tf3d-v2-wrapper">
<p class="tf3d-v2-caption">⭕️ ホバーして、さまざまな3Dスピンを体験してください!</p>
<div class="tf3d-v2-axes-container">
<div class="tf3d-v2-axis-box">
<p class="tf3d-v2-axis-title">rotateX<br><small>(鉄棒・前回り)</small></p>
<div class="tf3d-v2-element is-rotateX">
<span class="tf3d-v2-emoji">🤸♂️</span>
X軸回転
</div>
</div>
<div class="tf3d-v2-axis-box">
<p class="tf3d-v2-axis-title">rotateY<br><small>(ドア・横開き)</small></p>
<div class="tf3d-v2-element is-rotateY" style="background-color: #198754;">
<span class="tf3d-v2-emoji">🚪</span>
Y軸回転
</div>
</div>
<div class="tf3d-v2-axis-box">
<p class="tf3d-v2-axis-title">rotateZ<br><small>(ハンドル・風車)</small></p>
<div class="tf3d-v2-element is-rotateZ" style="background-color: #dc3545;">
<span class="tf3d-v2-emoji">🎡</span>
Z軸回転
</div>
</div>
<div class="tf3d-v2-axis-box">
<p class="tf3d-v2-axis-title">rotate3d<br><small>(無重力スピン!)</small></p>
<div class="tf3d-v2-element is-rotate3d" style="background-color: #6f42c1;">
<span class="tf3d-v2-emoji">🛸</span>
全軸スピン
</div>
</div>
</div>
<div class="tf3d-v2-code">
/* 🤸♂️ X軸を基準に180度回転(上下逆さまになる) */<br>
<span class="hl-blue">.box-x:hover</span> {<br>
<span class="hl-red">transform: rotateX(180deg);</span><br>
}<br><br>
/* 🚪 Y軸を基準に180度回転(裏返る・鏡文字になる) */<br>
<span class="hl-blue">.box-y:hover</span> {<br>
<span class="hl-red">transform: rotateY(180deg);</span><br>
}<br><br>
/* 🎡 Z軸を基準に180度回転(平面のまま回転する) */<br>
<span class="hl-blue">.box-z:hover</span> {<br>
<span class="hl-red">transform: rotateZ(180deg);</span><br>
}<br><br>
/* 🛸 X・Y・Z軸を同時に使って360度大回転させる */<br>
<span class="hl-blue">.box-3d:hover</span> {<br>
<span class="hl-red">transform: rotate3d(1, 1, 1, 360deg);</span><br>
}
</div>
</div>CSSコード表示
.tf3d-v2-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
/* 💡 親要素に遠近感を持たせておく(必須) */
perspective: 800px;
}
.tf3d-v2-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 25px;
color: #d63384; /* 少しポップな色に */
text-align: center;
}
.tf3d-v2-axes-container {
display: flex;
flex-wrap: wrap; /* スマホで折り返すように */
gap: 20px;
justify-content: center;
margin-bottom: 20px;
}
.tf3d-v2-axis-box {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
width: 130px;
}
.tf3d-v2-axis-title {
margin: 0;
font-weight: bold;
font-size: 14px;
color: #333;
text-align: center;
line-height: 1.4;
}
.tf3d-v2-element {
width: 100px;
height: 100px;
background-color: #0d6efd;
color: white;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 14px;
border-radius: 12px;
box-shadow: 0 10px 20px rgba(0,0,0,0.15);
/* 💡 アニメーションを少し長め(0.8秒)にして動きを楽しむ */
transition: transform 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
cursor: pointer;
}
.tf3d-v2-emoji {
font-size: 30px;
margin-bottom: 5px;
}
/* 💡 X軸回転(縦にパタンと180度) */
.tf3d-v2-axis-box:hover .is-rotateX {
transform: rotateX(180deg);
}
/* 💡 Y軸回転(横にクルッと180度) */
.tf3d-v2-axis-box:hover .is-rotateY {
transform: rotateY(180deg);
}
/* 💡 Z軸回転(ハンドルを回すように180度) */
.tf3d-v2-axis-box:hover .is-rotateZ {
transform: rotateZ(180deg);
}
/* 💡 3D複合回転(無重力スピン:360度) */
.tf3d-v2-axis-box:hover .is-rotate3d {
transform: rotate3d(1, 1, 1, 360deg);
}
.tf3d-v2-code {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
margin-top: 10px;
}
.hl-blue {
color: #61afef;
font-weight: bold;
}
.hl-red {
color: #e06c75;
font-weight: bold;
}遠近感とpreserve-3dの指定方法
rotateXやrotateYを指定しただけでは、「要素が平面上でペシャンコに潰れた」ようにしか見えません。
これを3Dのように見せるのが「perspective-3d」です。
また、カードの「表面」と「裏面」を作ってクルッとひっくり返すようなアニメーションを作る際、もう一つ欠かせないのが「transform-style: preserve-3d」というプロパティです。
3D空間を作る必須セットアップは以下の3点です。
- カメラ(親要素)
変形させる要素の親要素にperspective: 800px;(数値が小さいほどレンズが近く強烈に歪む)を指定する。 - 空間の保持
親要素が別の親要素にネストされている場合、3Dの空間がペシャンコに潰れるのを防ぐため、コンテナとなる要素にtransform-style: preserve-3d;を指定する。 - 裏面の非表示
ひっくり返るカードを作る際は、要素にbackface-visibility: hidden;を指定して、裏返った時に鏡文字にならないようにする。
⭕️ ホバーして、さまざまな3Dアニメーションを体験してください
① フリップ(反転)
② 3Dドア(開閉)
③ 3Dチルト(浮遊)
.flip-inner {
transform-style: preserve-3d; /* 3D空間を維持 */
}
.camera:hover .flip-inner {
transform: rotateY(180deg); /* Y軸で裏返す */
}
/* 💡 ② 3Dドアの仕組み */
.door-front {
transform-origin: left; /* 💡 左端を蝶番(ちょうつがい)にする */
}
.camera:hover .door-front {
transform: rotateY(-110deg); /* 奥に向かって扉を開く */
}
/* 💡 ③ 3Dチルト(浮遊)の仕組み */
.camera:hover .tilt-inner {
/* 💡 X軸とY軸を少し傾け、Z軸で手前に浮かせる */
transform: rotateX(20deg) rotateY(-20deg) translateZ(30px);
box-shadow: 20px 20px 30px rgba(0,0,0,0.3); /* 影で立体感を強調 */
}
HTMLコード表示
<div class="tf3d-v3-wrapper">
<p class="tf3d-v3-caption">⭕️ ホバーして、さまざまな3Dアニメーションを体験してください</p>
<div class="tf3d-v3-demo-area">
<div class="v3-card-camera">
<p class="v3-card-title">① フリップ(反転)</p>
<div class="v3-flip-inner">
<div class="v3-face v3-front bg-blue">表面</div>
<div class="v3-face v3-back bg-green">裏面</div>
</div>
</div>
<div class="v3-card-camera">
<p class="v3-card-title">② 3Dドア(開閉)</p>
<div class="v3-door-content bg-dark">OPEN!</div>
<div class="v3-door-front bg-orange">扉を開く</div>
</div>
<div class="v3-card-camera">
<p class="v3-card-title">③ 3Dチルト(浮遊)</p>
<div class="v3-tilt-inner bg-purple">
<span class="v3-tilt-text">浮き上がる</span>
</div>
</div>
</div>
<div class="tf3d-v3-code">
/* 💡 ① フリップカードの仕組み */<br>
<span class="hl-blue">.flip-inner</span> {<br>
<span class="hl-red">transform-style: preserve-3d;</span> /* 3D空間を維持 */<br>
}<br>
<span class="hl-blue">.camera:hover .flip-inner</span> {<br>
<span class="hl-red">transform: rotateY(180deg);</span> /* Y軸で裏返す */<br>
}<br><br>
/* 💡 ② 3Dドアの仕組み */<br>
<span class="hl-blue">.door-front</span> {<br>
<span class="hl-red">transform-origin: left;</span> /* 💡 左端を蝶番(ちょうつがい)にする */<br>
}<br>
<span class="hl-blue">.camera:hover .door-front</span> {<br>
<span class="hl-red">transform: rotateY(-110deg);</span> /* 奥に向かって扉を開く */<br>
}<br><br>
/* 💡 ③ 3Dチルト(浮遊)の仕組み */<br>
<span class="hl-blue">.camera:hover .tilt-inner</span> {<br>
<span class="hl-comment">/* 💡 X軸とY軸を少し傾け、Z軸で手前に浮かせる */</span><br>
<span class="hl-red">transform: rotateX(20deg) rotateY(-20deg) translateZ(30px);</span><br>
<span class="hl-green">box-shadow: 20px 20px 30px rgba(0,0,0,0.3);</span> /* 影で立体感を強調 */<br>
}
</div>
</div>CSSコード表示
.tf3d-v3-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tf3d-v3-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
color: #198754;
text-align: center;
}
/* 💡 デモエリア */
.tf3d-v3-demo-area {
display: flex;
flex-wrap: wrap; /* スマホで縦並びにする */
gap: 30px;
justify-content: center;
background-color: #e9ecef;
padding: 40px 20px 50px 20px; /* 下部余白を多めに取ってコードとの被りを防ぐ */
border-radius: 4px;
border: 1px dashed #adb5bd;
margin-bottom: 30px;
}
/* 💡 共通のカメラ(親要素) */
.v3-card-camera {
width: 150px;
height: 200px;
perspective: 800px; /* 💡 遠近感をセット */
position: relative;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
}
.v3-card-title {
position: absolute;
top: -30px;
margin: 0;
font-size: 13px;
font-weight: bold;
color: #333;
white-space: nowrap;
}
/* =========================================
① フリップカード(反転)
========================================= */
.v3-flip-inner {
position: relative;
width: 100%;
height: 100%;
transform-style: preserve-3d;
transition: transform 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.v3-card-camera:hover .v3-flip-inner {
transform: rotateY(180deg);
}
.v3-face {
position: absolute;
width: 100%;
height: 100%;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 15px;
color: white;
backface-visibility: hidden; /* 裏返った時に隠す */
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.v3-back {
transform: rotateY(180deg);
}
/* =========================================
② 3Dドア(開閉)
========================================= */
.v3-door-content {
position: absolute;
width: 100%;
height: 100%;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 18px;
color: white;
box-shadow: inset 0 0 10px rgba(0,0,0,0.5);
}
.v3-door-front {
position: absolute;
width: 100%;
height: 100%;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 15px;
color: white;
transform-origin: left; /* 💡 左端を軸にする */
transition: transform 0.8s ease;
transform-style: preserve-3d;
box-shadow: 5px 0 15px rgba(0,0,0,0.2);
z-index: 2;
}
.v3-card-camera:hover .v3-door-front {
transform: rotateY(-110deg); /* 奥に向かって開く */
}
/* =========================================
③ 3Dチルト(浮遊)
========================================= */
.v3-tilt-inner {
width: 100%;
height: 100%;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 15px;
color: white;
transition: all 0.5s ease;
transform-style: preserve-3d;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.v3-card-camera:hover .v3-tilt-inner {
/* 💡 X・Y軸を傾け、Z軸で浮かせる */
transform: rotateX(20deg) rotateY(-20deg) translateZ(30px);
box-shadow: 20px 20px 30px rgba(0,0,0,0.3);
}
.v3-tilt-text {
/* 文字もさらに手前に浮かせる裏技 */
transform: translateZ(20px);
}
/* --- カラーパレット --- */
.bg-blue { background: linear-gradient(135deg, #0d6efd, #0dcaf0); }
.bg-green { background: linear-gradient(135deg, #198754, #20c997); }
.bg-dark { background: #343a40; }
.bg-orange { background: linear-gradient(135deg, #fd7e14, #ffc107); }
.bg-purple { background: linear-gradient(135deg, #6f42c1, #d63384); }
/* 💡 コードブロック */
.tf3d-v3-code {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }
.hl-comment { color: #6c757d; font-style: italic; }アニメーション連携とトラブル解決
transformプロパティは、単体で使うと一瞬で形や位置が変わるだけですが、アニメーション機能を組み合わせることでWebサイトにリッチな操作感を追加できます。
しかし、動きを加えたり複雑なレイアウトに組み込んだりすると、「なぜか動かない」「文字がぼやける」「要素が他の要素の後ろに隠れてしまう」といった特有のトラブルも発生しやすくなります。
ここでは、transformアニメーションの実装方法と実務で遇するエラーの解決策を解説します。
hover時のアニメーションとJS制御transformが効かない!インライン要素の罠と原因- 文字や画像がぼやける時の原因と対策
z-indexが効かなくなる仕様
hover時のアニメーションとJS制御
要素にカーソルを合わせた時に、ボタンがフワッと浮き上がったり拡大したりするホバーエフェクトは、Webデザインの基本です。
アニメーションを定義する際は、通常時の要素に対してtransitionを指定してください。
また、実務では単に時間を指定するだけでなく、変化のカーブや少し遅れて発火を組み合わせることで、心地よいモーションを作れます。
⭕️ hover時のアニメーションと、JS制御(クラス付与)のアニメーション
hoverでフワッと拡大
JSでクラスが付与された想定
.btn:hover {
transition: transform 0.3s ease;
transform: scale(1.1);
}
/* ⭕️ 通常時にtransitionを設定する */
.correct-btn {
transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); /* 💡 easeを細かく設定 */
}
.correct-btn:hover {
transform: scale(1.1);
}
/* 💡 スクロール発火(JS)用のCSS */
.js-fade-up {
transform: translateY(30px); /* 💡 初期状態は下にずらしておく */
transition: transform 0.6s ease;
}
.js-fade-up.is-active {
transform: translateY(0); /* 💡 JSでクラスが付いたら元の位置へ戻る */
}
HTMLコード表示
<div class="tf-anim-wrapper">
<p class="tf-anim-caption">⭕️ hover時のアニメーションと、JS制御(クラス付与)のアニメーション</p>
<div class="tf-anim-demo-area">
<div class="tf-anim-box">
<p class="tf-anim-label">hoverでフワッと拡大</p>
<div class="tf-anim-btn is-hover-btn">HOVER ME</div>
</div>
<div class="tf-anim-box">
<p class="tf-anim-label">JSでクラスが付与された想定</p>
<button class="tf-anim-trigger" onclick="document.getElementById('js-target').classList.toggle('is-active')">JS発火ボタン</button>
<div id="js-target" class="tf-anim-btn is-js-btn">下から出現</div>
</div>
</div>
<div class="tf-anim-code">
/* ❌ hover側にtransitionを書くと離した時にカクつく */<br>
<span class="hl-blue">.btn:hover</span> {<br>
<span class="hl-red">transition: transform 0.3s ease;</span><br>
<span class="hl-red">transform: scale(1.1);</span><br>
}<br><br>
/* ⭕️ 通常時にtransitionを設定する */<br>
<span class="hl-blue">.correct-btn</span> {<br>
<span class="hl-green">transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);</span> /* 💡 easeを細かく設定 */<br>
}<br>
<span class="hl-blue">.correct-btn:hover</span> {<br>
<span class="hl-red">transform: scale(1.1);</span><br>
}<br><br>
/* 💡 スクロール発火(JS)用のCSS */<br>
<span class="hl-blue">.js-fade-up</span> {<br>
<span class="hl-red">transform: translateY(30px);</span> /* 💡 初期状態は下にずらしておく */<br>
<span class="hl-green">transition: transform 0.6s ease;</span><br>
}<br>
<span class="hl-blue">.js-fade-up.is-active</span> {<br>
<span class="hl-red">transform: translateY(0);</span> /* 💡 JSでクラスが付いたら元の位置へ戻る */<br>
}
</div>
</div>CSSコード表示
.tf-anim-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tf-anim-caption {
font-size: 13px;
font-weight: bold;
margin-bottom: 20px;
color: #198754;
text-align: center;
}
.tf-anim-demo-area {
display: flex;
flex-wrap: wrap;
gap: 30px;
justify-content: center;
background-color: #e9ecef;
padding: 30px 20px;
border-radius: 4px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
}
.tf-anim-box {
display: flex;
flex-direction: column;
align-items: center;
width: 200px;
}
.tf-anim-label {
font-size: 12px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.tf-anim-btn {
background-color: #0d6efd;
color: white;
font-weight: bold;
padding: 15px 30px;
border-radius: 50px;
cursor: pointer;
}
/* 💡 ホバーボタンの正しい実装 */
.is-hover-btn {
/* 通常時にtransitionを置くことで、戻る時も滑らかになる */
transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.is-hover-btn:hover {
transform: scale(1.15) translateY(-5px);
}
/* JSのトリガーボタン */
.tf-anim-trigger {
background-color: #343a40;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
margin-bottom: 15px;
}
/* 💡 JS制御用の要素 */
.is-js-btn {
background-color: #6f42c1;
/* 初期状態は下に30px隠しておく(透明度はデモ用にあえて1のままにしています) */
transform: translateY(30px);
transition: transform 0.5s ease;
}
/* JSでクラスが付与されたら発火 */
.is-js-btn.is-active {
transform: translateY(0);
}
.tf-anim-code {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }
.hl-green { color: #98c379; font-weight: bold; }transitionプロパティの使い方を詳しく知りたい人は「【CSS】transitionの使い方:animationやtransformとの違い」を一読ください。
transformが効かない!インライン要素の罠と原因
「transformが効かない」と検索して頭を抱える原因のトップは、本記事でも何度か触れている「インライン要素の罠」です。
また、基準点の中央指定が効かないというトラブルも頻発します。
これも根本的な原因は同じで、要素が空間(幅と高さ)を正しく持っていないために、ブラウザが「中央」の座標を計算できなくなっている状態です。
要素を変形させたい時は、対象の要素がインライン要素(span,a,i,strongなど)ではないかを確認し、display: inline-block;(またはblockやflex)を指定してください。
これを行うことで初めて要素に「幅・高さ」と「座標軸」が生まれ、transformやtransform-originが機能します。
❌ インライン要素は動かない / ⭕️ inline-blockにすると動く
この文の中の 【失敗span】 はホバーしても回転しませんが、 【成功span】 はホバーすると回転します。
span.wrong {
transition: transform 0.3s;
}
span.wrong:hover {
transform: rotate(10deg); /* 💡 インライン要素なので無視される! */
}
/* ⭕️ inline-blockに変換する */
span.correct {
display: inline-block; /* 💡 これが絶対に必要 */
transition: transform 0.3s;
}
span.correct:hover {
transform: rotate(10deg); /* 💡 正常に回転する */
}
HTMLコード表示
<div class="tf-inline-wrapper">
<p class="tf-inline-caption">❌ インライン要素は動かない / ⭕️ inline-blockにすると動く</p>
<div class="tf-inline-demo-area">
<p class="tf-inline-text">
この文の中の
<span class="tf-inline-wrong">【失敗span】</span>
はホバーしても回転しませんが、
<span class="tf-inline-correct">【成功span】</span>
はホバーすると回転します。
</p>
</div>
<div class="tf-anim-code">
/* ❌ spanにそのまま指定している */<br>
<span class="hl-blue">span.wrong</span> {<br>
<span class="hl-green">transition: transform 0.3s;</span><br>
}<br>
<span class="hl-blue">span.wrong:hover</span> {<br>
<span class="hl-red">transform: rotate(10deg);</span> /* 💡 インライン要素なので無視される! */<br>
}<br><br>
/* ⭕️ inline-blockに変換する */<br>
<span class="hl-blue">span.correct</span> {<br>
<span class="hl-red">display: inline-block;</span> /* 💡 これが絶対に必要 */<br>
<span class="hl-green">transition: transform 0.3s;</span><br>
}<br>
<span class="hl-blue">span.correct:hover</span> {<br>
<span class="hl-red">transform: rotate(10deg);</span> /* 💡 正常に回転する */<br>
}
</div>
</div>CSSコード表示
.tf-inline-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tf-inline-caption {
font-size: 13px;
font-weight: bold;
margin-bottom: 15px;
color: #dc3545;
text-align: center;
}
.tf-inline-demo-area {
background-color: #e9ecef;
padding: 30px 20px;
border-radius: 4px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
text-align: center;
}
.tf-inline-text {
font-size: 16px;
color: #333;
line-height: 2;
}
/* ❌ 失敗(インライン) */
.tf-inline-wrong {
background-color: #dc3545;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-weight: bold;
cursor: pointer;
transition: transform 0.3s ease;
/* display: inline; のままなので動かない */
}
.tf-inline-wrong:hover {
transform: rotate(15deg);
}
/* ⭕️ 成功(インラインブロック) */
.tf-inline-correct {
background-color: #198754;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-weight: bold;
cursor: pointer;
transition: transform 0.3s ease;
/* 💡 ブロック要素に振る舞いを変える */
display: inline-block;
}
.tf-inline-correct:hover {
transform: rotate(15deg) scale(1.1);
}displayプロパティの使い方を詳しく知りたい人は「【html&css】displayの種類は?flexやinline-blockの違い」を一読ください。
文字や画像がぼやける時の原因と対策
transform: translate()で要素を移動させたり、scale()で拡大したりした際、中の文字や画像がかすれたようにぼやけるという現象が起きることがあります。
特にWindowsの低解像度モニターで顕著に現れます。
ぼやけを解消するための実務テクニックは以下の2つです。
- 3D処理でGPU描画を強制し裏面を隠す
ぼやける要素に対してtransform: translateZ(0);とbackface-visibility: hidden;を同時に指定します。
これでブラウザのレンダリング方法が変わり、文字のぼやけが直ることが多いです。 - 偶数サイズにする
中央寄せ(-50%)でぼやける場合は、対象の要素や親要素の幅・高さを「偶数」に固定して、移動距離が整数ピクセルになるように設計を直します。
⭕️ ぼやけ対策プロパティの有無(※端末によって見え方が異なります)
対策なし
(スケール時にぼやけやすい)
対策あり
(GPU描画でくっきりする)
.anti-blur-element {
transform: translateZ(0); /* 💡 3DレイヤーとしてGPUで処理させる */
backface-visibility: hidden; /* 💡 不要な裏側の描画を切り捨てる */
}
HTMLコード表示
<div class="tf-blur-wrapper">
<p class="tf-blur-caption">⭕️ ぼやけ対策プロパティの有無(※端末によって見え方が異なります)</p>
<div class="tf-blur-demo-area">
<div class="tf-blur-box">
<p class="tf-blur-label">対策なし<br><small>(スケール時にぼやけやすい)</small></p>
<div class="tf-blur-item is-wrong">TEXT</div>
</div>
<div class="tf-blur-box">
<p class="tf-blur-label">対策あり<br><small>(GPU描画でくっきりする)</small></p>
<div class="tf-blur-item is-correct">TEXT</div>
</div>
</div>
<div class="tf-anim-code">
/* 💡 ぼやけを解消する2行 */<br>
<span class="hl-blue">.anti-blur-element</span> {<br>
<span class="hl-red">transform: translateZ(0);</span> /* 💡 3DレイヤーとしてGPUで処理させる */<br>
<span class="hl-red">backface-visibility: hidden;</span> /* 💡 不要な裏側の描画を切り捨てる */<br>
}
</div>
</div>CSSコード表示
.tf-blur-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tf-blur-caption {
font-size: 13px;
font-weight: bold;
margin-bottom: 15px;
color: #198754;
text-align: center;
}
.tf-blur-demo-area {
display: flex;
gap: 40px;
justify-content: center;
background-color: #e9ecef;
padding: 40px 20px;
border-radius: 4px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
}
.tf-blur-box {
display: flex;
flex-direction: column;
align-items: center;
}
.tf-blur-label {
font-size: 12px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
text-align: center;
line-height: 1.4;
}
.tf-blur-item {
font-size: 24px;
font-weight: 900;
color: #333;
background-color: #fff;
padding: 20px 40px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.5s ease;
cursor: pointer;
}
/* ❌ 対策なし(ホバーで少し拡大させた時に、過程が滲みやすい) */
.tf-blur-item.is-wrong:hover {
transform: scale(1.5);
}
/* ⭕️ 対策あり */
.tf-blur-item.is-correct {
/* 💡 ぼやけ対策の2行 */
transform: translateZ(0);
backface-visibility: hidden;
}
.tf-blur-item.is-correct:hover {
/* scaleと一緒に translateZ を維持する */
transform: scale(1.5) translateZ(0);
}z-indexが効かなくなる仕様
Web制作の終盤、多くのコーダーを絶望させるのがz-indexの仕様によるレイアウト崩壊です。
ドロップダウンメニューや画面の上部に固定したヘッダーなどが、あとから作った下部スライダーやアニメーション要素の後ろに潜り込み、z-indexの数値を増やしても一番前に出ないという現象です。
子要素がカプセルの中に閉じ込められてしまうと、その中の子要素がどれだけz-indexを「9999」に設定しても、外の世界の要素(z-index: 2だとしても、カプセル自体より上にあるもの)には絶対に勝てません。
もしz-indexが効かなくなったら、「裏に潜り込んでしまった要素の『親要素』や『先祖要素』に、transformが指定されていないか?」を疑い、不要なtransformを解除するか、重なり順の基準となる親要素同士のz-indexバトルを解決するのがよいです。
❌ z-index: 9999が「外の世界の2」に負ける瞬間
① transform なし
(子要素の9999が正常に勝つ)
z-index: 2
z-index: 9999
② transform あり
(カプセル化され、外の2に負ける)
z-index: 2
transformあり
z-index: 9999
.rival {
position: absolute;
z-index: 2;
}
/* ❌ transformが指定された親は「カプセル(重なり順の新しい世界)」を作る */
.parent.is-transformed {
transform: translateY(0); /* 💡 これがあるだけでカプセル化する! */
}
/* ❌ 親がカプセル化した時点で、中の子要素は「外の2」に一生勝てなくなる */
.child {
position: absolute;
z-index: 9999; /* 💡 カプセルの中だけでしか威力を発揮できない */
}
HTMLコード表示
<div class="tf-v4-wrapper">
<p class="tf-v4-caption">❌ z-index: 9999が「外の世界の2」に負ける瞬間</p>
<div class="tf-v4-demo-area">
<div class="tf-v4-scenario">
<p class="tf-v4-scenario-title">① transform なし<br><small>(子要素の9999が正常に勝つ)</small></p>
<div class="tf-v4-stage">
<div class="tf-v4-rival">外のライバル<br>z-index: 2</div>
<div class="tf-v4-parent">
親要素 (z-index: auto)
<div class="tf-v4-child">子要素<br>z-index: 9999</div>
</div>
</div>
</div>
<div class="tf-v4-scenario">
<p class="tf-v4-scenario-title" style="color: #dc3545;">② transform あり<br><small>(カプセル化され、外の2に負ける)</small></p>
<div class="tf-v4-stage">
<div class="tf-v4-rival">外のライバル<br>z-index: 2</div>
<div class="tf-v4-parent is-transformed">
親(カプセル化)<br>transformあり
<div class="tf-v4-child">子要素<br>z-index: 9999</div>
</div>
</div>
</div>
</div>
<div class="tf-v4-code">
/* 💡 外の世界のライバル要素 */<br>
<span class="hl-blue">.rival</span> {<br>
<span class="hl-red">position: absolute;</span><br>
<span class="hl-red">z-index: 2;</span><br>
}<br><br>
/* ❌ transformが指定された親は「カプセル(重なり順の新しい世界)」を作る */<br>
<span class="hl-blue">.parent.is-transformed</span> {<br>
<span class="hl-red">transform: translateY(0);</span> /* 💡 これがあるだけでカプセル化する! */<br>
}<br><br>
/* ❌ 親がカプセル化した時点で、中の子要素は「外の2」に一生勝てなくなる */<br>
<span class="hl-blue">.child</span> {<br>
<span class="hl-red">position: absolute;</span><br>
<span class="hl-red">z-index: 9999;</span> /* 💡 カプセルの中だけでしか威力を発揮できない */<br>
}
</div>
</div>CSSコード表示
.tf-v4-wrapper {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.tf-v4-caption {
font-size: 14px;
font-weight: bold;
margin-bottom: 20px;
color: #dc3545;
text-align: center;
}
/* 💡 デモエリア(横並びで比較) */
.tf-v4-demo-area {
display: flex;
flex-wrap: wrap;
gap: 40px;
justify-content: center;
background-color: #e9ecef;
padding: 30px 20px 40px 20px;
border-radius: 4px;
border: 1px dashed #adb5bd;
margin-bottom: 20px;
}
.tf-v4-scenario {
display: flex;
flex-direction: column;
align-items: center;
}
.tf-v4-scenario-title {
margin: 0 0 15px 0;
font-weight: bold;
font-size: 14px;
text-align: center;
color: #198754;
line-height: 1.4;
}
/* 💡 絶対配置のためのステージ */
.tf-v4-stage {
position: relative;
width: 180px;
height: 200px;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
/* 💡 ライバル要素(外の世界) */
.tf-v4-rival {
position: absolute;
top: 15px;
left: 15px;
width: 120px;
height: 80px;
background-color: rgba(220, 53, 69, 0.95); /* 赤 */
color: white;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-weight: bold;
font-size: 13px;
border-radius: 6px;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
/* z-index指定 */
z-index: 2;
}
/* 💡 親要素 */
.tf-v4-parent {
position: absolute;
top: 70px;
left: 45px;
width: 120px;
height: 110px;
background-color: rgba(13, 110, 253, 0.1); /* 青枠 */
border: 2px dashed #0d6efd;
color: #0d6efd;
font-weight: bold;
font-size: 11px;
padding: 10px;
border-radius: 6px;
/* z-index: auto(指定なし)のままにしておく */
}
/* 💡 罠発動:親要素にtransformが付与された状態 */
.tf-v4-parent.is-transformed {
background-color: rgba(13, 110, 253, 0.2);
/* 💡 これがスタッキングコンテキスト(カプセル)を生み出す元凶! */
transform: translateY(0);
}
/* 💡 子要素(閉じ込められる要素) */
.tf-v4-child {
position: absolute;
top: -40px; /* 親の枠からはみ出させて、ライバルと重ねる */
left: -20px;
width: 100px;
height: 60px;
background-color: rgba(25, 135, 84, 0.95); /* 緑 */
color: white;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-weight: bold;
font-size: 12px;
border-radius: 6px;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
/* 数値を9999に設定 */
z-index: 9999;
}
/* コードブロック */
.tf-v4-code {
background-color: #282c34;
color: #abb2bf;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
line-height: 1.6;
border-left: 4px solid #0d6efd;
}
.hl-blue { color: #61afef; font-weight: bold; }
.hl-red { color: #e06c75; font-weight: bold; }z-indexプロパティの使い方を詳しく知りたい人は「【html&css】z-indexとは?使い方と効かない時の対処」を一読ください。
まとめ
分かりやすいようにまとめを記載します。
transformは要素の形状や位置を変化させるプロパティであり、文字の大文字・小文字変換(text-transform)とは異なる。- インライン要素には効かないため、
display: inline-blockまたはblockへの変更が必須である。 - 2D変形関数として
translate()(移動)、scale()(拡大縮小)、rotate()(回転)、skew()(傾斜)が存在する。 - 左右反転を実装する場合は、回転ではなく
scaleX(-1)を使用する。 - 複数の変形を同時に適用する場合は半角スペースで区切り、座標軸のズレを防ぐため「移動(
translate)」を先に記述する。 - 変形や回転の中心点は
transform-originプロパティで任意の座標(top, left, %, px等)に変更できる。 - 3D変形を行う際は、カメラとなる親要素に
perspective(遠近感)を指定する。 - 子要素の3D空間をペシャンコに潰さず維持するには、コンテナ要素に
transform-style: preserve-3dを指定する。 - ホバー時のアニメーションを滑らかにするには
:hover側ではなく「通常時」の要素に対してtransitionを指定する。 - 要素の移動や拡大時に文字がぼやける場合は
translateZ(0)とbackface-visibility: hidden;を指定してGPU描画を強制する。 transformが指定された要素は新たな「スタッキングコンテキスト」を生成するため、内部の子要素のz-indexが外の要素に負ける原因となる。
よくある質問(FAQ)
transformとtransitionの違いは何ですか?
transformは「どう変形するか(移動、拡大、回転など)」を指定するプロパティであり、transitionは「どのくらいの時間をかけて変化させるか」を指定するプロパティです。
ホバーした時にボタンをフワッと拡大させたい場合は、この2つを組み合わせて使用します。
(例:transform: scale(1.1);とtransition: transform 0.3s ease;)
transform: scale()などを指定しても要素が動かないのはなぜですか?
最も多い原因は、対象の要素が<span>や<a>などの「インライン要素」になっていることです。
CSSの仕様上、インライン要素にはtransformプロパティが一切効きません。
解決するには、対象の要素にdisplay: inline-block;またはdisplay: block;を追加して、要素に幅や高さの概念を持たせてください。
複数の変形を同時に適用するにはどう書けばいいですか?
1つのtransformプロパティの中に、半角スペースで区切って記述します。
(例:transform: translateY(-10px) rotate(5deg);)
初心者は2行に分けて(transform: translateY(-10px);の下の行にtransform: rotate(5deg);)書いてしまいがちですが、CSSのルール上「最後に書いた回転」だけで上書きされてしまい、移動がキャンセルされてしまいます。
position: absoluteと併用するtransform: translate(-50%, -50%)にはどんな意味があるのですか?
要素を「中央」に補正する設定です。
top: 50%; left: 50%;だけを指定すると、要素の「左上の角」が画面の中心に来てしまい、自分のサイズの分だけ右下にズレてしまいます。
そこでtranslate(-50%, -50%)を使うことで、「自分自身の幅と高さの半分」だけ上と左に引き戻し、アンカー(基準点)を要素の中心に合わせることができます。
要素を移動させる時、marginなどを使うのとtransform: translate()を使うのでは何が違いますか?
アニメーションさせた時の「パフォーマンス(描画の滑らかさ)」が決定的に違います。
ホバー時にmargin-topなどの数値を変化させると、ブラウザが周りの要素のレイアウトまで巻き込んで再計算(リフロー)するため、カクついたりレイアウトが崩れたりします。
一方transformは、他のレイアウトに影響を与えずGPUで独立して高速処理されるため、滑らかに動きます。
動かす時はtransformを使うのがよいです。

