Các phần tử được xoay trong CSS ảnh hưởng chính xác đến chiều cao của cha mẹ chúng


119

Giả sử tôi có một vài cột, trong đó một số cột tôi muốn xoay các giá trị của:

http://jsfiddle.net/MTyFP/1/

<div class="container">
    <div class="statusColumn"><span>Normal</span></div>
    <div class="statusColumn"><a>Normal</a></div>
    <div class="statusColumn"><b>Rotated</b></div>
    <div class="statusColumn"><abbr>Normal</abbr></div>
</div>

Với CSS này:

.statusColumn b {
  writing-mode: tb-rl;
  white-space: nowrap;
  display: inline-block;
  overflow: visible;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
}

Nó kết thúc như thế này:

Một loạt bốn yếu tố cấp khối.  Văn bản của phần tử thứ ba được xoay.

Có thể viết bất kỳ CSS nào sẽ làm cho phần tử được xoay ảnh hưởng đến chiều cao của cha mẹ nó, sao cho văn bản không chồng lấp các phần tử khác? Một cái gì đó như thế này:

Bây giờ văn bản của cột thứ ba ảnh hưởng đến chiều cao của hộp cha mẹ sao cho văn bản vừa với hộp.



7
Chế độ viết hiện có sẵn cho hầu hết các trình duyệt (sử dụng là một tính năng của IE5) Tôi sẽ làm ngay hôm nay jsfiddle.net/MTyFP/698 xem: developer.mozilla.org/en-US/docs/Web/CSS/wr-mode
G-Cyrillus

1
@ G-Cyr writing-modecó sẵn cho hầu hết các trình duyệt, nhưng khủng khiếp lỗi. Về một câu hỏi liên quan, tôi đã trải qua nhiều lần cố gắng khắc phục các lỗi cụ thể của trình duyệt trước khi từ bỏ; bạn có thể thấy sự thất bại liên tiếp của tôi tại stackoverflow.com/posts/47857248/revutions , nơi tôi bắt đầu với một cái gì đó hoạt động trong Chrome nhưng không phải Firefox hoặc Edge, sau đó phá vỡ Chrome trong quá trình sửa hai cái kia và cuối cùng hoàn nguyên trả lời và gắn nhãn là chỉ Chrome. Hãy thận trọng, nếu bạn muốn đi theo con đường này. (Cũng lưu ý rằng nó không có tác dụng với các yếu tố phi văn bản.)
Mark Amery

1
Chế độ viết afaik chỉ hoạt động trên văn bản ...
Fredrik Johansson

Câu trả lời:


51

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

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 transformCSS của nó đều xảy ra sau khi bố cục đã diễn ra. Nói cách khác, sử dụng transformtrê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 transformhoạ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:

  1. 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
  2. Viết biến đổi của chúng ta trên .element-to-rotatenhư để 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% 0trê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-toppadding-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:

  1. Chúng tôi thiết lập display: tabletrê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-contentvẫn không được hỗ trợ tại Edge .)
  2. Chúng tôi đặt giá heighttrị .rotation-wrapper-innerlà 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-rotatedisplay: 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''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-innerthành anh chị em .element-to-rotatethay vì cha mẹ.


1
Tôi chấp nhận đây là câu trả lời cho thời điểm này. Các writing-modegiải pháp sẽ là cách tiếp cận tốt nhất nếu IE 11 không cần được hỗ trợ.
Chris

1
Bắt đáng chú ý: Không hoạt động cho các vòng quay khác như 45deg.
E. Villiger

Mặc dù được chấp nhận, đây không phải là câu trả lời chính xác. Xem bên dưới để biết câu trả lời bằng cách sử dụng chế độ viết.
GilShalit

