CSS中的旋转元素会正确影响其父母的身高


119

假设我有几列,我想旋转其中的一些值:

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>

使用此CSS:

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

最终看起来像这样:

一系列四个块级元素。 第三元素的文本被旋转。

是否有可能编写任何CSS来使旋转后的元素影响其父级的高度,从而使文本不会与其他元素重叠?像这样:

现在,第三列的文本会影响其父框的高度,以使文本适合该框。



7
现在,大多数浏览器都可以使用写作模式(以前​​是IE5功能),现在我可以使用jsfiddle.net/MTyFP/698参见:developer.mozilla.org/en-US/docs/Web/CSS/writing-mode
G-Cyrillus

1
@ G-CYR writing-mode用于大多数浏览器,但可怕的马车。在一个相关的问题上,我经历了多次迭代,尝试在放弃之前解决浏览器特定的错误。您可以在stackoverflow.com/posts/47857248/revisions上看到我的一系列失败,从那里我可以在Chrome上运行但不能在Firefox或Edge上运行,然后在修复其他两个过程中破坏Chrome,最后恢复我的状态。回答并标记为仅限Chrome。如果您想走这条路,请保持谨慎。(还要注意,它与非文本元素没有用。)
Mark Amery

1
afaik写作模式仅适用于文本...
Fredrik Johansson,

Answers:


51

假设您希望旋转90度,即使对于非文本元素也可以旋转-但是像CSS中许多有趣的事情一样,它需要一点技巧。根据CSS 2规范,我的解决方案还从技术上调用了未定义的行为-因此,尽管我已经测试并确认它可以在Chrome,Firefox,Safari和Edge中运行,但我不能保证将来不会损坏浏览器版本。

简短答案

给定这样的HTML,您想在其中旋转.element-to-rotate...

<div id="container">
  <something class="element-to-rotate">bla bla bla</something>
</div>

... 在要旋转的元素周围引入两个包装器元素:

<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>

...,然后使用以下CSS逆时针旋转(或查看注释,transform以将其更改为顺时针旋转):

.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;
}

堆栈片段演示

这是如何运作的?

我上面使用的魔咒面对的困惑是合理的;发生了很多事情,总体策略并不简单,需要一些CSS琐事知识才能理解。让我们逐步进行一下。

我们面临的问题的核心是,使用其transformCSS属性应用于元素的转换都在布局发生后发生。换句话说,transform在元素上使用完全不影响其父元素或任何其他元素的大小或位置。绝对没有办法改变这种transform工作原理。因此,为了产生旋转元素并使其父代的高度尊重旋转的效果,我们需要执行以下操作:

  1. 以某种方式构造高度等于宽度的其他元素.element-to-rotate
  2. 编写我们的转换,.element-to-rotate以便将其准确地覆盖在步骤1的元素上。

步骤1中的元素应为.rotation-wrapper-outer。但是,如何使它的高度等于.element-to-rotate宽度呢?

我们策略中的关键部分是padding: 50% 0on .rotation-wrapper-inner。此漏洞利用是该规范的怪异细节padding:即使对于padding-toppadding-bottom,填充百分比也总是定义为元素容器宽度的百分比。这使我们能够执行以下两步技巧:

  1. 我们设定display: table.rotation-wrapper-outer。这将导致其具有“ 收缩至适合”宽度,这意味着将根据其内容的固有宽度(即基于的固有宽度)来设置其宽度.element-to-rotate。(在支持的浏览器,我们可以更干净地做到这一点width: max-content,但截至2017年12月,max-content仍然不支持边)。
  2. 我们将heightof 设置.rotation-wrapper-inner为0,然后将其padding设置为50% 0(即50%顶部和50%底部)。这将导致它占用等于其父级宽度100%的垂直空间-通过第1步中的技巧,它等于的宽度.element-to-rotate

