UIアニメーションは「動けばOK」ではありません。
- カクつく
- 状態が壊れる
- 意図しない挙動になる
こういった問題の多くは、アンチパターン(悪い設計)が原因です。
この記事では、
実際によくあるミスをベースに
- ❌ 悪い実装
- ✅ 改善方法
の形で整理していきます。
① 状態を持たずに直接DOMを操作する
❌ よくあるコード
button.addEventListener('click', () => {
box.style.transform = 'translateX(100px)';
});問題点
- 状態がどこにも存在しない
- 複数操作で破綻する
- アニメーション制御ができない
✅ 改善(状態を持つ)
let state = {
x: 0,
targetX: 0
};
button.addEventListener('click', () => {
state.targetX = 100;
});② requestAnimationFrameを使わない
❌ setIntervalで制御
setInterval(() => {
box.style.left = parseInt(box.style.left || 0) + 1 + 'px';
}, 16);問題点
- フレーム同期しない
- カクつく
- CPU無駄
✅ requestAnimationFrameにする
function loop() {
state.x += (state.targetX - state.x) * 0.1;
box.style.transform = `translateX(${state.x}px)`;
requestAnimationFrame(loop);
}
loop();👉 詳細解説
③ 状態と描画が混ざっている
❌ 悪い例
function update() {
state.x += 1;
box.style.transform = `translateX(${state.x}px)`;
}問題点
- テストできない
- 再利用不可
- バグりやすい
✅ 分離する
function update() {
state.x += 1;
}
function render() {
box.style.transform = `translateX(${state.x}px)`;
}
function loop() {
update();
render();
requestAnimationFrame(loop);
}👉 設計の基礎
④ easingを使わずに一定速度で動かす
❌ 機械的な動き
state.x += 2;問題点
- UXが悪い
- 人間らしさがない
✅ easing / lerp
state.x += (state.targetX - state.x) * 0.1;👉 easing解説
⑤ scrollイベントで直接処理する
❌ よくある地獄
window.addEventListener('scroll', () => {
box.style.transform = `translateY(${window.scrollY}px)`;
});問題点
- 高頻度で重い
- ガクガク
- 制御不能
✅ stateに変換
let state = {
scroll: 0
};
window.addEventListener('scroll', () => {
state.scroll = window.scrollY;
});function loop() {
box.style.transform = `translateY(${state.scroll}px)`;
requestAnimationFrame(loop);
}👉 scroll設計
⑥ アニメーションを途中で止められない
❌ 典型
function animate() {
requestAnimationFrame(animate);
}問題点
- 無限ループ
- 制御不能
- メモリ浪費
✅ フラグ管理
let isRunning = true;
function loop() {
if (!isRunning) return;
requestAnimationFrame(loop);
}⑦ 複数アニメーションが干渉する
❌ 上書き地獄
box.style.transform = 'translateX(100px)';
box.style.transform = 'scale(1.2)';問題点
- transformが上書きされる
- 意図しない動き
✅ 合成する
box.style.transform = `
translateX(${state.x}px)
scale(${state.scale})
`;⑧ 入力イベントをそのまま使う
❌ 生のイベント
mousemove = (e) => {
box.style.left = e.clientX + 'px';
};問題点
- ノイズが多い
- カクつく
- デバイス依存
✅ 状態化+補間
state.targetX = e.clientX;
state.x += (state.targetX - state.x) * 0.1;
🧪 実験:Anti vs Good Animation Comparison
以下のデモでは
左:アンチパターン
右:改善版
を比較できます。
まとめ
UIアニメーションの質は「設計」で決まります。
今回のポイント:
- 状態を持つ
- requestAnimationFrameを使う
- update / renderを分離
- easingを使う
- 入力をそのまま使わない

コメントを残す