transform-origin

rotate·scale 같은 transform 함수가 "어느 점을 기준으로" 적용될지 정하는 속성. 기본값은 요소 정중앙(50% 50%)입니다.

다른 이름: Rotation pivotAnchor point
···
html
<div class="wrap"><div class="square" id="sq"><i class="dot" id="dot"></i></div></div><div class="label" id="l">transform-origin: 0% 0%</div>
css
.wrap{position:relative;display:grid;place-items:center}
.square{position:relative;width:min(26vmin,64px);aspect-ratio:1;border-radius:12px;
  background:linear-gradient(135deg,#5b5bf7,#18c29c)}
.dot{position:absolute;width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;background:#fff;
  box-shadow:0 0 0 3px rgba(0,0,0,.3);transition:left .5s,top .5s}
.label{position:absolute;left:50%;bottom:6%;transform:translateX(-50%);padding:4px 10px;border-radius:999px;
  background:rgba(0,0,0,.6);color:#fff;font:600 12px/1.4 monospace}
js
const sq = document.getElementById('sq');
const dot = document.getElementById('dot');
const l = document.getElementById('l');
const ORIGINS = ['0% 0%','100% 0%','100% 100%','50% 50%','20% 80%'];
let oi = 0, angle = 0;
function setOrigin(){
  const css = ORIGINS[oi];
  sq.style.transformOrigin = css;
  const [x, y] = css.split(' ');
  dot.style.left = x; dot.style.top = y;
  l.textContent = 'transform-origin: ' + css;
  oi = (oi + 1) % ORIGINS.length;
}
setOrigin();
setInterval(setOrigin, 1800);
function spin(){ angle += 0.6; sq.style.transform = 'rotate(' + angle + 'deg)'; requestAnimationFrame(spin); }
spin();

transform-origin 은 요소를 움직이지 않습니다 — 그 요소가 회전·확대될 때 어느 점이 고정축이 되는지만 바꿉니다. 값은 자기 자신의 박스를 기준으로 한 %·px·키워드(top left 등)이고, 3D 변환에는 z 값도 세 번째로 줄 수 있습니다.

데모의 점은 지금 축으로 쓰이는 위치를 그대로 표시합니다. 축이 모서리(0% 0%)에 있으면 요소가 그 모서리를 중심으로 크게 휘돌고, 중앙(50% 50%)이면 제자리에서 팽이처럼 돕니다.

가장 자주 겪는 착각은 "요소를 옮기는 속성"으로 오해하는 것입니다 — transform-origin 만 바꾸면 화면상 위치는 그대로고, 그 다음에 실행되는 rotate/scale 의 궤적만 달라집니다. 메뉴가 트리거 버튼 모서리에서 펼쳐지는 애니메이션, 아코디언이 위쪽을 축으로 펼쳐지는 애니메이션이 전형적인 활용입니다.

언제 쓰나

메뉴·툴팁이 트리거 버튼 쪽에서 자라나듯 열리게 하거나, 아코디언이 위쪽을 축으로 펼쳐지게 할 때 씁니다.