接下来,剩下的就是执行子元素的实际旋转和定位。自然地,transform: rotate(-90deg)旋转;我们transform-origin: top left;通常使旋转发生在旋转元素的左上角附近,这使得后续平移更容易推论,因为它使旋转元素直接位于原本应绘制的位置上方。然后,我们可以使用translate(-100%)向下拖动元素,使其距离等于其预旋转宽度。

那仍然不能完全正确地定位,因为我们仍然需要为上的50%顶部填充补偿.rotation-wrapper-outer。我们通过确保.element-to-rotate具有display: block(使边距在其上正常工作)然后应用-50%来实现这一点margin-top-请注意,相对于父元素的宽度定义了边距百分比。

就是这样!

为什么不完全符合规范?

由于以下规范中的填充百分比和边距定义(注释矿井),请注意以下几点

即使对于'padding-top''padding-bottom'而言,百分比也是相对于所生成的盒子的包含块宽度来计算如果包含块的宽度取决于此元素,则CSS 2.1中未定义结果布局。

由于整个技巧都围绕着使内部包装器元素的填充相对于其容器的宽度而定,而容器的宽度又取决于其子容器的宽度,因此我们遇到了这种情况并调用了不确定的行为。不过,它目前可在所有4种主要浏览器中使用-与我尝试过的一些看似符合规范的调整方法不同,例如更改.rotation-wrapper-inner为同级.element-to-rotate而不是父级。