@ mark-amery, tôi đã phải điều chỉnh CSS cho mã của mình: imgur.com/a/ZgafkgE Hình ảnh đầu tiên với CSS của bạn, thứ 2 với các điều chỉnh tôi đã loại bỏ biến đổi: 'trên cùng bên trái', và thay đổi biến đổi: 'dịch (-100% ) ', để chuyển đổi:' dịch (20%) ', <div style = {{flexDirection:' cột ', alignItems:' centre ',}}> <div style = {{display:' table ', lề: 30 ,}}> <div style = {{padding: '50% 0 ', height: 0}}> <img src = {image} style = {{display:' block ', marginTop:' -50% ', biến đổi : 'Xoay (-90deg) dịch (20%)', maxWidth: 300, maxHeight: 400,}} /> </ div> </ div> <div> {title} </ div> </ div>
Dan Đắng

44

Như nhận xét của G-Cyr đã chỉ ra một cách đúng đắn, sự hỗ trợ hiện tại không chỉ writing-modelà tốt . Kết hợp với một vòng xoay đơn giản, bạn sẽ có được kết quả chính xác mà bạn muốn. Xem ví dụ dưới đây.

.statusColumn {
  position: relative;
  border: 1px solid #ccc;
  padding: 2px;
  margin: 2px;
  width: 200px;
}

.statusColumn i,
.statusColumn b,
.statusColumn em,
.statusColumn strong {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
  display: inline-block;
  overflow: visible;
}
<div class="container">
  <div class="statusColumn"><span>Normal</span></div>
  <div class="statusColumn"><a>Normal</a></div>
  <div class="statusColumn"><b>Rotated</b></div>
  <div class="statusColumn"><abbr>Normal</abbr></div>
</div>


1
Vui mừng khi tôi cuộn xuống - điều này đã cứu tôi khỏi một thế giới đau đớn bằng cách xoay và dịch.
James McLaughlin

3
Để có được vòng quay được yêu cầu trong câu hỏi, hãy sử dụngwriting-mode: vertical-lr; transform: rotate(180deg);
James McLaughlin

40

Thật không may (?) Đây là cách nó giả sử hoạt động ngay cả khi bạn xoay phần tử của mình, nó vẫn có chiều rộng và chiều cao nhất định, điều đó không thay đổi sau khi xoay. Bạn trực quan thay đổi nó, nhưng không có hộp gói vô hình thay đổi kích thước của nó khi bạn xoay mọi thứ.

Hãy tưởng tượng xoay nó dưới 90 ° (ví dụ transform: rotate(45deg)): bạn sẽ phải giới thiệu hộp vô hình như vậy hiện có kích thước mơ hồ dựa trên kích thước ban đầu của đối tượng bạn đang quay và giá trị xoay thực tế.

Đối tượng xoay

Đột nhiên, bạn không chỉ có widthheightcủa đối tượng bạn đã xoay, mà bạn còn có widthheight"chiếc hộp vô hình" xung quanh nó. Hãy tưởng tượng yêu cầu chiều rộng bên ngoài của đối tượng này - nó sẽ trả về cái gì? Chiều rộng của đối tượng, hoặc hộp mới của chúng tôi? Làm thế nào chúng ta sẽ phân biệt giữa cả hai?

Do đó, không có CSS ​​mà bạn có thể viết để sửa hành vi này (hoặc tôi nên nói là "tự động hóa" nó). Tất nhiên, bạn có thể tăng kích thước của vùng chứa cha mẹ bằng tay hoặc viết một số JavaScript để xử lý việc đó.

(Để rõ ràng, bạn có thể thử sử dụng element.getBoundingClientRectđể có được hình chữ nhật được đề cập trước đó).

Như được mô tả trong thông số kỹ thuật :

Trong không gian tên HTML, thuộc tính biến đổi không ảnh hưởng đến luồng nội dung xung quanh thành phần được chuyển đổi.

Điều này có nghĩa là sẽ không có thay đổi nào đối với nội dung xung quanh đối tượng bạn đang chuyển đổi, trừ khi bạn thực hiện chúng bằng tay.

Điều duy nhất được tính đến khi bạn chuyển đổi đối tượng của mình là khu vực tràn:

(...) phạm vi của khu vực tràn có tính đến các yếu tố được chuyển đổi. Hành vi này tương tự như những gì xảy ra khi các yếu tố được bù qua định vị tương đối .

