목차10개 항목
출처와 참고
transform이란
transform은 요소에 이동(translate), 확대·축소(scale), 회전(rotate), 기울이기(skew) 를 적용하는 속성이다. 요소가 차지하는 원래 자리는 그대로 두고 그려지는 모습만 바꾸므로 주변 요소의 배치에 영향을 주지 않는다. transition이나 animation과 함께 쓰면 움직임이 되고, 단독으로 쓰면 바로 적용된 상태로 렌더링된다.
transform 함수
| 함수 | 설명 | 값 |
|---|---|---|
translate(x, y) | X축으로 x, Y축으로 y만큼 이동 | px, %, em 등 |
translateX(n) / translateY(n) | 한 축으로만 이동 | px, %, em 등 |
scale(x, y) | X축 x배, Y축 y배 확대·축소 | 0과 양수 |
scaleX(n) / scaleY(n) | 한 축으로만 확대·축소 | 0과 양수 |
rotate(angle) | angle만큼 회전 | ±deg |
skew(x-angle, y-angle) | X축·Y축으로 기울임 | ±deg |
skewX(angle) / skewY(angle) | 한 축으로만 기울임 | ±deg |
여러 함수를 공백으로 이어 쓸 수 있고, 적힌 순서대로 적용된다.
.card:hover {
transform: translateY(-4px) scale(1.02);
}translate의 %는 부모가 아니라 요소 자기 크기 기준이다. 아래 가운데 정렬이 동작하는 이유다.
개별 변형 속성
translate, rotate, scale은 transform 함수일 뿐 아니라 독립된 CSS 속성이기도 하다. 모든 주요 브라우저가 2022년부터 지원한다.
.card { translate: 0 0; scale: 1; transition: translate .2s, scale .2s; }
.card:hover { translate: 0 -4px; } /* scale은 그대로 둔 채 이동만 바꾼다 */
.card:active { scale: .98; }transform 한 줄에 함수를 몰아 쓰면 호버에서 이동만 바꾸려 해도 확대 값까지 다시 적어야 한다. 개별 속성은 서로 덮어쓰지 않아 상태별로 따로 바꾸기 쉽다. 적용 순서는 작성 순서와 상관없이 항상 translate → rotate → scale → transform이다.
3D 변형과 원근
| 함수·속성 | 하는 일 |
|---|---|
rotateX(), rotateY(), rotateZ() | 한 축을 중심으로 회전. rotate()는 rotateZ()와 같다 |
rotate3d(x, y, z, 각도) | 벡터 (x, y, z)를 축으로 회전 |
translateZ(), translate3d() | 화면 앞뒤(Z축)로 이동 |
scaleZ(), scale3d() | Z축 배율 |
perspective 속성 / perspective() 함수 | 보는 사람과 Z=0 평면 사이의 거리. 작을수록 원근이 강하다. 속성은 자식에, 함수는 그 요소에 적용된다 |
perspective-origin | 소실점 위치 |
transform-style: preserve-3d | 자식도 같은 3D 공간에 놓는다(카드 뒤집기) |
backface-visibility: hidden | 뒤집혔을 때 뒷면을 숨긴다 |
.scene { perspective: 800px; }
.card { transform-style: preserve-3d; transition: rotate .6s; }
.card.flipped { rotate: y 180deg; }
.card :is(.front, .back) { backface-visibility: hidden; }
.card .back { rotate: y 180deg; } /* 앞뒤 면을 같은 칸에 겹쳐 둔다 */transform·opacity만 바꾸면 합성만 다시 돌아 부드럽다(변경이 다시 도는 단계). transform이 있는 요소는 새 쌓임 맥락을 만들고, 그 안의 position: fixed 자식의 기준이 된다(CSS position과 z-index).
translate와 위치 지정(top·left) 고르기
요소를 옮기는 방법은 두 가지다. 기준이 다르다. 움직임이면 translate, 놓을 자리면 위치 지정이다.
translate()·translate 속성 | position: absolute + top·left | |
|---|---|---|
| 다시 도는 렌더링 단계 | 합성만. 브라우저가 요소를 별도 합성 레이어에 올려 GPU로 처리하기도 한다 | 값이 바뀔 때마다 레이아웃(리플로)부터 다시 한다 |
| 원래 자리 | 그대로 차지한다(position: relative로 옮긴 것과 비슷하다) | 흐름에서 빠져 자리를 비운다 |
% 기준 | 요소 자기 크기 | 컨테이닝 블록 크기 |
| 어울리는 곳 | 애니메이션·드래그·스크롤 연동처럼 매 프레임 바뀌는 이동 | 배지·드롭다운·오버레이처럼 레이아웃상 자리를 정하는 배치 |
.toast { position: fixed; inset-block-end: 1rem; inset-inline-end: 1rem; } /* 자리는 위치 지정으로 */
.toast[data-state="enter"] { animation: slide-in .2s ease-out; }
@keyframes slide-in { from { translate: 0 120%; } } /* 움직임은 translate로 */애니메이션에서 top·left를 바꾸면 매 프레임 레이아웃을 다시 계산해 버벅이기 쉽다. 반대로 translate로 옮긴 요소는 원래 자리를 계속 차지하므로, 레이아웃에서 실제로 자리를 옮겨야 하면 위치 지정이나 플렉스·그리드 배치를 쓴다.
가운데 정렬 패턴
증상
팝업을 화면 가운데 두려고 top: 50%; left: 50%만 주면 몇십 픽셀 어긋난다. 위치가 요소의 왼쪽 위 모서리 기준으로 계산되기 때문이다. 모서리가 화면 중앙에 오고, 요소는 오른쪽 아래로 치우친다.
해결
자기 너비·높이의 절반만큼 되돌린다.
.pop-up {
position: fixed; /* 뷰포트 기준 */
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 999;
}top/left 50%만 translate(-50%, -50%) 추가
┌──────────────┐ ┌──────────────┐
│ │ │ │
│ ┌───┐ │ │ ┌───┐ │
│ │ │ │ │ │ ● │ │
│ └───┘ │ │ └───┘ │
└──────────────┘ └──────────────┘
모서리가 중앙 요소의 중심이 중앙요소 크기를 몰라도 동작한다는 점이 장점이다. 부모 안에서 가운데 두려면 position: absolute와 부모의 position: relative를 쓴다(position).
지금은 변형 없이 가운데 정렬하는 방법이 더 간단하다.
| 상황 | 방법 |
|---|---|
| 일반 레이아웃 안 | 부모에 display: grid; place-items: center 또는 Flexbox(justify-content·align-items: center) |
| 고정·절대 위치 팝업 | position: fixed; inset: 0; margin: auto; width: fit-content; height: fit-content |
| 모달 대화 상자 | <dialog>의 showModal(). 브라우저가 화면 가운데·최상위 레이어에 띄운다 |
정리
transform은 자리는 그대로, 보이는 모습만 바꾼다.translate의%는 자기 크기 기준이다.- 움직임은 합성만 다시 도는
translate로, 레이아웃상 자리는 위치 지정으로 정한다. - 크기를 모르는 팝업의 정중앙 배치:
top·left: 50%+translate(-50%, -50%). 그리드place-items: center,inset: 0; margin: auto,<dialog>도 같은 일을 한다. - 상태마다 따로 바꿀 값은
translate·rotate·scale개별 속성으로 쓴다.
참고 자료
관련 노트: position, Float와 Flexbox, CSS 애니메이션: transition과 @keyframes, CSS 함수: calc()·clamp()와 수학 함수