Timeline sequencing

타임라인 시퀀싱

A structure where a single playhead sweeps through several parallel tracks — position, scale, opacity — at once, reading each one's value as it passes, even when the tracks start and end at different points.

Also known as: Animation timelineKeyframe sequencing
···
html
<div class="tl">
  <div class="stage"><div class="obj" id="obj"></div></div>
  <div class="tracks" id="tracks">
    <div class="track"><span class="lbl">X</span><div class="lane"><div class="seg s1"></div></div></div>
    <div class="track"><span class="lbl">Scale</span><div class="lane"><div class="seg s2"></div></div></div>
    <div class="track"><span class="lbl">Opacity</span><div class="lane"><div class="seg s3"></div></div></div>
    <div class="playhead" id="ph"></div>
  </div>
</div>
css
.tl{position:absolute;inset:6%;display:grid;grid-template-rows:1fr auto;gap:10px}
.stage{position:relative;border:1px solid var(--line);border-radius:10px;background:var(--surface);overflow:hidden}
.obj{position:absolute;top:50%;left:4%;width:clamp(18px,7vmin,30px);height:clamp(18px,7vmin,30px);margin-top:-15px;border-radius:8px;background:var(--accent)}
.tracks{position:relative;display:flex;flex-direction:column;gap:6px;padding:2px 0}
.track{display:flex;align-items:center;gap:8px}
.lbl{width:38px;flex:none;font:10px/1 ui-monospace,monospace;color:var(--muted);text-align:right}
.lane{position:relative;flex:1;height:8px;background:var(--line);border-radius:4px;overflow:hidden}
.seg{position:absolute;top:0;bottom:0;border-radius:4px}
.s1{left:0%;width:60%;background:var(--accent)}
.s2{left:30%;width:50%;background:var(--accent-2)}
.s3{left:50%;width:50%;background:var(--accent-3)}
.playhead{position:absolute;top:-52px;bottom:-2px;width:2px;background:var(--fg);opacity:.5}
js
const obj = document.getElementById('obj');
const ph = document.getElementById('ph');
const DUR = 3200;
function clamp01(n) { return Math.max(0, Math.min(1, n)); }
const t0 = performance.now();
function loop(now) {
  const t = ((now - t0) % DUR) / DUR;
  ph.style.left = (t * 100) + '%';

  const px = clamp01(t / 0.6) * 78;
  let sc = 1;
  if (t >= 0.3) sc = 1 + clamp01((t - 0.3) / 0.5) * 0.7;
  let op = 1;
  if (t >= 0.5) op = 1 - clamp01((t - 0.5) / 0.5) * 0.75;

  obj.style.left = (4 + px) + '%';
  obj.style.transform = 'translateY(-50%) scale(' + sc.toFixed(2) + ')';
  obj.style.opacity = String(op.toFixed(2));
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

Each track owns its own span — a start and end progress — and how its value changes within that span. As the playhead moves from 0 to 1, you check which tracks' spans the current point falls inside, and apply each active track's value to the element. Because one track can start before another finishes, you get overlapping motion: "moving, then growing partway through, then fading out while still growing."

The implementation gives each track a [start, end] progress range; when the current progress falls inside it, you renormalize to (progress − start) / (end − start) to get that track's own 0–1 interpolation. After Effects' graph editor and GSAP's Timeline are the canonical tools that expose this structure through a GUI or API — you lay tracks per layer and drag bars to line up overlaps visually.

With two or three tracks, hand-tuning the spans is fine. Past four or five, keeping the overlaps straight in code gets error-prone — that's the point to reach for a dedicated timeline tool instead.

When to use

Use it for compound sequences where several properties must overlap on different timings — logo intros, onboarding sequences. Overkill for a transition that only changes one property.