큐빅 베지어

Cubic bezier

두 제어점(P1, P2)의 위치로 이징 곡선의 모양을 정의하는 함수. cubic-bezier(x1,y1,x2,y2) 형태로 씁니다.

다른 이름: cubic-bezier()Bezier easing curve
···
html
<div class="wrap">
  <svg class="graph" viewBox="-15 -70 130 185">
    <path class="grid" d="M0,100 L100,100 M0,0 L0,100"/>
    <path id="curve" class="curve" d=""/>
    <line id="l1" class="cpline"/><line id="l2" class="cpline"/>
    <circle id="cp1" class="cp" r="2.6"/><circle id="cp2" class="cp" r="2.6"/>
    <circle id="tracer" class="tracer" r="3"/>
  </svg>
  <div class="track"><i id="ball" class="ball"></i></div>
  <code class="label">cubic-bezier(.34, 1.56, .64, 1)</code>
</div>
css
.wrap{position:absolute;inset:8% 10%;display:grid;grid-template-rows:1fr auto auto;gap:12px}
.graph{display:block;width:100%;height:100%;min-height:0}
.grid{stroke:var(--line);stroke-width:1;fill:none}
.curve{stroke:var(--accent);stroke-width:2.5;fill:none}
.cpline{stroke:var(--muted);stroke-width:.8;stroke-dasharray:2 2}
.cp{fill:var(--accent-2)}
.tracer{fill:var(--accent-3)}
.track{position:relative;height:10px;border-radius:6px;background:var(--line)}
.ball{position:absolute;top:50%;left:0;width:16px;height:16px;margin-top:-8px;border-radius:50%;background:var(--accent-3);box-shadow:0 2px 6px rgba(0,0,0,.25)}
.label{font:11px/1 ui-monospace,monospace;color:var(--muted);text-align:center}
js
const P1 = [.34, 1.56], P2 = [.64, 1];
const curve = document.getElementById('curve');
const cp1 = document.getElementById('cp1'), cp2 = document.getElementById('cp2');
const l1 = document.getElementById('l1'), l2 = document.getElementById('l2');
const tracer = document.getElementById('tracer');
const ball = document.getElementById('ball');
function toSvg(x, y) { return [x * 100, 100 - y * 100]; }
const p1s = toSvg(P1[0], P1[1]), p2s = toSvg(P2[0], P2[1]);
curve.setAttribute('d', 'M0,100 C' + p1s[0] + ',' + p1s[1] + ' ' + p2s[0] + ',' + p2s[1] + ' 100,0');
cp1.setAttribute('cx', p1s[0]); cp1.setAttribute('cy', p1s[1]);
cp2.setAttribute('cx', p2s[0]); cp2.setAttribute('cy', p2s[1]);
l1.setAttribute('x1', 0); l1.setAttribute('y1', 100); l1.setAttribute('x2', p1s[0]); l1.setAttribute('y2', p1s[1]);
l2.setAttribute('x1', 100); l2.setAttribute('y1', 0); l2.setAttribute('x2', p2s[0]); l2.setAttribute('y2', p2s[1]);

function bezier(t) {
  const x = 3 * (1 - t) ** 2 * t * P1[0] + 3 * (1 - t) * t ** 2 * P2[0] + t ** 3;
  const y = 3 * (1 - t) ** 2 * t * P1[1] + 3 * (1 - t) * t ** 2 * P2[1] + t ** 3;
  return [x, y];
}
const start = performance.now();
function loop(now) {
  const dur = 2200;
  const t = ((now - start) % dur) / dur;
  const pt = bezier(t);
  const s = toSvg(pt[0], pt[1]);
  tracer.setAttribute('cx', s[0]); tracer.setAttribute('cy', s[1]);
  const p = Math.max(0, Math.min(1, pt[1]));
  ball.style.left = 'calc(' + (p * 100) + '% - ' + (p * 16) + 'px)';
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

시작점(0,0)과 끝점(1,1)은 고정돼 있고, 그 사이의 두 제어점(P1, P2)을 움직여 곡선의 휘어짐을 정합니다. x축은 시간(0→1), y축은 진행률(0→1)입니다. y값이 0~1을 벗어나면 값이 목표를 지나쳤다가 돌아오는 오버슈트(back·elastic 느낌)가 생깁니다.

디자인 툴에서 곡선을 눈으로 그리고, 그 값을 그대로 CSS cubic-bezier()에 넣는 식으로 작업하는 경우가 많습니다. 크롬 개발자 도구의 애니메이션 패널도 곡선을 시각적으로 편집해줍니다.

데모의 그래프가 곡선이고, 아래 막대가 그 곡선을 실제로 따라 움직입니다 — 곡선이 가파른 구간에서 막대도 빠르게 움직입니다.

언제 쓰나

브라우저 키워드(ease-out 등)로 충분하면 그걸 쓰세요. 브랜드 고유의 "느낌"을 만들 때만 커스텀 베지어를 설계합니다.