Menu

Transform CSS: translate, scale, skew và transform-origin

Thuộc tính transform di chuyển, phóng to thu nhỏ, xoay và làm nghiêng một phần tử mà không thay đổi bố cục xung quanh. Tìm hiểu mọi hàm transform, vì sao thứ tự các hàm quan trọng, transform-origin, các thuộc tính riêng translate, scale và rotate, và mẹo căn giữa bằng translate(-50%, -50%).

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

transform làm gì

transform thay đổi cách một phần tử được vẽ ra: dịch chuyển, phóng to thu nhỏ, xoay hoặc làm nghiêng. Các đường viền nét đứt bên dưới cho thấy mỗi hộp sẽ nằm ở đâu nếu không có transform:

translate(20px, 10px) dịch hộp xanh dương đi 20px theo chiều ngang và 10px xuống dưới. scale(1.3) làm hộp xanh lá lớn hơn 30% quanh tâm của nó. rotate(20deg) xoay hộp màu hổ phách theo chiều kim đồng hồ, còn skewX(-20deg) làm nghiêng hộp màu tối. Trong mọi trường hợp, ô nét đứt, tức khoảng trống mà phần tử chiếm trong bố cục, vẫn nằm nguyên chỗ cũ.

Các hàm transform

HàmTác dụngVí dụ
translate(x, y)dịch chuyểntranslate(20px, -10px), translate(-50%, -50%)
translateX(x) / translateY(y)dịch chuyển theo một trụctranslateY(-4px)
scale(s) / scale(x, y)đổi kích thước (1 = gốc, 0.5 = một nửa, số âm để lật gương)scale(1.1), scale(-1, 1)
scaleX(s) / scaleY(s)đổi kích thước theo một trụcscaleX(0.5)
rotate(angle)xoay theo chiều kim đồng hồ (số âm = ngược chiều kim đồng hồ)rotate(45deg), rotate(0.25turn)
skew(x, y) / skewX(a) / skewY(a)làm nghiêngskewX(-15deg)
matrix(a, b, c, d, e, f)tất cả những phép trên gộp trong một ma trận, chủ yếu là thứ trình duyệt trả về
nonekhông transform

Phần trăm trong translate() được tính theo kích thước của chính phần tử, không phải của phần tử cha. Phép xoay có trang riêng, với độ, vòng và hiệu ứng quay tròn: CSS rotate.

Thử nghiệm transform

Kéo các thanh trượt. Dòng đầu là giá trị transform bạn vừa tạo; dòng thứ hai là ma trận trình duyệt tính ra từ giá trị đó, cũng chính là thứ getComputedStyle trả về cho mọi transform:

Transform không làm thay đổi bố cục

Phần tử được transform được vẽ ở chỗ khác, nhưng trang vẫn được dàn bố cục như thể nó chưa hề bị transform. Các phần tử lân cận giữ nguyên vị trí. Chính điều này khiến transform rất hợp cho hiệu ứng hover và animation: không có gì xung quanh phải tính lại bố cục.

Cả hai hộp ở giữa đều trông lớn gấp 1,5 lần. Hộp dùng scale đè lên các hộp bên cạnh và C đứng yên; hộp được làm lớn bằng width và height thì đẩy C ra xa thêm 40px.

Nhiều transform và thứ tự của chúng

Đặt nhiều hàm trong một transform, ngăn cách bằng dấu cách. Thứ tự sẽ làm thay đổi kết quả, vì khi đọc từ trái sang phải, mỗi hàm hoạt động trong hệ tọa độ mà các hàm trước để lại:

Hộp xanh dương được dịch thẳng 120px rồi xoay tại chỗ. Hộp xanh lá được xoay trước, nên "trục x" của nó chỉ theo đường chéo, và translateX(120px) dịch nó xuống theo đường chéo đó. Cùng các hàm, nhưng ra vị trí khác nhau.

Cái bẫy còn lại: khai báo transform thứ hai sẽ thay thế khai báo đầu tiên. Nếu một card có transform: rotate(-2deg) và quy tắc hover của nó ghi transform: scale(1.05), phép xoay sẽ biến mất khi hover. Hãy viết lại cả hai (rotate(-2deg) scale(1.05)), hoặc dùng các thuộc tính riêng ở phần dưới.