Xem jsfiddle này để tìm hiểu thêm.

Thật sự khá tốt khi so sánh tình huống này với một đối tượng bù bằng cách sử dụng: position: relative- nội dung xung quanh không thay đổi, mặc dù bạn đang di chuyển đối tượng của mình ( ví dụ ).


Nếu bạn muốn xử lý việc này bằng JavaScript, hãy xem câu hỏi này .


8
Tôi sẽ nói rằng trong một khung UI bình thường, chiều rộng và chiều cao của bố mẹ bạn nên được bao bọc xung quanh chiều rộng và chiều cao của hộp giới hạn của trẻ bị ảnh hưởng như bạn đã đề cập trong các phép quay của trẻ. Chiều rộng và chiều cao của trẻ em là chiều rộng và chiều cao của chúng trước khi quay và chiều rộng và chiều cao của bố mẹ được xác định bởi hộp của trẻ em. Điều đó rất cơ bản và tôi muốn nói CSS / HTML sẽ hoạt động theo cách đó ... Nếu không ai sẽ đặt câu hỏi về chủ đề này mà chỉ có thể khắc phục trong cách cư xử không thỏa đáng với các bản hack.
dùng18490

25

Sử dụng tỷ lệ phần trăm cho paddingvà một yếu tố giả để đẩy nội dung. Trong JSFiddle tôi đã để phần tử giả màu đỏ hiển thị nó và bạn sẽ phải bù phần dịch chuyển của văn bản nhưng tôi nghĩ đó là cách để đi.

.statusColumn {
    position: relative;
    border: 1px solid #ccc;
    padding: 2px;
    margin: 2px;
    width: 200px;
}

.statusColumn i, .statusColumn b, .statusColumn em, .statusColumn strong {
  writing-mode: tb-rl;
  white-space: nowrap;
  display: inline-block;
  overflow: visible;
  -webkit-transform: rotate(-90deg);
  -moz-transform: rotate(-90deg);
  -ms-transform: rotate(-90deg);
  -o-transform: rotate(-90deg);
  transform: rotate(-90deg);
  -webkit-transform: rotate(-90deg);

  /* also accepts left, right, top, bottom coordinates; not required, but a good idea for styling */
  -webkit-transform-origin: 50% 50%;
  -moz-transform-origin: 50% 50%;
  -ms-transform-origin: 50% 50%;
  -o-transform-origin: 50% 50%;
  transform-origin: 50% 50%;

  /* Should be unset in IE9+ I think. */
  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
}
.statusColumn b:before{ content:''; padding:50% 0; display:block;  background:red; position:relative; top:20px
}
<div class="container">
    <div class="statusColumn"><span>Normal</span></div>
    <div class="statusColumn"><a>Normal</a></div>
    <div class="statusColumn"><b>Rotated</b></div>
    <div class="statusColumn"><abbr>Normal</abbr></div>
</div>

http://jsfiddle.net/MTyFP/7/

Bạn có thể tìm thấy một bài viết về giải pháp này tại đây: http://kizu.ru/en/fun/rotated-text/


3
Vấn đề với giải pháp này là phần tử giữ nguyên chiều rộng ban đầu của nó, vì vậy, trong (ví dụ) một bảng, nếu đó là ô rộng nhất, nó sẽ đẩy cột đó rộng ra với khoảng trắng không cần thiết sẽ được đưa lên nếu văn bản có không được luân chuyển. Điều đó thật khó chịu.
Jez

@litek, bạn có thể xem jsfiddle.net/MTyFP/7 không? Tôi đang có vấn đề gần như tương tự với hình ảnh. Câu hỏi: stackoverflow.com/questions/31072959/ cường
Awlad Liton

11

Xin vui lòng xem phiên bản cập nhật trong câu trả lời khác của tôi . Câu trả lời này không còn hiệu lực và đơn giản là không còn hoạt động nữa. Tôi sẽ để nó ở đây vì lý do lịch sử.


