「ネットで調べた通りに書いているのにtransformが効かないことがある」
「transformには省略記法が使えると思っている」
「keyframesを使ったアニメーションなどで思ったような動きを作れない」
本日はそんな方に向けてCSSのtransformプロパティのよくあるミス例と原因をお話していきます。
transformプロパティを使うことでクリエイティブなデザインやCSSのみでのアニメーション実装ができるため、幅広いケースでよく使われる便利なプロパティです。
一方でスクールや初学者向けの教材ではあまり深く解説されないので、いざ自分で作ってみるとハマることがあります。
そこで初心者の方がよくやりがちなミス例を紹介していきますので、コーディングで詰まったときにはまず本記事の内容を確認するところから始めてみてください。
また動画もあるので必要な方はご覧ください。
CSSのtransformには省略記法が使えないものがある
transformには図形の拡大、回転などいろんなプロパティを実行することができます。
多くの場合、メソッドになっていて引数にX軸の値とY軸の値を入れて実行します。
例えば以下はscaleを使った拡大、縮小をしています。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<div class="wrapper">テキスト</div>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 32px;
font-size: 24px;
font-weight: bold;
color: #fff;
}
.wrapper {
background: blue;
transform: scale(1.5, 0.7);
}
もともとのサイズが200pxと200pxの正方形だったのですが、横幅を1.5倍にして縦幅を0.7倍に縮小したために長方形になっていますね。
引数の数字を変えて正方形のまま拡大させることもできます。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 32px;
font-size: 24px;
font-weight: bold;
color: #fff;
}
.wrapper {
background: blue;
/* ここを修正 */
transform: scale(1.5, 1.5);
}
このようにX軸とY軸の値が同じ値の場合、以下のように省略して書いても同じ結果になります。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 32px;
font-size: 24px;
font-weight: bold;
color: #fff;
}
.wrapper {
background: blue;
/* ここを修正 */
transform: scale(1.5);
}
scale(1.5, 1.5)とscale(1.5)は同じ意味になるということです。
このような省略した書き方は他のCSSプロパティでも登場するので皆さんもよく知っているはずです。
一方でtransformで指定するメソッドには「同じ値でも省略できない」ものがあるのをご存知でしょうか?
例えばtranslateを使った移動です。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<div class="inner">テキスト</div>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 32px;
font-size: 24px;
font-weight: bold;
color: #fff;
}
.inner {
background: green;
position: relative;
}
.inner::after {
content: "";
width: 100px;
height: 100px;
background: grey;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
擬似要素のグレー部分が親要素に対して中央寄せの配置になっています。
translate(-50%, -50%)となっている部分をX軸とY軸が同じ値のため省略してみます。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 32px;
font-size: 24px;
font-weight: bold;
color: #fff;
}
.inner {
background: green;
position: relative;
}
.inner::after {
content: "";
width: 100px;
height: 100px;
background: grey;
position: absolute;
top: 50%;
left: 50%;
/* ここを修正 */
transform: translate(-50%);
}
X軸とY軸が同じ値だったので省略した書き方をすると配置がずれてしまいました。
こちらはtranslate(-50%, 0)と同じ意味になったためです。
公式ドキュメントにも引数が一つの場合について記載があります。
scaleと違ってtranslateは引数を一つにした場合、自動的に第二引数のY軸が「0」になる仕組みだったんですね。
同じような現象はskewにも当てはまります。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<div class="box">テキスト</div>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 32px;
font-size: 24px;
font-weight: bold;
color: #fff;
}
.box {
background: purple;
transform: skew(30deg);
}
「X軸とY軸ともに30度歪める」という意味でskew(30deg)を書いてもX軸しか実行されませんでした。
こちらもskew(30deg, 0)と同じ意味だからです。
そのためX軸とY軸が同じ値だったとしても省略せずに記載しましょう。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 32px;
font-size: 24px;
font-weight: bold;
color: #fff;
}
.box {
background: purple;
/* ここを修正 */
transform: skew(30deg, 30deg);
}
transformに複数メソッドを指定した場合は上書きに注意
transformには「拡大+回転」のように複数の指定をすることができます。
以下は拡大とY軸の移動を行っています。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<div class="row"></div>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
}
.row {
background: orange;
transform: scale(2, 1.5) translateY(150px);
}
transformを使うケースとしてアニメーションなどで「特定のクラスの追加(削除)」とセットで使用することがあります。
例えば「card」というクラス名が追加されたときはtranslateYをリセットするような場合はCSSで以下のように記載して、JavaScriptで「card」を追加する処理を作るわけです。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
}
.row {
background: orange;
transform: scale(2, 1.5) translateY(150px);
}
/* ここを追加 */
.row.card {
transform: translateY(0px);
}
今回はHTML側で手動でcardを追加してしましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<!-- ここを変更 -->
<div class="row card"></div>
</body>
</html>
Y軸の移動がなくなったので画面の上に移動しましたね。
しかしよく見てみると要素のサイズが元に戻っています、scaleが効いていません。
こちらはtranslateYと一緒にscaleもリセットされたためです。
transformの挙動というより、CSSの上書きの原理から考えると難しい話ではありません。
background: red; をbackground: blue; と上書きしているようなものと同じで、transformで指定した一部の値だけを変更することはCSSの原理としてはできないわけです。
そのため今回のようなケースでscaleは変えずにtranslateYのみ変更したい場合は以下のようにすれば良いです。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
}
.row {
background: orange;
transform: scale(2, 1.5) translateY(150px);
}
/* ここを修正 */
.row.card {
transform: scale(2, 1.5) translateY(0px);
}
これでscaleによる拡大をしつつtranslateYのみを上書きすることができました。
よくよく考えれば当たり前の話ですが、初心者が学習をある程度進めて少し慣れてくると今回のような勘違いをしてしまいますので注意しましょう。
ちなみにCSS変数を使う方法でも同じことを実現できます。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 64px;
}
div {
width: 200px;
height: 200px;
}
.row {
background: orange;
/* ここを修正 */
transform: scale(2, 1.5) translateY(var(--ty, 150px));
}
/* ここを修正 */
.row.card {
--ty: 0px;
}
CSSでは「–変数名」で変数に値を保管することで使い回しができます。
作った変数はvar()の引数として使用するのですが、実は引数は2つ設定することができます。
var(変数名、デフォルト値)といった具合に、変数名が存在していれば使用して、変数名が存在しないときは第二引数に書いたデフォルト値が適用される仕組みです。
条件分岐のような形でとても便利だと思います。
加えて今回で言うとscaleのように「変更ないもの」を2回書くこともありません。
インライン要素にtransformを使用することはできない
もう一つ挙げるとするならばインライン要素に対してのtransformの扱いです。
インライン要素にもサイズや余白を指定するのでdisplay: inline-block;を使うことが多く、身を持って経験することは少ないですが「transformはインライン要素には使えない」ということを覚えておきましょう。
ブロック要素のdivタグとインライン要素のspanタグで挙動を確認してみます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<div class="container"></div>
<div class="wrapper">
<span>テキストです</span>
</div>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
padding: 80px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 80px;
}
.container {
background: red;
transform: rotate(45deg);
}
.wrapper span {
font-size: 32px;
font-weight: bold;
color: #fff;
background: blue;
transform: rotate(45deg);
}
transformプロパティはインライン要素には使用できないことが改めてわかりました。
先ほども言いましたがブロック要素に切り替えればtransformプロパティの効果を得ることができます。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
padding: 80px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 80px;
}
.container {
background: red;
transform: rotate(45deg);
}
.wrapper span {
font-size: 32px;
font-weight: bold;
color: #fff;
background: blue;
/* ここを追加 */
display: inline-block;
transform: rotate(45deg);
}
これで一件落着ですね、もう一つあるとしたら親要素に対してtransformを使用することで解決できることもあります。
上記のコードではspanタグはdivタグの子要素なので、divタグの方にtransformを使用してみても良いでしょう。
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
padding: 80px;
}
div {
width: 200px;
height: 200px;
margin-bottom: 80px;
}
.container {
background: red;
transform: rotate(45deg);
}
/* ここを追加 */
.wrapper {
transform: rotate(45deg);
}
.wrapper span {
font-size: 32px;
font-weight: bold;
color: #fff;
background: blue;
/* こちらはインライン要素に戻す */
/* display: inline-block; */
transform: rotate(45deg);
}
参考までに公式ドキュメントも紹介しておきます。
インライン要素では使用できないことが明記してありますね。
凝ったデザインをコーディングしているとaタグでボタンを作ったりなど、インライン要素のタグをブロック要素に切り替えることは多いので「今まで気にしたことなかった」という方も多いかもしれませんね。
以上がtransformプロパティが効かない時に確認すべき3点になります。