transform-origin

Các phép phóng to thu nhỏ, xoay và làm nghiêng diễn ra quanh một điểm cố định, gọi là điểm gốc transform, mặc định nằm ở tâm. Bạn đổi điểm này bằng từ khóa, độ dài hoặc phần trăm:

Cả ba hộp đều xoay 25 độ. Hộp đầu tiên xoay quanh tâm, hộp thứ hai giữ góc trên bên trái gắn chặt vào góc của ô, hộp thứ ba giữ góc dưới bên phải. Một menu thả xuống mọc ra từ nút của nó sẽ dùng transform-origin: top cùng với scale().

Các thuộc tính translate, scale và rotate

Các trình duyệt hiện đại còn có ba thuộc tính riêng làm đúng việc của các hàm trên, nhưng có thể đặt và tạo animation độc lập với nhau:

.card {
  rotate: -2deg;
  transition: scale 0.2s ease-out;
}
.card:hover {
  scale: 1.05;   /* the rotation stays, because it is a different property */
}

Chúng luôn được kết hợp như thể viết transform: translate(...) rotate(...) scale(...) đứng trước mọi giá trị transform, bất kể bạn khai báo theo thứ tự nào. Lưu ý cú pháp: translate: 10px 20px và scale: 1.2 nhận giá trị mà không có dấu ngoặc của hàm.

Căn giữa bằng translate(-50%, -50%)

Vì phần trăm trong translate() tính theo kích thước của chính phần tử, top: 50%; left: 50%; transform: translate(-50%, -50%) căn giữa một phần tử định vị tuyệt đối với kích thước bất kỳ trong phần tử cha của nó. Trang căn giữa div so sánh cách này với flexbox và grid.

Lỗi thường gặp

  • Hai khai báo transform. Khai báo sau thắng hoàn toàn. Hãy gộp các hàm lại, hoặc dùng các thuộc tính rotate, scale và translate.
  • Transform một phần tử inline. Thẻ <span> hay <a> thông thường bỏ qua transform. Hãy đặt cho nó inline-block.
  • Tưởng các phần tử lân cận sẽ dịch theo. Transform không bao giờ thay đổi bố cục. Muốn tạo thêm khoảng trống, hãy đổi width, margin hoặc gap.
  • Sai thứ tự hàm. rotate() đứng trước translate() sẽ dịch theo trục đã bị xoay.
  • Bất ngờ với phần tử con position: fixed. Phần tử được transform trở thành khối chứa (containing block) cho các phần tử con định vị cố định, nên một header cố định bên trong sẽ cuộn theo nó. Nó cũng tạo ra một stacking context mới cho z-index.

Câu hỏi thường gặp

transform trong CSS dùng để làm gì?

Nó thay đổi cách một phần tử được vẽ ra: dịch chuyển (translate), đổi kích thước (scale), xoay (rotate) hoặc làm nghiêng (skew). Phần tử vẫn giữ vị trí ban đầu trong bố cục, nên các phần tử xung quanh không bị xê dịch.

Làm sao áp dụng nhiều transform trong CSS?

Liệt kê chúng trong cùng một giá trị transform, ngăn cách bằng dấu cách: transform: translateX(50px) rotate(20deg) scale(1.2);. Viết thêm một khai báo transform thứ hai sẽ thay thế khai báo đầu tiên chứ không cộng dồn.

Thứ tự các hàm transform có quan trọng không?

Có. Đọc từ trái sang phải, mỗi hàm hoạt động trên hệ trục mà các hàm trước để lại, nên rotate(45deg) translateX(100px) dịch phần tử theo một trục đã bị xoay, còn translateX(100px) rotate(45deg) dịch thẳng rồi mới xoay.

transform-origin là gì?

Là điểm cố định mà các phép phóng to thu nhỏ, xoay và làm nghiêng xoay quanh. Mặc định là tâm (50% 50%). transform-origin: top left khiến phần tử lớn lên từ góc trên bên trái, hoặc xoay quanh góc đó.

Vì sao transform không có tác dụng với thẻ span?

Transform không áp dụng cho các phần tử inline không thay thế, như thẻ <span> hay <a> thông thường. Đặt display: inline-block hoặc block cho phần tử là transform hoạt động.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu