Giả sử rằng bạn muốn xoay 90 độ, điều này là có thể, ngay cả đối với các yếu tố phi văn bản - nhưng giống như nhiều điều thú vị trong CSS, nó đòi hỏi một chút tinh ranh. Giải pháp của tôi về mặt kỹ thuật cũng gọi hành vi không xác định theo thông số CSS 2 - vì vậy trong khi tôi đã kiểm tra và xác nhận rằng nó hoạt động trong Chrome, Firefox, Safari và Edge, tôi không thể hứa với bạn rằng nó sẽ không bị hỏng trong tương lai phát hành trình duyệt.
Câu trả lời ngắn
Đưa ra HTML như thế này, nơi bạn muốn xoay .element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... giới thiệu hai phần tử bao quanh phần tử mà bạn muốn xoay:
<div id="container">
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<something class="element-to-rotate">bla bla bla</something>
</div>
</div>
</div>
... Và sau đó sử dụng CSS sau đây, để xoay ngược chiều kim đồng hồ (hoặc xem nhận xét transform
để biết cách thay đổi nó thành xoay theo chiều kim đồng hồ):
.rotation-wrapper-outer {
display: table;
}
.rotation-wrapper-inner {
padding: 50% 0;
height: 0;
}
.element-to-rotate {
display: block;
transform-origin: top left;
/* Note: for a CLOCKWISE rotation, use the commented-out
transform instead of this one. */
transform: rotate(-90deg) translate(-100%);
/* transform: rotate(90deg) translate(0, -100%); */
margin-top: -50%;
/* Not vital, but possibly a good idea if the element you're rotating contains
text and you want a single long vertical line of text and the pre-rotation
width of your element is small enough that the text wraps: */
white-space: nowrap;
}
Bản demo đoạn trích
p {
/* Tweak the visuals of the paragraphs for easier visualiation: */
background: pink;
margin: 1px 0;
border: 1px solid black;
}
.rotation-wrapper-outer {
display: table;
}
.rotation-wrapper-inner {
padding: 50% 0;
height: 0;
}
.element-to-rotate {
display: block;
transform-origin: top left;
/* Note: for a CLOCKWISE rotation, use the commented-out
transform instead of this one. */
transform: rotate(-90deg) translate(-100%);
/* transform: rotate(90deg) translate(0, -100%); */
margin-top: -50%;
/* Not vital, but possibly a good idea if the element you're rotating contains
text and you want a single long vertical line of text and the pre-rotation
width of your element is small enough that the text wraps: */
white-space: nowrap;
}
<div id="container">
<p>Some text</p>
<p>More text</p>
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<p class="element-to-rotate">Some rotated text</p>
</div>
</div>
<p>Even more text</p>
<img src="https://i.stack.imgur.com/ih8Fj.png">
<div class="rotation-wrapper-outer">
<div class="rotation-wrapper-inner">
<img class="element-to-rotate" src="https://i.stack.imgur.com/ih8Fj.png">
</div>
</div>
<img src="https://i.stack.imgur.com/ih8Fj.png">
</div>
Cái này hoạt động ra sao?
Nhầm lẫn khi đối mặt với câu thần chú tôi đã sử dụng ở trên là hợp lý; có rất nhiều điều đang diễn ra, và chiến lược tổng thể không đơn giản và đòi hỏi một số kiến thức về các câu đố CSS để hiểu. Chúng ta hãy đi qua nó từng bước một.
Cốt lõi của vấn đề chúng ta gặp phải là các phép biến đổi được áp dụng cho một phần tử sử dụng thuộc tính transform
CSS của nó đều xảy ra sau khi bố cục đã diễn ra. Nói cách khác, sử dụng transform
trên một phần tử hoàn toàn không ảnh hưởng đến kích thước hoặc vị trí của phần tử mẹ hoặc bất kỳ phần tử nào khác. Hoàn toàn không có cách nào để thay đổi thực tế này về cách thức transform
hoạt động. Do đó, để tạo hiệu ứng xoay một phần tử và có chiều cao của phần tử mẹ tôn trọng phép quay, chúng ta cần thực hiện các thao tác sau:
- Bằng cách nào đó xây dựng một số phần tử khác có chiều cao bằng chiều rộng của
.element-to-rotate
- Viết biến đổi của chúng ta trên
.element-to-rotate
như để phủ chính xác nó vào phần tử từ bước 1.
Phần tử từ bước 1 sẽ là .rotation-wrapper-outer
. Nhưng làm thế nào chúng ta có thể làm cho chiều cao của nó bằng chiều rộng.element-to-rotate
của nó ?
Thành phần chính trong chiến lược của chúng tôi ở đây là padding: 50% 0
trên .rotation-wrapper-inner
. Khai thác này là một chi tiết lập dị của thông số kỹ thuật chopadding
: phần trăm phần trăm, thậm chí cho padding-top
và padding-bottom
, luôn được định nghĩa là phần trăm chiều rộng của phần tử của phần tử . Điều này cho phép chúng tôi thực hiện thủ thuật hai bước sau:
- Chúng tôi thiết lập
display: table
trên .rotation-wrapper-outer
. Điều này làm cho nó có chiều rộng co lại vừa vặn , có nghĩa là chiều rộng của nó sẽ được đặt dựa trên chiều rộng nội tại của nội dung của nó - nghĩa là dựa trên chiều rộng nội tại của .element-to-rotate
. (Trên trình duyệt hỗ trợ, chúng ta có thể đạt được điều này sạch hơn với width: max-content
, nhưng kể từ tháng 12 năm 2017, max-content
là vẫn không được hỗ trợ tại Edge .)
- Chúng tôi đặt giá
height
trị .rotation-wrapper-inner
là 0, sau đó đặt phần đệm của nó thành 50% 0
(nghĩa là 50% trên cùng và 50% dưới cùng). Điều này khiến nó chiếm không gian dọc bằng 100% chiều rộng của cha mẹ - mà thông qua thủ thuật ở bước 1, bằng với chiều rộng của .element-to-rotate
.
Tiếp theo, tất cả những gì còn lại là để thực hiện xoay và định vị thực tế của phần tử con. Đương nhiên, transform: rotate(-90deg)
không quay; chúng ta sử dụng transform-origin: top left;
để làm cho phép quay xảy ra xung quanh góc trên bên trái của phần tử được xoay, điều này làm cho bản dịch tiếp theo dễ dàng hơn vì nó để phần tử xoay ngay phía trên nơi nó được vẽ. Sau đó, chúng ta có thể sử dụng translate(-100%)
để kéo phần tử xuống dưới một khoảng cách bằng chiều rộng trước khi quay của nó.
Điều đó vẫn không hoàn toàn đúng định vị, bởi vì chúng ta vẫn cần phải bù cho phần đệm hàng đầu 50% trên .rotation-wrapper-outer
. Chúng tôi đạt được điều đó bằng cách đảm bảo rằng .element-to-rotate
có display: block
(để các lề sẽ hoạt động chính xác trên nó) và sau đó áp dụng -50% margin-top
- lưu ý rằng tỷ lệ phần trăm cũng được xác định tương ứng với độ rộng của phần tử cha.
Và đó là nó!
Tại sao điều này không hoàn toàn tuân thủ thông số kỹ thuật?
Do các lưu ý sau từ định nghĩa về tỷ lệ phần trăm và tỷ suất lợi nhuận trong thông số kỹ thuật (khai thác mỏ):
Tỷ lệ phần trăm được tính tương ứng với chiều rộng của khối chứa của hộp được tạo , ngay cả đối với 'padding-top' và 'padding-bottom' . Nếu chiều rộng của khối chứa phụ thuộc vào thành phần này, thì bố cục kết quả không được xác định trong CSS 2.1.
Do toàn bộ mánh khóe xoay quanh việc làm cho phần đệm của phần tử bao bọc bên trong có liên quan đến chiều rộng của thùng chứa, nó phụ thuộc vào chiều rộng của con của nó, nên chúng ta gặp phải tình trạng này và gọi hành vi không xác định. Mặc dù vậy, nó hiện đang hoạt động trong cả 4 trình duyệt chính - không giống như một số điều chỉnh có vẻ phù hợp với cách tiếp cận mà tôi đã thử, như thay đổi .rotation-wrapper-inner
thành anh chị em .element-to-rotate
thay vì cha mẹ.