플립 기법

FLIP technique

레이아웃이 바뀌기 전(First)과 후(Last)의 위치 차이를 계산해, 바뀐 직후 요소를 원래 자리로 되돌려놓고(Invert) transform으로 자연스럽게 되돌리는(Play) 기법.

다른 이름: First Last Invert PlayShared element transition
···
html
<div class="items" id="items">
  <div class="item i1">1</div>
  <div class="item i2">2</div>
  <div class="item i3">3</div>
  <div class="item i4">4</div>
</div>
css
.items{position:absolute;inset:24% 10%;display:flex;gap:4%;align-items:flex-start}
.item{flex:1;aspect-ratio:1;border-radius:10px;display:grid;place-items:center;color:#fff;font:700 clamp(14px,4vmin,22px)/1 sans-serif}
.i1{background:var(--accent)}.i2{background:var(--accent-2)}.i3{background:var(--accent-3)}.i4{background:var(--accent)}
js
const box = document.getElementById('items');
const items = [...box.children];
let alt = false;
function flip(mutate) {
  const first = items.map((el) => el.getBoundingClientRect());
  mutate();
  const last = items.map((el) => el.getBoundingClientRect());
  items.forEach((el, i) => {
    const dx = first[i].left - last[i].left;
    const dy = first[i].top - last[i].top;
    if (dx || dy) {
      el.style.transition = 'none';
      el.style.transform = 'translate(' + dx + 'px, ' + dy + 'px)';
      requestAnimationFrame(() => {
        el.style.transition = 'transform .5s cubic-bezier(.2,.8,.2,1)';
        el.style.transform = '';
      });
    }
  });
}
function shuffle() {
  alt = !alt;
  const order = alt ? [3, 1, 2, 0] : [0, 1, 2, 3];
  flip(() => order.forEach((i) => box.appendChild(items[i])));
}
setInterval(shuffle, 2400);

top·left·width 같은 레이아웃 속성을 직접 애니메이션하면 브라우저가 매 프레임 레이아웃을 다시 계산해야 해서 느립니다. FLIP은 순간이동 자체는 그대로 두고, "이동 전 위치 − 이동 후 위치" 차이만큼 transform으로 되돌린 뒤 그 transform을 0으로 애니메이션합니다 — GPU가 처리하는 transform만 쓰므로 훨씬 부드럽습니다.

리스트 정렬 변경, 카드가 그리드에서 모달로 확대되는 전환, 드래그 앤 드롭 재정렬처럼 레이아웃이 순간적으로 바뀌는 상황에 씁니다. 이름은 Google의 Paul Lewis가 지었습니다.

데모의 네모들은 2.4초마다 순서를 바꾸는데, 실제로는 DOM이 순간이동한 뒤 매 요소의 Before/After 좌표를 재서 transform으로 되돌리고 있습니다.

언제 쓰나

요소의 위치·크기가 레이아웃 변경으로 순간적으로 바뀔 때 씁니다. transition만으로 되는 단순한 위치 이동이면 FLIP까지 갈 필요 없습니다.