🚴♂️
DOMatrix で CSS の translate による移動量を取得する
タイトルの通りですが translate
の移動量を取得したいなというモチベーションがあり調べていたところ、DOMMatrix
に出会いました。
この出会いを記念して以下みたいなもの(語彙力、、、)を作って遊んでみたという記事です。
サンプルコード
記事では抜粋した内容しか書きませんが、React / TypeScript / CSS Modules という構成で以下のリポジトリに全体のコードはあげています。
CSS
本題となるアニメーションの主要な部分は高速かつ連続で画像を切り替えるアニメーションです。
これを translate
での移動と CSS アニメーションをステップ実行させることで再現しています。
styles.module.css
/* ラッパー */
.shuffleArea {
--item-size: 300px;
--item-length: 10;
overflow: hidden;
width: var(--item-size);
margin-inline: auto;
}
/* このエリアをストップするまでずっと移動させつづける */
.shuffleList {
transform: translateX(0);
display: flex;
animation: move 2s steps(var(--item-length)) infinite;
}
/* ストップボタンを押したらアニメーションはストップ */
.shuffleList[data-shuffle="false"] {
animation-play-state: paused;
}
/* アイテム(画像) */
.shuffleItem {
flex-shrink: 0;
width: var(--item-size);
}
/* アイテムの横幅 * アイテムの枚数 を左側に動かすために負の値へ */
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(calc(var(--item-size) * var(--item-length) * -1));
}
}
DOMMatrix で移動量から何番目の画像なのか判定する
今回の記事の主題となる DOMMatrix
を利用した translate
の移動量を取得する処理は以下で実装しています。
// 移動量から現在表示しているのが何番目の画像かを返す関数
const getSelectedIndex = (transform: string) => {
const matrix = new DOMMatrix(transform);
// X軸軸の移動量をここで取得している(方向が負の値になるので絶対値にする)
const translateX = Math.abs(matrix.m41);
// X座標の移動量 / 1つの要素の幅
const rawIndex = translateX / IMAGE_SIZE;
const currentIndex = Math.round(rawIndex);
return currentIndex;
};
// contentRef.current は .shuffleList の RefObject
const style = window.getComputedStyle(contentRef.current);
const transform = style.transform;
// transform を渡し関数内で translateX の移動量から何番目の画像かを計算して返す
const selectedIndex = getSelectedIndex(transform);
感想
JavaScript を利用すると結構なんでもできてしまいますがパフォーマンスを考えると CSS でやるべきという場面に遭遇することもあります。
例えば React で何も考えず再レンダーを無限に繰り返す実装にすると CPU のメインスレッドに負荷がかかり予期せぬパフォーマンスの問題を引き起こす可能性があります。
CSS の translate
で GPU アクセラレーションの恩恵を受けつつ、ストップした時点の画像が何番目なのかは JavaScript 側で取得することで相対的に負荷を軽減できたかなというのが感想です。
まとめ
世界には知らないことがまだまだたくさんあって楽しい。
おわり👋
Discussion