【CSS】transformのmatrixって何ですか?
CSSのtransform
プロパティにおけるmatrix関数
(matrix()およびmatrix3d())について書いていきます。
transform
プロパティとは
transform は CSS のプロパティで、与えられた要素を回転、拡大縮小、傾斜、移動することできます。
MDNの説明にあるようにtransform
プロパティは要素に平行移動、拡大縮小、回転、傾斜などの座標変換を適用するものです。
これらの座標変換はtranslate関数
、scale関数
、rotate関数
、skew関数
などのCSS関数で簡単に実現することができ、CSSに精通している方々には馴染み深いものかもしれません。筆者の経験上においても前述したCSS関数はよく使用します。
一方で本記事の主題であるmatrix関数
は使用頻度が比較的低く、筆者自身も案件で使ったことはほぼありません。MDNの説明を読んでみて、正直よくわからないと感じる方もいるのではないでしょうか。
matrix
)とは
行列(matrix
には、日本語で数学における「行列」という意味があります。
数学の線型代数学周辺分野における行列(ぎょうれつ、英: matrix)は、数や記号や式などを縦と横に矩形状に配列したものである。
Wikipediaの行列のページには上記のように書かれています。ざっくりいうと行列は数字を縦と横の矩形状に並べたものです。行列の横方向を行、縦方向を列と呼びます。
行列は、空間内のオブジェクトの変換を表すために使用でき、画像を構築したり、ウェブ上でデータを視覚化したりするときに、多くの主要な種類の計算を実行するために使用されます。
行列は空間内のオブジェクトの変換を表現するために使用され、画像の構築やウェブ上でのデータの視覚化など、さまざまな分野で重要な計算を実行するのに使われています。
CSSのmatrix関数
では、これらの行列を用いて座標変換の計算を行います。すでに登場したtranslate関数
、scale関数
、rotate関数
、skew関数
などの他のCSS座標変換関数を代替することも可能です。
行列を使った座標変換のイメージ
matrix関数
を使うことで行列を用いた座標変換が行えることがわかりましたが、どのように行列を使用するのかがイメージしづらいですね。ここでは、その流れを簡単に確認してみましょう。
座標変換と変換行列
座標変換では、座標に何かしらの行列を乗算することで回転、拡大縮小、傾斜、移動のような変換を適用します。この何かしらの行列のことを「変換行列」と呼びます。
たとえば(x、y)の座標に変換行列[?]
を乗算して、新たな(x′, y′)の座標を取得したいときは以下のような式で表現できます。
上記の式を見てみるとなにやら不思議な箇所がありますね。x
、y
(およびx′
、y′
)まではわかりますが、1
はどこから出てきたのでしょうか。これは同次座標系と呼ばれるものです。
同次座標系
同次座標系はN次元の座標系に対し、次元が1つ多いN+1次元の座標系です。たとえば2次元平面上の座標(x,y)を同次座標系で表すと(x,y,w)、3次元空間上の座標(x,y,z)を同次座標系で表すと(x,y,z,w)となります。また通常このwは1に設定されます(前述の式でも1に設定しています)。
なぜ同次座標系(つまりN+1次元の座標系)を使うのかというと、計算上都合がいいからです。同次座標系を使うことで平行移動、拡大縮小、回転などの座標変換を一度の乗算で計算できるようになります。
座標と行列の乗算
ここで座標と行列の乗算について確認しておきます。
上記のような座標と変換行列で乗算を行う場合、変換後の座標(x′, y′, w′)はそれぞれ次のように計算されます。
変換行列の「行」と座標の各々の要素を乗算し、それらすべてを足し合わせています。以下のような形の方がわかりやすいかもしれません。
また計算方法で気付いた方もいるかもしれませんが、行列Aと行列Bで乗算するためには行列Aの列数と行列Bの行数が等しい必要があります。たとえば座標が3次元(x,y,w)であれば、変換行列は3列になります。ちなみに本記事で出てくる変換行列は2次元平面の場合は3×3型、3次元空間の場合は4×4型です。
行列の乗算についてのイメージが湧かない場合は、Matrix Multiplicationというサイトが役立ちます。このサイトでは、行列の乗算がどのように動作するかが視覚的に示されています。
4つの変換行列と座標変換
さて、ここでは平行移動、拡大縮小、回転、傾斜の各座標変換とそれに対応する変換行列について、もう少し詳しく見ていきましょう。これらの座標変換はCSSのtranslate関数
、scale関数
、rotate関数
、skew関数
に相当します。
CSSでは3次元空間での座標変換が可能なので、基本的には3次元の座標(x,y,z)を考えていきます。ただし、傾斜(skew関数
)に関しては2次元の座標(x,y)を対象としているので、この場合は2次元の座標だけを考慮します。
平行移動
平行移動の変換行列は以下の通りです。
tx
、ty
, tz
はそれぞれX軸、Y軸、Z軸における移動量です。したがって、平行移動の計算は次のように表されます。
拡大縮小
拡大縮小の変換行列は以下の通りです。
sx
、sy
, sz
はそれぞれX軸、Y軸、Z軸における拡大縮小率です。したがって、拡大縮小の計算は次のように表されます。
回転
回転の座標変換はX軸、Y軸、Z軸のそれぞれの軸に対する変換行列が関与します。
X軸回転
X軸回転の変換行列は以下の通りです。
したがって、X軸回転の計算は次のように表されます。
Y軸回転
Y軸回転の変換行列は以下の通りです。
したがって、Y軸回転の計算は次のように表されます。
Z軸回転
Z軸回転の変換行列は以下の通りです。
したがって、Z軸回転の計算は次のように表されます。
傾斜
傾斜の変換行列は以下の通りです。2次元における同次座標系なので3×3型の変換行列になることに注意してください。
tanθx
およびtanθy
はそれぞれX軸、Y軸に対応しています。したがって、傾斜の計算は次のように表されます。
matrix関数
を用いた座標変換
CSSのここまできてようやくCSSのmatrix関数
の出番です。座標変換と変換行列について把握したところで、実際にどのようにmatrix関数
を使用するのかについて考えていきましょう。
matrix3d()
とは
まず3次元の座標(x,y,z)を対象とするmatrix3d()
について考えてみましょう。
matrix3d() は CSS の関数で、 4x4 の 3D 同次変換行列を定義します。
MDNでは上記のように書かれています。matrix3d()
はこれまでに登場した同次座標系による変換行列を定義すると考えて良さそうです。
実際にmatrix3d()
は4×4=16個の値で指定を行います。
matrix3d(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4)
ただし、その値の順番には気を付ける必要があります。どういうことかというとmatrix3d()
は列優先で記述するということです。JavaScriptの配列などで行列を考える際は、行優先で考えることも多いと思いますので注意が必要です。
matrix3d()
を4×4で記述すると
となり、これは次の変換行列
に対応します。
matrix()
とは
次に2次元の座標(x,y)を対象とするmatrix()
についても考えてみます。
matrix() は CSS の関数で、二次元同次変換行列を定義します。
matrix3d()
と同様に同次座標系の変換行列を定義すると考えて良さそうです。設定できる値は以下になります。
matrix(a, b, c, d, tx, ty)
注目すべきなのは2次元の同次座標系で考えているのにもかかわらず、設定できる値が6個(3×3=9個ではなく)になるということでしょう。
実はmatrix()
の場合、対応する変換行列は以下のようになります。
3行目の値は常に固定なのでmatrix()
では省略されます。
matrix(a, b, c, d, tx, ty) は matrix3d(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1) の短縮形です。
さらにmatrix()
はmatrix3d()
の短縮系であり、matrix3d()
に変換することが可能です。3次元として考えれば以下のような変換行列となります。
これを任意の座標(x,y,z)を用いて、実際に計算してみます。
この計算からわかる通り、Z座標には何の変換も行われていないことが確認できます。3次元座標においても、Z座標に対する変換が行われないのであれば、実質的には2次元座標の考え方を適用できますね。
matrix関数
平行移動とCSSのmatrix関数
による平行移動を実装してみましょう。他の座標変換のことも考えると手で計算するのは少し面倒なので、計算にはJavaScriptを使用します。
以下は平行移動の座標変換を行う実装例です。水色の「Origin」と書かれたボックスと、紫色の「Matrix」と書かれたボックスが存在します。水色のボックスは座標変換適用前、紫色のボックスは座標変換適用後の要素です(ここで指している座標変換の対象は.box
の要素になります)。
平行移動の座標変換を行なっているのは次の箇所になります。
// 平行移動の座標変換
const translate = (tx, ty, tz) => {
// 平行移動の変換行列
return [
1, 0, 0, tx,
0, 1, 0, ty,
0, 0, 1, tz,
0, 0, 0, 1,
]
};
const target = document.getElementById("matrix");
target.style.transform = convertArrayToCSSMatrix3d(translate(30, 100, 50));
上記のtranslate(30, 100, 50)
はCSSのtranslate3d(30px, 100px,50px)
に相当します。
convertArrayToCSSMatrix3d関数
convertArrayToCSSMatrix3d
はJavaScriptの配列をCSSのmatrix3d
に変換する関数です。
// JavaScriptの配列をCSSのmatrix3d()に変換する
const convertArrayToCSSMatrix3d = (matrix) => {
if (matrix.length !== 16) {
throw new Error("Matrix must be 16 elements");
}
// 行優先から列優先への変換
const rearrangedMatrix = [
matrix[0], matrix[4], matrix[8], matrix[12],
matrix[1], matrix[5], matrix[9], matrix[13],
matrix[2], matrix[6], matrix[10], matrix[14],
matrix[3], matrix[7], matrix[11], matrix[15],
]
return `matrix3d(${rearrangedMatrix.join(",")})`;
};
matrix関数
拡大縮小と次にCSSのmatrix関数
による拡大縮小の座標変換を実装してみましょう。
以下は拡大縮小の座標変換を行う実装例です。
拡大縮小の座標変換を行っているのは次の箇所になります。
// 拡大縮小の座標変換
const scale = (sx, sy, sz) => {
// 拡大縮小の変換行列
return [
sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, 1,
]
};
const target = document.getElementById("matrix");
target.style.transform = convertArrayToCSSMatrix3d(scale(1.2, 1.2, 1.2));
上記のscale(1.2, 1.2, 1.2)
はCSSのscale3d(1.2, 1.2, 1.2)
に相当します。
matrix関数
回転と次にmatrix関数
を用いた回転の座標変換について考えてみます。本記事において回転はX軸、Y軸、Z軸をそれぞれ分けて考えていたので、ここでも別々に実装を行います。
以下は上からX軸回転、Y軸回転、Z軸回転の座標変換を行う実装例です。
回転の座標変換を行なっているのは次の箇所になります。
const sin = Math.sin;
const cos = Math.cos;
// 度(度数法)から、ラジアン(弧度法)に変換
const degreesToRadians = (degrees) => {
const radian = (Math.PI / 180) * degrees;
return radian;
};
// X軸回転の座標変換
const rotateX = (theta) => {
const radian = degreesToRadians(theta);
// X軸回転の変換行列
return [
1, 0, 0, 0,
0, cos(radian), -1 * sin(radian), 0,
0, sin(radian), cos(radian), 0,
0, 0, 0, 1,
]
};
// Y軸回転の座標変換
const rotateY = (theta) => {
const radian = degreesToRadians(theta);
// Y軸回転の変換行列
return [
cos(radian), 0, sin(radian), 0,
0, 1, 0, 0,
-1 * sin(radian), 0, cos(radian), 0,
0, 0, 0, 1,
]
};
// Z軸回転の座標変換
const rotateZ = (theta) => {
const radian = degreesToRadians(theta);
// Z軸回転の変換行列
return [
cos(radian), -1 * sin(radian), 0, 0,
sin(radian), cos(radian), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]
};
const targetX = document.getElementById("matrixX");
targetX.style.transform = convertArrayToCSSMatrix3d(rotateX(45));
const targetY = document.getElementById("matrixY");
targetY.style.transform = convertArrayToCSSMatrix3d(rotateY(45));
const targetZ = document.getElementById("matrixZ");
targetZ.style.transform = convertArrayToCSSMatrix3d(rotateZ(45));
上記のrotateX(45)
、rotateY(45)
、rotateZ(45)
はそれぞれCSSのrotateX(45deg)
、rotateY(45deg)
、rotateZ(45deg)
に相当します。
matrix関数
傾斜と最後にmatrix関数
を用いた傾斜の座標変換について考えてみます。傾斜は2次元の座標を対象としていたので、ここでは2次元の座標で考えます。
以下は傾斜の座標変換を行う実装例です。
傾斜の座標変換を行なっているのは次の箇所になります。
// 傾斜の座標変換
const skew = (thetaX, thetaY) => {
const tan = Math.tan;
const radianX = degreesToRadians(thetaX);
const radianY = degreesToRadians(thetaY);
// 傾斜の変換行列
return [
1, tan(radianX), 0,
tan(radianY), 1, 0,
0, 0, 1
]
};
const target = document.getElementById("matrix");
target.style.transform = convertArrayToCSSMatrix3d(skew(10, 20));
上記のskew(10, 20)
はCSSのskew(10deg, 20deg)
に相当します。
おわりに
CSSのtransform
プロパティにおけるmatrix関数
について書いてみました。本記事で紹介したようにmatrix関数
はあまり直感的ではないので、ユースケースとしてはそれほど多くはないかもしれません。
ただ行列による座標変換についてはCSSに限った話ではなく、たとえばCanvasやWebGLにおいても用いいられることがあるので、どこかで役に立つことがあれば幸いです。
参考
Discussion
JS で行列計算をしたり、CSS の文字列を作ったりするために
DOMMatrixReadOnly
とDOMMatrix
がありますね。ありがとうございます。
恥ずかしながら知らなかったので確認します!