No.178
TRANSITION CSSJS

FLIP List Reorder

シャッフルや並べ替えでカードの順番が変わった時、各カードが元の位置から新しい位置へなめらかにすべって移動する演出。FLIP(First・Last・Invert・Play)の手順で、並べ替えた後の DOM から逆算して動かします。

MY COLLECTION RANDOM ↗

DEMO

  • 01
  • 02
  • 03
  • 04
  • 05
  • 06

CODE

HTML
<div class="demo-stage flp-stage">
  <div class="flp-box">
    <div class="flp-tools">
      <button type="button" class="flp-btn" id="flp-shuffle">シャッフル</button>
      <button type="button" class="flp-btn" id="flp-sort">番号順に戻す</button>
    </div>
    <ul class="flp-grid" id="flp-grid" aria-label="並べ替えるカード">
      <li class="flp-item" data-n="1">01</li>
      <li class="flp-item" data-n="2">02</li>
      <li class="flp-item" data-n="3">03</li>
      <li class="flp-item" data-n="4">04</li>
      <li class="flp-item" data-n="5">05</li>
      <li class="flp-item" data-n="6">06</li>
    </ul>
    <p class="flp-status" id="flp-status" role="status" aria-live="polite"></p>
  </div>
</div>
CSS
.flp-stage { display:flex; align-items:center; justify-content:center; padding:1.5rem; background:#0B0B10; min-height:320px; }
.flp-box { width:100%; max-width:340px; display:flex; flex-direction:column; gap:.9rem; }
.flp-tools { display:flex; gap:.6rem; flex-wrap:wrap; }
.flp-btn { min-height:44px; padding:0 1rem; border-radius:10px; border:1px solid rgba(255,255,255,.2); background:transparent; color:#EDEDED; font-size:.82rem; cursor:pointer; }
.flp-btn:focus-visible { outline:2px solid #E91B89; outline-offset:2px; }
.flp-grid { list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(3, 1fr); gap:.6rem; }
.flp-item { display:flex; align-items:center; justify-content:center; aspect-ratio:1; border-radius:12px; color:#fff; font-family:'Oswald',sans-serif; font-size:1.3rem; letter-spacing:.06em; }
.flp-item[data-n="1"] { background:#7B37A8; }
.flp-item[data-n="2"] { background:#D1177A; }
.flp-item[data-n="3"] { background:#0369A1; }
.flp-item[data-n="4"] { background:#047857; }
.flp-item[data-n="5"] { background:#B45309; }
.flp-item[data-n="6"] { background:#4338CA; }
.flp-status { margin:0; min-height:1.2em; color:#A1A1AA; font-size:.76rem; }
JS
(function(){
  var grid = document.getElementById('flp-grid');
  var shuffleBtn = document.getElementById('flp-shuffle');
  var sortBtn = document.getElementById('flp-sort');
  var status = document.getElementById('flp-status');
  if (!grid || !shuffleBtn || !sortBtn || !status) return;
  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
  function items(){ return Array.prototype.slice.call(grid.children); }
  function reorder(next, msg){
    var first = new Map();
    items().forEach(function(el){
      first.set(el, el.getBoundingClientRect());
      if (el.getAnimations) el.getAnimations().forEach(function(a){ a.cancel(); });
    });
    next.forEach(function(el){ grid.appendChild(el); });
    status.textContent = msg + ':' + next.map(function(el){ return el.textContent; }).join('、');
    if (reduce.matches || !grid.animate) return;
    next.forEach(function(el){
      var f = first.get(el), l = el.getBoundingClientRect();
      var dx = f.left - l.left, dy = f.top - l.top;
      if (!dx && !dy) return;
      el.animate([{ transform: 'translate(' + dx + 'px,' + dy + 'px)' }, { transform: 'none' }],
        { duration: 450, easing: 'cubic-bezier(.22,1,.36,1)' });
    });
  }
  shuffleBtn.addEventListener('click', function(){
    var list = items(), next;
    do {
      next = list.slice();
      for (var i = next.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1)), t = next[i];
        next[i] = next[j]; next[j] = t;
      }
    } while (next.every(function(el, k){ return el === list[k]; }));
    reorder(next, 'シャッフルしました');
  });
  sortBtn.addEventListener('click', function(){
    reorder(items().sort(function(a, b){ return a.dataset.n - b.dataset.n; }), '番号順に戻しました');
  });
})();

AI PROMPT

Claude Code Cursor v0
PROMPT
シャッフルや並べ替えでカードの順番が変わった時、各カードが元の位置から新しい位置へなめらかにすべる演出を、FLIP(First・Last・Invert・Play)の手順で作って。6枚のカードを ul と li で3列の CSS Grid に並べる。並べ替える時は、①First:各カードの getBoundingClientRect を記録する(動いている途中なら、その時点で見えている位置が記録される)。続けて getAnimations() で動いている途中のアニメーションを cancel する。②Last:appendChild で新しい順番に並べ替える。③Invert:並べ替えた後の getBoundingClientRect との差(元の位置 − 新しい位置)を求める。④Play:element.animate で transform: translate(差X, 差Y) から transform: none へ450ミリ秒・cubic-bezier(.22,1,.36,1) で動かす(位置が変わらないカードは動かさない)。レイアウトは1回だけ変え、動きは transform だけで作る(top や left は動かさない)。シャッフルは Fisher–Yates で行い、結果が元と同じ順番なら引き直す。「シャッフル」と「番号順に戻す」のボタンを置き、連打しても途中の位置から次の位置へ動くようにする。並べ替えた結果は role="status" の段落で「シャッフルしました:03、01、…」と伝える。prefers-reduced-motion: reduce の時は動かさずに並べ替えだけ行う。

Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift

AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する

BOOKS · PR · AMAZON

📖 なかしまぁ先生のCSSアニメーション入門 📖 UIデザインの教科書 新版 マルチデバイス時代のインターフェース設計 📖 Fundamentals of Web Animation with GSAP

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

学びを止めない · AMAZONサブスク

📚 Kindle Unlimited — 技術書・デザイン書が読み放題。30日無料体験 → 🎧 Audible — 移動中に耳で学ぶオーディオブック。30日無料体験 → 📦 Amazon Prime — Prime Reading・配送特典つき。30日無料体験 → 🎵 Music Unlimited — 作業用BGMに。1億曲が聴き放題・30日無料体験 →

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

CREATOR TOOLS · スクール(PR)

🎬 Adobe Creative Cloud — After Effects / Photoshop 業界標準ツール → 💻 SkillHacks — プログラミング・動画編集オンラインスクール → 🚀 デイトラ — Web制作スクール・実案件レベルまで習得 →

※本サイトはアフィリエイトリンクを含みます。リンク経由のご購入・お申し込みで運営者が紹介料を受け取る場合があります。

RELATED EFFECTS

↗ MORE
→ FRAMER LAYOUT ANIMATION → ANIMATED LIST → VIEW TRANSITION API

FLIP List Reorder(FLIP で並べ替えるアニメーション)とは

並べ替えやシャッフルでカードの順番が変わった時、それぞれのカードが元の場所から新しい場所へすべって移動する演出です。Paul Lewis さんの記事「FLIP Your Animations」で紹介された、First・Last・Invert・Play の頭文字を取った FLIP という手順で作っています。

当サイトの Framer Layout Animation は要素の幅が変わると兄弟要素もなめらかに再配置される Framer Motion の演出、Animated List は新着が上から差し込まれ既存の行が押し出される通知リスト、View Transition API はブラウザ標準の API で画面の切り替えを滑らかにする演出です。本ページはライブラリを使わず、並べ替えそのものを素の JavaScript で動かす別テーマです。

一覧の並べ替え・絞り込み・お気に入りへの移動など、「どこへ行ったか」が分かると迷わない操作に向いています。動きは transform だけで作れるので軽く、動きを減らす設定の利用者には並べ替えだけを行えば、機能はそのまま使えます。

よくある質問

FLIP とは何の略ですか?

Paul Lewis さんが記事「FLIP Your Animations」で紹介した手法で、First(最初の位置)・Last(最後の位置)・Invert(差を打ち消す)・Play(再生)の頭文字です。先に DOM を最終的な形へ変えてしまい、見た目だけを transform で元の位置へ戻してから、transform を外すアニメーションを再生します。位置の計算はブラウザのレイアウトに任せられるので、グリッドや折り返しのある並びでも同じ書き方で動かせます。

連打した時にカードが飛ばないのはなぜですか?

並べ替える前の位置を getBoundingClientRect で測ると、動いている途中のカードは transform を含めた「今見えている位置」が返ります。その位置を First として記録してから、動いている途中のアニメーションを cancel しています。こうすると、途中の位置から次の位置へ続けて動くため、連打しても元の場所へ一瞬戻るようなちらつきが起きません。

コードは無料で使えますか?

はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。