1
我暂时接受了这个作为答案。该writing-mode解决方案将是最好的办法,如果IE 11并不需要得到支持。
克里斯(Chris

1
值得注意的收获:不适用于其他旋转(例如45度)。
E. Villiger

尽管已被接受,但这不是最新的正确答案。请参阅下面的使用写作模式的答案。
GilShalit

@ mark-amery,我必须为我的代码调整CSS:imgur.com/a/ZgafkgE第一张图片带有CSS,第二张带有调整后,我删除了transformOrigin:“左上角”,并更改了transform:“ translate(-100% )”,转换为:“ translate(20%)”,<div style = {{flexDirection:'column',alignItems:'center',}}> <div style = {{display:'table',margin:30 ,}}> <div style = {{padding:'50%0',height:0}}> <img src = {image} style = {{display:'block',marginTop:'-50%',transform :'rotate(-90deg)translation(20%)',maxWidth:300,maxHeight:400,}} /> </ div> </ div> <div> {title} </ div> </ div>
丹苦

44

正如G-Cyr的评论正确指出的那样,当前的支持writing-mode远非得体。结合简单的旋转,即可获得所需的精确结果。请参见下面的示例。

.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
很高兴我向下滚动-使用旋转和平移让我免于痛苦的世界。
詹姆斯·麦克劳克林

3
要使问题得到轮换,请使用writing-mode: vertical-lr; transform: rotate(180deg);
James McLaughlin,

40

不幸的是(?),即使您旋转元素,元素仍然具有一定的宽度和高度,旋转后也不会改变,这就是假定的工作方式。您可以从视觉上对其进行更改,但是当旋转事物时,没有不可见的包装盒会更改其大小。

想象一下,将其旋转不到90°(例如transform: rotate(45deg)):您将不得不引入这种不可见的框,该框现在基于要旋转的对象的原始尺寸和实际旋转值而具有模糊的尺寸。

旋转物体

突然,您不仅具有旋转对象的widthheight,而且还具有旋转对象的widthheight。想象一下,请求此对象的外部宽度-它会返回什么?对象的宽度,还是我们的新盒子?我们如何区分两者?

因此,没有可写的CSS来解决此问题(或者应该说“使其自动化”)。当然,您可以手动增加父容器的大小,或编写一些JavaScript来处理它。

(请注意,您可以尝试使用element.getBoundingClientRect来获得前面提到的矩形)。

规范中所述

在HTML命名空间中,transform属性不会影响转换元素周围的内容流。

这意味着除非您手动进行更改,否则将不会对要转换的对象周围的内容进行任何更改。

转换对象时唯一要考虑的是溢出区域:

(...)溢出区域的范围考虑了转换后的元素。此行为类似于通过相对定位使元素偏移时发生的情况。

请参阅此jsfiddle了解更多信息。

使用以下方法将这种情况与对象偏移量进行比较实际上是相当不错的:position: relative-即使您移动对象(示例),周围的内容也不会改变。


如果要使用JavaScript处理问题,请查看问题。


8
我要说的是,在普通的UI框架中,您的父母的宽度和高度应该包裹在孩子的边框的宽度和高度上,这正如您提到的孩子的旋转方式所影响。子项的宽度和高度是旋转前的宽度和高度,父项的宽度和高度由子项的bbox定义。这是非常基本的,我想说CSS / HTML应该可以这样工作……如果没有人会问这个问题,那只能用黑客的方式来解决。
user18490

25

使用百分比作为padding和伪元素来推送内容。在JSFiddle中,我将伪元素保留为红色以显示它,您必须补偿文本的偏移,但是我认为这是要走的路。

.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/

可以在这里找到该解决方案的摘要http : //kizu.ru/en/fun/rotated-text/


3
这种解决方案的问题在于,所述元件保持其原始宽度,因此,在(例如)一个表,如果它是最广泛的细胞,这将推动该列出宽与不必要的空白已采取了如果文本过没有旋转。真烦人
2014年

@litek,您能看看jsfiddle.net/MTyFP/7吗?我在图像方面几乎有同样的问题。问:stackoverflow.com/questions/31072959/...
Awlad LITON

11

请在其他答案中查看更新的版本。该答案不再有效,根本不再起作用。由于历史原因,我将其保留在此处。


这个问题是很老,但对于目前的支持.getBoundingClientRect()对象和它的widthheight值,在与一起使用此方法整齐的能力相结合transform,我觉得我的解决方案应该提到的为好。

看到它在这里行动。(已在Chrome 42,FF 33和37中测试。)

getBoundingClientRect计算元素的实际宽和高。然后,很简单地,我们可以遍历所有元素并将其最小高度设置为其子代的实际盒子高度。

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

(通过一些修改,您可以遍历子代,找出哪个子代最高,并将父代的高度设置为子代最高的子代。但是,我决定简化示例。您还可以将父代的填充添加到子代高度,也可以box-sizing在CSS中使用相关值。)

不过请注意,我在CSS中添加了translatetransform-origin,以使定位更加灵活和准确。

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

真?一个带有JQUERY答案的CSS问题,您称其为@MichaelLumbroso的完美答案?它的工作原理是可以的,但是不需要JavaScript或像jQuery这样的整个js库
Nebulosar

1
@Nebulosar好挖。在当时(已经两年半了!),这是唯一不会引起任何位置问题或伪元素运行不正常的答案。幸运的是,浏览器对某些属性的支持已得到改进。请看我的编辑。
Bram Vanroy

2
这本质上是两个完全不相关的答案。在一个问题中发布多个答案完全在规则之内;我认为,如果您在2017年添加的新解决方案是一个新的答案,那将是更好的选择,以便可以将两个完全独立的答案分别进行投票和评论。
Mark Amery

@MarkAmery你是对的。我编辑了问题,并在此处添加了新答案。
布拉姆·范罗伊

我能够使用此答案和图像加载事件来使用以下方法获得适用的图像解决方案:$ pic.on('load',function(){$ pic.css('min-height',$ pic [0] .getBoundingClientRect()。height);});
Miika L.

-18

我知道这是一篇过时的文章,但我在遇到完全相同的问题时发现了它。对我来说有效的解决方案是粗略的“低技术含量”方法,只需将我旋转90度的div包围很多

<br>

知道div的大致宽度(旋转后即变为高度),我可以通过在此div周围添加br来补偿差异,从而相应地推动上下内容。


10
也许这可行,但是您永远都不要这样做。
MMM 2014年

感谢您的反馈意见!是的,一种更好的方法是在旋转对象时使用div的margin-top属性调整像素的高度。
vic_sk 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.