Câu hỏi này là khá cũ, nhưng với sự hỗ trợ hiện tại cho các .getBoundingClientRect()đối tượng và nó widthheightgiá trị, kết hợp với khả năng sử dụng phương pháp này gọn gàng cùng với transform, tôi nghĩ rằng giải pháp của tôi nên được đề cập là tốt.

Xem nó trong hành động ở đây . (Đã thử nghiệm trong Chrome 42, FF 33 & 37.)

getBoundingClientRecttính toán chiều rộng và chiều cao hộp thực tế của phần tử. Rất đơn giản, sau đó, chúng ta có thể lặp qua tất cả các yếu tố và đặt chiều cao tối thiểu của nó thành chiều cao hộp thực tế của con cái chúng.

$(".statusColumn").each(function() {
    var $this = $(this),
        child = $this.children(":first");
    $this.css("minHeight", function() {
        return child[0].getBoundingClientRect().height;
    });
});

(Với một số sửa đổi, bạn có thể vòng qua các con và tìm ra con nào cao nhất và đặt chiều cao của bố mẹ thành con cao nhất. Tuy nhiên, tôi quyết định làm ví dụ đơn giản hơn. Bạn cũng có thể thêm phần đệm của bố mẹ vào chiều cao nếu bạn muốn hoặc bạn có thể sử dụng một box-sizinggiá trị có liên quan trong CSS.)

Tuy nhiên, xin lưu ý rằng tôi đã thêm một translatetransform-originvào CSS của bạn để giúp định vị linh hoạt và chính xác hơn.

transform: rotate(-90deg) translateX(-100%);
transform-origin: top left;

Có thật không? Một câu hỏi css với câu trả lời JQUERY và bạn gọi đó là câu trả lời hoàn hảo @MichaelLumbroso? Nó hoạt động có, nhưng không cần javascript hoặc toàn bộ thư viện js như jQuery
Nebulosar

1
@Nebulosar Đào đẹp. Vào thời điểm đó (đã hai năm rưỡi trước!) Đây là câu trả lời duy nhất không gây ra bất kỳ vấn đề nào với các yếu tố định vị hoặc giả không hoạt động tốt. May mắn thay, hỗ trợ trình duyệt cho một số thuộc tính đã được cải thiện. Xin vui lòng xem chỉnh sửa của tôi.
Bram Vanroy

2
Đây thực chất là hai câu trả lời hoàn toàn không liên quan trong một. Đó là hoàn hảo trong các quy tắc để gửi nhiều câu trả lời cho một câu hỏi; Tôi nghĩ rằng sẽ tốt hơn nếu giải pháp mới của bạn được thêm vào năm 2017 là một câu trả lời mới, để hai câu trả lời hoàn toàn riêng biệt có thể được bình chọn và nhận xét riêng.
Đánh dấu Amery

@MarkAmery Bạn nói đúng. Tôi đã chỉnh sửa câu hỏi và thêm một câu trả lời mới ở đây .
Bram Vanroy

Tôi đã có thể sử dụng câu trả lời này và sự kiện tải hình ảnh để có được giải pháp làm việc cho hình ảnh bằng cách sử dụng: $ pic.on ('load', function () {$ pic.css ('min-height', $ pic [0] .getBoundingClientRect (). height);});
Miika L.

-18

Tôi biết đó là một bài viết cũ nhưng tôi đã tìm thấy nó trong khi đấu tranh với chính xác cùng một vấn đề. Giải pháp hiệu quả với tôi là phương pháp "công nghệ thấp" khá thô sơ bằng cách đơn giản bao quanh div I xoay 90deg với rất nhiều

<br>

Biết chiều rộng gần đúng (trở thành chiều cao sau khi xoay) của div Tôi có thể bù chênh lệch bằng cách thêm br quanh div này để nội dung bên trên và bên dưới được đẩy tương ứng.


10
Có thể điều này hoạt động nhưng bạn không bao giờ nên làm điều này.
MMM

Cảm ơn phản hồi của bạn! Có, cách tốt hơn là tôi sử dụng thuộc tính lề trên của div để điều chỉnh chiều cao tính bằng pixel khi xoay đối tượng.
Abbey_sk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.