为什么缩放CSS变换函数不影响边距?

9
我有一个包含多个div和元素的div。
现在我想要使用transform: scale(n)
但是当我使用它时,一切看起来都很好,除了内部div的边距保持相同大小。
但是内部元素的宽度会改变。

enter image description here


enter image description here

正如您所看到的,div的大小被减半了...但是边距仍然相同 :/
例如代码中,您有一个父级内部的div。这个子div具有100px的顶部边距
如果我改变父级的比例,子级的边距保持不变,但大小不同。您可以通过子级不随比例变化而移动的点来看到这一点。

.holder {
  background: pink;
  transform: scale(0.5);
}

#son {
  margin-top: 100px;
  padding: 20px;
  background: green;
}
<div class="holder">
  <div id="son">
    dontcare
  </div>
</div>


你的代码好像和我的不一样... 你能分享一下你的代码吗? - Temani Afif
你能分享一下你的代码吗? - Bart
谢谢大家,我添加了一个示例。 - Tzook Bar Noy
更新了我的回答,希望现在清楚了 ;) - Temani Afif
3个回答

7

简而言之

变换是相对于参考框进行的,参考框由transform-box属性设置,默认为border-box。边框框定了边框。因此,缩放变换将缩放边框内和包括边框(边框、填充、内容框)的所有内容。

详细回答

我将以以下示例开始我的回答:

button {
  padding: 10px;
  margin: 10px;
}

.scaled {
  transform: scale(2);
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
</head>
<body>
  <button class="default">Button</button>
  <button class="scaled">Button</button>
</body>
</html>

有两个按钮。两个都有10px的填充和10px的边距(以及默认的浏览器样式)。第二个被缩放了2倍。

在Firefox 58中,第一个按钮呈现以下属性:

Screenshot of box model view in Firefox DevTools of first button

我们将重点关注水平轴的属性和值:
        34.7167   // Content box width
        10        // Padding left
        10        // Padding right
         7        // Border left
         7     +  // Border right
    ------------
        68.7167
    ------------

        10         // Margin left
        10         // Margin right

如果将边距以外的所有内容加起来,您将得到元素的宽度。边距被排除在外。这是因为浏览器将元素框定义为包括边框内的一切内容。这种行为由box-sizing规则集指定为border-box
那么,这如何影响不缩放边距的scale变换函数呢?
变换相对于参考框发生。参考框由transform-box属性定义,默认值为border-box。因此,边框、填充和内容框尺寸将扩大2倍,而边距则不会。
这种行为可以在第二个按钮中观察到,该按钮被缩放:

Screenshot of box model view in Firefox DevTools of second button

再次,查看水平轴的属性和值:

    34.7167 * 2  =   69.4334   // Content box width
    10      * 2  =   20        // Padding left
    10      * 2  =   20        // Padding right
     7      * 2  =   14        // Border left
     7      * 2  =   14     +  // Border right
-----------------------------
    68.7167 * 2  =  137.4334
-----------------------------

    10                         // Margin left
    10                         // Margin right

进一步阅读


抱歉,这与什么有关系?顺便说一下,这很好解释...但是如果您检查他的示例,您会发现边距属于子元素而不是元素本身...所以问题是为什么内部边距应该包含在转换中,但却没有被考虑。 - Temani Afif
顺便提一下,检查开发工具并使用非常小的比例,您会发现边距也被考虑在内。 - Temani Afif
@TemaniAfif:啊,我没有看到父级上声明的变换规则。感谢您提出。但是我认为我的答案仍然成立,因为“变换是累积的。也就是说,元素在其父级坐标系内建立其本地坐标系”,而本地坐标系“其原点位于由transform-box属性指定的参考框的左上角。”引用自https://drafts.csswg.org/css-transforms/#transform-rendering。 - Wing
尝试一下并查看效果 :) 如果你缩放两个元素的容器,内边距会减少。他有两个主要问题,请查看我的答案以获取更多细节 ;) - Temani Afif
如果你点击链接,就会发现 transform-box 是一个试验性的功能,在较旧的浏览器上无法更改。 - Andris

4
您的第一张屏幕截图似乎显示了一个问题,即transform-origin默认为50%50%0(元素中心)。将其更改为左上角,您的元素将被缩小到左上角,因此margin将减少。


关于您分享的代码,这与比例无关,而是您面临margin-collapsing问题。子元素的边距与其父元素的边距折叠,同样的事情发生直到它与body边距折叠。换句话说,边距不再属于缩放的元素。
以下是您的初始代码与margin-collpasing:

body {
  background:linear-gradient(to bottom,#000 50%,transparent 0) 0 0/100% 100px;
}

.holder {
  animation:anim 3s infinite linear;
  transform-origin:top;
}

#son {
  margin-top: 100px;
  padding: 20px;
  background: green;
}

@keyframes anim {
  from {
    transform:scale(1);
  }
  to {
    transform:scale(0.3);
  }

}
<div class="holder">
  <div id="son">
    dontcare
  </div>
</div>

如果我们添加一点padding来去除margin合并的影响,同样的代码会是这样:

body {
  background:linear-gradient(to bottom,#000 50%,transparent 0) 0 0/100% 100px;
}

.holder {
  padding-top:1px;
  animation:anim 3s infinite linear;
  transform-origin:top;
}

#son {
  margin-top: 100px;
  padding: 20px;
  background: green;
}

@keyframes anim {
  from {
    transform:scale(1);
  }
  to {
    transform:scale(0.3);
  }

}
<div class="holder">
  <div id="son">
    dontcare
  </div>
</div>

您可以清楚地看到它们之间的区别。在第二个示例中,与第一个示例不同,边距是根据比例考虑的。
以下是一些有用的链接,以获取更多详细信息:

CSS边距恐怖;边距会在父元素外添加空白

https://dev59.com/nmkw5IYBdhLWcg3w_Pbn#9519933

CSS折叠边距的目的是什么?

https://css-tricks.com/what-you-should-know-about-collapsing-margins/


0

变换确实会影响视觉呈现,但除了影响溢出之外,对CSS布局没有任何影响。

来自W3.org《变换渲染模型》标准

以及这里的演示:

console.log('getComputedStyle:height: ' + getComputedStyle(box).height);
console.log('getComputedStyle:width: ' + getComputedStyle(box).width);
#box {
  width: 100px;
  height: 100px;
  background: pink;
  transform: scale(2, 2);
}
<div id="box">
  box
</div>

但是当我使用它时,一切看起来都很好,除了内部 div 的边距保持相同大小,但内部元素的宽度已经改变。您可以使用 getComputedStyle 来发现内部元素的宽度没有改变;

1
@Bart,也就是说,实际大小仍然与您设置之前相同,我尝试查找文档。 - xianshenglu
但在视觉上它们会改变,这就是问题的目的... 你的意思是没有任何改变,但他说宽度在改变而不是边距,所以我想他是在谈论视觉效果。 - Temani Afif
1
我猜他认为变换会改变宽度,实际上并没有,所有东西都在原位,只是视觉呈现发生了变化,这意味着你以为某些东西改变了,但实际上没有。 - xianshenglu
是的,但我们正在谈论视觉宽度... 我们都知道初始值不会改变... 但正如您在他的截图中所看到的,视觉上它正在改变,因此有这个问题。 - Temani Afif
当然会有视觉变化,你设置了 transform: scale 然后惊讶于视觉上的变化。我不明白是否有什么值得惊讶的。 - xianshenglu
他并不感到惊讶 :) 他只是不明白为什么宽度在视觉上发生了变化而边距没有……对我来说两者在视觉上都发生了变化,这并不清楚。 - Temani Afif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接