transform은 요소의 그려진 모습을 옮기거나(translateY()) 키우는(scale()) CSS 속성입니다. 배치가 끝난 뒤 그리기에서만 하는 일이라, 요소의 원래 자리와 이웃 요소의 자리는 그대로입니다.
레시피 카드에 마우스를 올리면 살짝 떠오르게 하려 합니다. 바깥 여백으로 카드를 올리면 아래 카드까지 따라 올라가고, 너비를 늘려 키우면 옆 글이 밀려 읽던 목록이 흔들립니다. transform은 배치를 건드리지 않고 그려진 모습만 바꿔, 가리킨 카드만 움직이고 나머지는 그 자리에 둡니다. 이 레슨은 그려진 자리 · 크기와 배치 자리 · 크기가 어떻게 갈리는지를 봅니다.
그림 준비 중
1translateY()는 그려진 모습만 옮긴다
일반 흐름과 position · 기준 상자와 absolute에서 세운 규칙에 문장을 잇습니다.
브라우저 규칙 8.
transform은 배치가 끝난 뒤 그려진 모습만 옮깁니다 — 자리는 그대로입니다.
240 × 100 카드를 위로 4px 옮깁니다. 아래 문단은 카드 바로 밑에 붙어 있습니다.
<div class="card">김치볶음밥</div>
<p>다음 문단</p>
.card {
box-sizing: border-box;
width: 240px;
height: 100px;
border: 1px solid #d2d2d7;
transform: translateY(-4px);
}
p {
margin: 0;
}.card의 위 자리4px.card의 배치 위 자리8pxp의 위 자리108px
카드는 페이지 위에서 4px에 그려졌지만, 배치 위 자리는 transform이 없을 때와 같은 8px(body의 바깥 여백)입니다. 아래 문단도 카드의 원래 자리를 보고 놓여 108px 그대로입니다 — 카드가 올라가 생긴 4px 틈을 채우러 올라오지 않습니다.
translateY() 안의 값 | 그려지는 쪽 |
|---|---|
음수(-4px) | 원래 자리보다 위 |
0 | 원래 자리 |
양수(4px) | 원래 자리보다 아래 |
가로로 옮기는 것은 translateX()입니다 — 양수면 오른쪽, 음수면 왼쪽입니다.
transform은 블록 · 인라인 블록 상자에 먹고, 링크 a처럼 글을 담는 인라인에는 먹지 않습니다 — 링크를 옮기려면 display: inline-block을 함께 줍니다.
2scale()은 그려진 크기만 키운다
scale() 안의 숫자는 배율입니다. scale(1.02)는 가로 · 세로를 각각 1.02배로 그리고, 단위를 붙이지 않습니다. 따로 정하지 않으면 상자의 가운데를 기준으로 커집니다.
<div class="card">김치볶음밥</div>
<p>다음 문단</p>
.card {
box-sizing: border-box;
width: 240px;
height: 100px;
border: 1px solid #d2d2d7;
transform: scale(1.02);
}
p {
margin: 0;
}.card의 너비244.8px.card의 배치 너비240pxp의 위 자리108px
'너비'는 그려진 너비라 240 × 1.02 = 244.8입니다. '배치 너비'는 배치에 쓴 너비라 240 그대로이고, 아래 문단도 그대로입니다. 카드는 위아래로 1px씩 커져 문단 위를 조금 덮었습니다. scale(1)은 원래 크기의 1배라 아무것도 바뀌지 않습니다.
3옮기기와 키우기를 한 줄에
옮기기와 키우기를 함께 하려면 한 선언에 빈칸으로 이어 적습니다. 줄이 넘치면 함수마다 한 줄에 씁니다. 앞 레슨의 전환을 붙이면 카드가 0.18초 동안 떠오르며 커집니다.
<div class="card">김치볶음밥</div>
<div>다음 글</div>
.card {
box-sizing: border-box;
width: 240px;
height: 100px;
border: 1px solid #d2d2d7;
transition:
transform 180ms ease;
}
.card:hover {
transform:
translateY(-4px)
scale(1.02);
}.card의 위 자리3px.card의 너비244.8px.card + div의 위 자리108px
카드는 4px 올라가고 가운데에서 1px 더 커져 3px에 그려집니다. transform이 이어 그리기에 알맞은 까닭도 여기 있습니다 — 배치가 그대로라, 0.18초 동안 브라우저가 이웃을 다시 놓을 일이 없습니다.
예제
레시피 카드 떠오르기
레시피 목록의 카드에 마우스를 올리면 위로 4px 떠오르게 합니다. 아래 잰 값은 첫 카드에 마우스를 올리고 전환이 끝난 뒤의 값입니다.
<div class="recipe-card">
김치볶음밥
</div>
<div class="recipe-card">
계란말이
</div>
.recipe-card {
box-sizing: border-box;
width: 240px;
height: 100px;
margin-bottom: 16px;
border: 1px solid #d2d2d7;
transition:
transform 180ms ease;
}
.recipe-card:hover {
transform: translateY(-4px);
}.recipe-card의 위 자리4px.recipe-card의 배치 위 자리8px.recipe-card + .recipe-card의 위 자리124px
첫 카드만 4px 떠오르고, 둘째 카드는 8 + 100 + 16 = 124px 그대로입니다. 결과 화면의 카드에 마우스를 올렸다 떼며 봅니다.
자주 틀리는 곳
틀린 생각: transform으로 옮기면 자리도 옮겨져 이웃이 비켜 준다.
<div class="card">김치볶음밥</div>
<p>다음 문단</p>
.card {
box-sizing: border-box;
height: 100px;
border: 1px solid #d2d2d7;
background-color: #ffffff;
transform: translateY(30px);
}
p {
margin: 0;
}p의 위 자리108pxp의 맨 위 요소div.card
카드를 30px 내렸더니 아래 문단 위에 겹쳤습니다. 문단은 108px 그대로이고, 문단 가운데 자리에는 카드가 맨 위에 그려졌습니다. 자리가 옮겨졌다면 문단이 30px 비켜났을 것입니다.
맞는 규칙: transform은 그려진 모습만 옮깁니다. 이웃은 원래 자리를 보고 놓여 비켜 주지 않으니, 옮긴 쪽에 다른 내용이 있으면 겹칩니다(브라우저 규칙 8).
틀린 생각: transform을 두 줄로 쓰면 둘이 합쳐진다.
<div class="card">김치볶음밥</div>
.card {
width: 240px;
height: 100px;
transform: translateY(-4px);
transform: scale(1.02);
}.card의 위 자리7px
카드는 올라가지 않고 커지기만 했습니다(8 − 1 = 7). 두 선언은 한 속성 transform을 두고 겨뤄 나중 것이 이깁니다(브라우저 규칙 5).
맞는 규칙: 함께 할 일은 3절처럼 한 선언에 빈칸으로 잇습니다.
틀린 생각: 카드를 올리는 데는 margin-top이나 top을 써도 같다.
<div class="card">김치볶음밥</div>
<p>다음 문단</p>
.card {
box-sizing: border-box;
width: 240px;
height: 100px;
border: 1px solid #d2d2d7;
margin-top: -4px;
}
p {
margin: 0;
}.card의 위 자리4pxp의 위 자리104px
바깥 여백은 배치의 일이라 카드와 함께 아래 문단도 4px 따라 올라갔습니다. 목록이라면 아래 카드가 모두 움직입니다. top: -4px은 position이 처음 값 static이면 아무 일도 하지 않고, position: relative와 함께 쓰면 이웃은 그대로지만 배치에서 자리를 옮긴 것이라 배치 위 자리가 바뀝니다(14단원 일반 흐름과 position).
맞는 규칙: 마우스를 올렸을 때처럼 잠깐 보이는 움직임은 transform으로 합니다. 배치를 다시 하지 않아 이웃이 흔들리지 않습니다.
확인 문제
1. 예제에서 .recipe-card:hover의 값을 scale(1.05)로 바꿉니다. 마우스를 올린 첫 카드의 너비와 배치 너비는 각각 몇 px일까요? 숫자만 쓰세요.
힌트
2절에서 '너비'와 '배치 너비'가 각각 무엇을 재는지 찾아보세요.
정답
너비 252 · 배치 너비 240입니다.
<div class="recipe-card">
김치볶음밥
</div>
.recipe-card {
box-sizing: border-box;
width: 240px;
height: 100px;
border: 1px solid #d2d2d7;
}
.recipe-card:hover {
transform: scale(1.05);
}.recipe-card의 너비252px.recipe-card의 배치 너비240px
- 그려진 너비는 240 × 1.05 = 252입니다. 배치에 쓴 너비는
transform과 상관없이 240입니다. - 배치 너비도 252라고 썼다면
scale()이 상자 크기를 바꾼다고 본 것입니다.
2. 목록 링크에 마우스를 올리면 오른쪽으로 4px 옮겨 그리려 합니다. ____ 자리를 채우세요.
a:hover {
transform: ____;
}
힌트
1절 표 아래 줄에서 가로로 옮기는 함수와 부호를 찾아보세요.
정답
translateX(4px)입니다.
<a href="list.html">목록</a>
a {
display: inline-block;
}
a:hover {
transform: translateX(4px);
}a의 왼쪽 자리12pxa의 배치 왼쪽 자리8px
- 오른쪽은 양수입니다.
translateX(-4px)은 왼쪽으로 옮깁니다. - 결과 화면에서 해 보려면
a에display: inline-block을 줍니다 — 인라인 상자에는transform이 먹지 않습니다.
3. 동아리 공지 페이지입니다. 띠를 1.25배로 그렸을 때 아래 문단의 위 자리는 몇 px일까요?
<div class="band">신입 모집</div>
<p>모집은 금요일까지</p>
.band {
width: 120px;
height: 40px;
margin-left: 40px;
background-color: #fff3c4;
transform: scale(1.25);
}
p {
margin: 0;
}
- ① 48px
- ② 53px
- ③ 58px
힌트
transform이 없을 때 문단이 놓이는 자리를 먼저 셈해 보세요 — body의 바깥 여백은 8px입니다.
힌트 둘
scale()이 바꾸는 것이 배치인지 그려진 모습인지 2절에서 찾아보세요.
정답
①입니다.
p의 위 자리48px.band의 높이50px
- 문단은 띠의 원래 자리(8 + 40 = 48)를 보고 놓입니다. 띠는 50px 높이로 그려져 가운데에서 위아래로 5px씩 커지고, 문단 위 5px을 덮습니다.
- ②는 커진 만큼 반을, ③은 커진 만큼 다 문단을 민다고 본 것입니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
예제의 HTML과 CSS를 index.html과 style.css로 저장하고 link로 이어 엽니다(문서 뼈대의 body 안에 예제 HTML을 넣습니다). .recipe-card:hover의 값을 scale(1.1)로 바꿔 저장하고 새로고침합니다. 첫 카드에 마우스를 올려 둘째 카드가 밀리는지, 두 카드가 겹치는지 봅니다. 개발자 도구에서 첫 카드를 고르고 Computed 창의 width도 봅니다.