前端开发者学堂 - fedev.cn

CSS中百分比单位计算方式

发布于 大漠

CSS中的单位有很多类型,不同类型单位计算方式也有所不同,特别是**百分比(%)**单位,它的计算和使用的属性有着直接关系。其中最为重要的是:百分比是一定要有其对应的参照值,也就是说,百分比值是一种相对值,任何时候要分析它的计算值,都需要正确的找到它的参照值。言外之意,CSS中的百分比单位值最终计算出来的值是可变的。在这篇文章中,我们主要来看看百分比单位在不同使用场景中计算方式。

在CSS中能运用%值的属性很多,为了能让大家更好的理解使用%的规则,我们从最熟悉和最简单地属性开始。

font-size中的%

font-size属性中,有的时候会取%为单位,比如:

:root {
    --font-size: 100%
}

h1 {
    font-size: var(--font-size)
}

font-size的值为%值时,它的计算是相对于父元素的font-size来计算,如果父元素(以及它的祖先元素)未显式设置font-size值的话,将会以浏览器的默认值16px为基准。比如下面这个示例:

<body>
    <div class="container">
        <h1>font-size</h1>
    </div>
</body>

:root {
    --font-size: 100%;
}

h1 {
    font-size: var(--font-size);
}

设置了%的元素将会根据它的DOM层级从底层向上层寻找,将会相对于离元素最近的父元素的font-size值计算。比如上面示例:

如果div.container显式设置了font-size的值,那么h1font-size: 100%会相对于div.containerfont-size值计算;反之往上寻找div.container的父元素(本例中对应的是body)的font-size。依此类推,直到寻找到显式设置了font-size值的祖先元素。这个过程有点类似于JavaScript中的事件冒泡的过程:

比如下面这个示例,你拖动下面的滑块,可以看到<h1>元素font-size的变化:

示例中body显示设置了font-size: 100%,计算出来的px值是16px,而且该示例<h1>font-size是相对于<body>来计算。我们可以用下面的表格来整个计算的变化过程:

font-size font-size: 100% (<body>) <h1>计算后的font-size
50% 100% x 16 = 16px 50% ❯ 50% x 16 = 8px
100% 100% x 16 = 16px 100% ❯ 100% x 16 = 16px
150% 100% x 16 = 16px 150% ❯ 150% x 16 = 24px
200% 100% x 16 = 16px 200% ❯ 200% x 16 = 32px

在上面的示例上稍作调整,把div.containerfont-size也设置为%值:

.container {
    font-size: var(--font-size)
}

这个时候<h1>font-size会相对于它的父元素div.containerfont-size计算,而div.container会相对于它的父元素bodyfont-size计算。整体的效果如下:

同样用表格来描述它们的计算过程:

font-size font-size: 100% (<body>) <div.container>计算后的值 <h1>计算后的font-size
50% 100% x 16 = 16px 50% ❯ 50% x 16 = 8px 50% ❯ 50% x 8 = 4px
100% 100% x 16 = 16px 100% ❯ 100% x 16 = 16px 100% ❯ 100% x 16 = 16px
150% 100% x 16 = 16px 150% ❯ 150% x 16 = 24px 150% ❯ 150% x 24 = 36px
200% 100% x 16 = 16px 200% ❯ 200% x 16 = 32px 200% ❯ 200% x 32 = 64px

如果在每个DOM元素的font-size都采用%值,那么越在DOM底层的元素,其font-size计算越复杂。

line-height中的%

CSS中的line-height取值为%时,它的计算方式是基于元素自身的font-size的值来计算。如果元素自身未显式设置font-size,则会基于元素继承过来的font-size的值计算。

比如下面这个示例:

<!-- HTML -->
<body>
    <h1></h1>
    <p></p>
    <footer></footer>
</body>

/* CSS */
body {
    font-size: 16px;
    line-height: 120%;
}

h1 {
    font-size: 32px;
}

p {
    font-size: 16px;
}

footer {
    font-size: 12px;
}

注意,line-heightfont-size类似,也是一个继承属性,就上面这个示例,每个元素的line-height计算出来的值如下表所示:

元素 font-size line-height 计算出来的line-height
body 16px 120% 120% ❯ 16px x 120% = 19.2px
h1 32px 继承bodyline-height 19.2px
p 16px 继承bodyline-height 19.2px
footer 12px 继承bodyline-height 19.2px

上面的示例中稍作调整,如果显式的在每个元素中设置font-size的值都是120%,这个时候每个元素的line-height的就会像下表这样:

元素 font-size line-height 计算出来的line-height
body 16px 120% 120% ❯ 16px x 120% = 19.2px
h1 32px 120% 120% ❯ 32px x 120% = 38.4px
p 16px 120% 120% ❯ 16px x 120% = 19.2px
footer 12px 120% 120% ❯ 12px x 120% = 14.4px

你可能看出它们之间的差异了。

上面的示例还算是简单的,因为每个元素的font-size都是固定单位px的值。如果元素的font-size是个%值,而且元素还有相应的嵌套关系,那么每个元素的line-height计算会更复杂一些。它的计算要分两个步骤:

  • 根据font-size的计算原理,先计算出每个元素自身的font-size
  • 元素的line-height根据元素自身的font-size再计算出line-height的值

来看一个简单的示例:

<!-- HTML -->
<body>
    <div class="container">
        <h1></h1>
    </div>
</body>

/* CSS */
:root {
    --font-size: 100%;
}

body {
    font-size: 100%;
    line-height: 150%;
}

.container {
    font-size: var(--font-size)
}

h1 {
    font-size: var(--font-size)
    line-height: 120%
}

你可以尝试着拖动下面Demo中的滑块,查看bodydiv.containerh1三个元素的line-height的值变化:

  • 先来看<html>元素,该元素显式的设置了line-height值为1.15,计算出来的line-height的值为18.4px
  • <body>元素显式设置了font-size100%line-height的值为150%,计算出来的line-height24px(即100% x 16px x 150% = 24px);
  • div.container元素会继承<body>line-height,即计算出来的值是24px,即使font-size的值有所改变,但该元素并没有显式设置line-height的值,因此该元素的line-height不受元素自身的font-size值影响

最为复杂的是h1元素,该元素显式设置了font-size的值和line-height的值,而且它们的值都是百分比。虽然line-height的计算是基于font-size,并不复杂;但元素的font-size百分值计算相对而言较为复杂,特别是在层级复杂的情况之下。就该示例而言,它的计算过程如下表所示:

font-size font-size: 100% (<body>) <div.container>计算后的font-size <h1>计算后的font-size h1line-height 计算出来的h1line-height
50% 100% x 16 = 16px 50% ❯ 50% x 16 = 8px 50% ❯ 50% x 8 = 4px 120% 120% x 4px = 4.8px
100% 100% x 16 = 16px 100% ❯ 100% x 16 = 16px 100% ❯ 100% x 16 = 16px 120% 120% x 16px = 19.2
150% 100% x 16 = 16px 150% ❯ 150% x 16 = 24px 150% ❯ 150% x 24 = 36px 120% 120% x 36px = 43.199px
200% 100% x 16 = 16px 200% ❯ 200% x 16 = 32px 200% ❯ 200% x 32 = 64px 120% 120% x 64px = 76.8px

CSS中使用line-height的时候,使用不带单位的因子数场景更多,比如line-height: 1.5。它的计算也是基于元素的font-size值来计算的,只不过继承过来的line-height如果是因子数,那么计算的结果和%是有所差异的。比如:

<!-- HTML -->
<body>
    <h1></h1>
    <p></p>
    <footer></footer>
</body>

/* CSS */
body {
    font-size: 16px;
    line-height: 1.2;
}

h1 {
    font-size: 32px;
}

p {
    font-size: 16px;
}

footer {
    font-size: 12px;
}

这个时候每个元素的line-height计算出来的值如下表所示:

元素 font-size line-height 计算出来的line-height
body 16px 1.2 1.2 ❯ 16px x 1.2 = 19.2px
h1 32px 继承bodyline-height1.2 1.2 ❯ 32px x 1.2 = 38.4px
p 16px 继承bodyline-height1.2 1.2 ❯ 16px x 1.2 = 19.2px
footer 12px 继承bodyline-height1.2 1.2 ❯ 12px x 1.2 = 14.4px

CSS中的line-height是一个特复杂的属性,它的大小除了受元素font-size的值影响之外,同时也受font-family的影响。有关于line-height更详细的介绍可以阅读《深入了解CSS字体度量,行高和vertical-align》一文

vertical-align中的%

CSS的vertical-align取值为%时,它的计算方式是基于元素自身的line-height值计算。如果元素自身未显式设置line-height的值,那么元素会继承其父元素(或祖先元素)的line-height值。

line-height会受font-size的影响,这样一来,font-size也会影响取值为%vertical-align

body {
    font-size: 100%;
    line-height: 150%;
}

.container {
    font-size: 2rem; // -> 2 x 16 = 32px
}

h1 {
    line-height: 120%; 
    font-size: 2em; // -> 2 x 32 = 64px
    // line-height 计算出来的值 64 x 1.2 = 76.8px
}

#per {
    vertical-align: 100%; 
    // i 继承 h1的 line-height, 即 76.8px
    // -> 100% x 76.8 = 76.8px
}

text-indent中的%

CSS的text-indent属性可以让一个块元素首行文本内容的缩进量。有的时候也会使用该方法实现图像替换文本(也属于Web隐藏术中的一种)。

同样的,text-indent取值也可以是个%值,它的计算是相对于元素块width(或height)计算,如果是逻辑属性的话,相对于inline-size(或block-size)计算。具体相对于widthinline-size)还是heightblock-size)取决于CSS的书写模式(即受writing-modedirection)。

这里以大家最为熟悉的书写模式为例,即水平方向书写模式。那么text-indent%值是相对于width(或inline-size)的值计算:

上面聊的都是有关于Web排版中用到的CSS属性,接下来我们来看看CSS中盒模型中属性(比如widthheightmarginpadding)取%值的计算方式(这些属性也是元素尺寸设置常见的属性)。

widthheight中的%

先来看元素尺寸设置中的widthheight属性:

  • 元素的width属性取%值时,它的计算是相对于元素父容器的width计算
  • 元素的height属性取%值时,它的计算是相对于元素父容器的height计算

我们来一个示例。

<!-- HTML -->
<div class="container"> 
    <div class="element"></div>
</div>

其中.element元素是.container的子元素。.element元素的widthheight取值为%值,为了更好的通过示例向大家演示,这里将该元素的父元素.containerwidthheight显式设置一个固定值,比如:

.container {
    width: 500px;
    height: 200px;
    border: 1px dashed #f36;
}

:root {
    --width: 50%;
    --height: 50%;
}

.element {
    width: var(--width);
    height: var(--height);
    background-color: #09f;
}

你可以尝试着拖动示例中的滑块,查看具体的效果:

margin中的%

marginmargin-topmargin-rightmargin-bottommargin-left的简写属性。这几个属性也可以取%的值。如果margin%的值,它的计算是相对于像容器(父元素)的width来计算。

我们在上面的示例基础上,稍作调整后的效果如下:

你操作上面的Demo,你可能发现了,显式给.element元素设置margin-leftmargin-right值,而且都同时为正值时,元素自身的空间会被margin-leftmargin-right挤压,当margin-leftmargin-right两个属性的值之和等元素元素width时,该元素将在视图中不可见:

另外,元素margin-left的值为负值时,元素会往左拉:

元素margin-right的值为正值时且大到一定的值(一般容器剩余空间),那么将会挤压元素自身的width

CSS的margin属性在CSS领域既是一个基础属性,但也是一个复杂的体系,如果你想彻底了解该属性,建议您花时间阅读下面几篇文章:

padding中的%

padding是CSS盒模型中的另一个属性,用来设置元素内容与元素边框之间的间距(常被称为内距),该属性也会影响影响盒模型的大小,只不过受box-sizing的值的影响。

注意,接下来的示例中,box-sizing取值为border-box。如果你对box-sizing属性不太了解的话,建议你花点时间阅读《CSS3 Box-sizing》一文

同样来看一个示例:

.container {
    width: 500px;
    height: 300px;
}

:root {
    --padding-top: 0;
    --padding-right: 0;
    --padding-bottom: 0;
    --padding-left: 0;
}

.element {
    padding: var(--padding-top) var(--padding-right) var(--padding-bottom)
    var(--padding-left);
}

可以尝试着拖动下面Demo中的滑块,调整每个方向的padding值,你会发现元素.element大小会改变(其中#09f)就是内距区域:

注意,负值对padding属性无效

marginpaddingwidthheight是CSS盒模型中可以用%定义值的属性,不过这几个属性都是CSS的物理属性,随着CSS逻辑属性的到来,对盒模型的定义带来了新的变化,在CSS的逻辑属性都有和物理属性相匹配的属性。也就是说,上面的百分比计算方式同样适用于CSS逻辑属性(指的是与marginpaddingwidthheight相匹配的属性)。不同的是,CSS逻辑属性会受CSS的书写模式的影响。

除此之外,CSS中的min-widthmax-width也可以取%值,它的计算方式和width相同;min-heightmax-height%值计算方式和height相同。

偏移中的%

CSS中定位框(显式设置了值为非staticposition元素)的精确位置是由**嵌入(Inset Properties)**属性来控制。其中物理嵌入属性有toprightbottomleft;而逻辑嵌入属性有inset-block-startinset-block-endinset-inline-startinset-inline-end以及它们的简写属性inset-blockinset-inlineinset

CSS偏移属性是隶属于CSS Positioned Layout Module Level 3模块,有关于这方面更详细的介绍可以阅读Web布局系列中的《CSS定位和层叠控制》一文

这些偏移属性都可以取%值,不过他的计算方式相对而言要复杂一些,因为它的计算取决于元素自身position所设置的值。

当元素position的取值为relativeabsolute时,那么元素的偏移属性的%值计算相对于离元素最近的设置了position为非static的元素的widthheight。其中topbottom相对于height计算;leftright相对于width计算。

我们来看一个最简单的示例:

<!-- HTML -->
<div class="container">
    <div class="element"></div>
</div>

/* CSS */
.container {
    width: 500px;
    height: 300px;
    position: relative;
}

:root {
    --top: 0;
    --right: 0;
    --bottom: 0;
    --left: 0;
}

.element {
    top: var(--top);
    right: var(--right);
    bottom: var(--bottom);
    left: var(--left);
    position: absolute;
}

你可以尝试着拖动下面示例中的滑块,查看效果:

但如果你将结构稍作调整:

<!-- HTML -->
<div class="wrapper">
    <div class="container">
        <div class="element"></div>
    </div>
</div>

/* CSS */
.wrapper {
    width: 40vw;
    height: 40vh;
    position: relative;
}

.container {
    width: 30vw;
    height: 30vh;
}

:root {
    --top: 20%;
    --left: 20%;
}

.element {
    top: var(--top);
    left: var(--left);
    position: absolute;
}

这个时候,.elementtopleft是相对于div.wrapperheightwidth计算:

我们来看计算出来的结果:

对于position取值为absolute的元素,如果找不到最近的祖先元素有显式设置position(非static)值,那么元素的偏移属性会相对于视窗(Viewport)的widthheight来计算。

如果元素的position取值为fixed时,一般情况之下,元素的偏移属性取%值的计算是相对于视窗的widthheight。不过CSS的transform对于position值为fixed的元素有所影响,如果固定定位元素的祖先元素显式设置了transform属性,那么该固定元素会相对于设置了transform的祖先元素定位,这个时候取值为%的偏移属性的计算也会相对于该元素的widthheight,将不会再相对于视窗计算。

特别声明,如果固定定位元素的祖先元素显式设置了contain:paint的话,其计算方式和祖先元素显式设置transform等同

如果元素的position显式设置值为sticky,那么偏移属性取%值的计算是相对于相关的滚动端口(Scrollport的大小计算

滚动端口(Scrollport)是指文档(Document)中滚动容器(Scroll Container)的可视窗口。显式给元素设置overflow: scrolloverflow: auto(在内容足够导出溢出时产生)可以创建滚动容器

border-radius中的%

border-radius属性相对于前面几个属性要复杂一点。不过@supremebeing09的《CSS Border-Radius Can Do That?》对border-radius做过详细的阐述。如果你不愿意花时间阅读文章的话,可以用几张简单的图来阐述border-radius属性。

border-radius是一个简写属性,是border-top-left-radiusborder-top-right-radiusborder-bottom-right-radiusborder-bottom-left-radius四个属性的简写属性。这几个属性可以接受一个值,也可以接受两个值<length-percentage>{1,2},简写语法:

border-radius: <length-percentage>{1,4} [ / <length-percentage>{1,4} ]?

其中/前面的值表示x轴的半径,/后面的值表示y轴的半径,如果没有显式使用/表示xy的值相同,比如像下图这样:

同样的,border-radius也可以接受%的值,如果像border-radius: 30%表示每个角的圆角半径(x轴和y轴)都是30%

border-radius使用%值时,它计算的相对值是需要分开来算的,其中 x轴的%值相对于元素的width值计算;y轴的%值相对于元素的height值计算,比如:

.element {
    width: 300px;
    height: 300px;
    border-radius: 30% 70% 20% 40%;
}

上面示例中border-radius: 30% 70% 20% 40%;对应的计算结果是:

左上角(top-left)⇒ border-top-left-radius: 30% ⇒ x = y = 300px x 30% = 90px
右上角(top-right) ⇒ border-top-right-radius: 70% ⇒ x = y = 300px x 70% = 210px
右下角(bottom-right)⇒ border-bottom-right-radius: 20% ⇒ x = y = 300px x 20% = 60px
左下角(bottom-left)⇒ border-bottom-left-radius: 40% ⇒ x = y = 300px x 40% = 120px

 效果看上去像下图这样:

上面示例是元素widthheight相等,如果元素widthheight不相等时:

.element {
    width: 600px;
    height: 300px;
    border-radius: 30% 70% 20% 40%;
}

这个时候,border-radius: 30% 70% 20% 40%;对应的计算结果是:

左上角(top-left)⇒ border-top-left-radius: 30% ⇒ x = 600px x 30% = 180px; y = 300px x 30% = 90px
右上角(top-right)⇒ border-top-right-radius: 70% ⇒ x = 600px x 70% = 420px; y = 300px x 70% = 210px
右下角(bottom-right)⇒ border-bottom-right: 20% ⇒ x = 600px x 20% = 120px; y = 300px x 20% = 60px
左下角(bottom-left)⇒ border-bottom-left: 40% ⇒ x = 600px x 40% = 240px; y = 300px x 40% = 120px

效果看上去像下图这样:

如果元素的widthheight相等,但border-radius属性的值是一个带/符号的八个值:

.element { width: 300px; height: 300px; border-radius: 70% 30% 30% 70% / 60% 40% 60% 40%; }

对应的计算如下:

左上角(top-left)⇒ border-top-left-radius: 70% 60% ⇒ x = 300px x 70% = 210px; y = 300px x 60% = 180px
右上角(top-right)⇒ border-top-right-radius: 30% 40% ⇒ x = 300px x 30% = 90px; y = 300px x 40% = 120px
右下角(bottom-right)⇒ border-bottom-right-radius: 30% 60% ⇒ x = 300px x 30% = 90px; y = 300px x 60% = 180px
左下角(bottom-left) ⇒ border-bottom-left-radius: 70% 40% ⇒ x = 300px x 70% = 210px; y = 300px x 40% = 120px

对应的效果如下:

同样的,如果元素的widthheight值不同时,计算方式相似:

.element {
    width: 600px;
    height: 300px;
    border-radius: 70% 30% 30% 70% / 60% 40% 60% 40%;
}

对应的计算如下:

左上角(top-left)⇒ border-top-left-radius: 70% 60% ⇒ x = 600px x 70% = 420px; y = 300px x 60% = 180px
右上角(top-right)⇒ border-top-right-radius: 30% 40% ⇒ x = 600px x 30% = 180px; y = 300px x 40% = 120px
右下角(bottom-right)⇒ border-bottom-right-radius: 30% 60% ⇒ x = 600px x 30% = 180px; y = 300px x 60% = 180px
左下角(bottom-left) ⇒ border-bottom-left-radius: 70% 40% ⇒ x = 600px x 70% = 420px; y = 300px x 40% = 120px

对应的效果如下图所示:

如果你感兴趣的话,可以尝试着拖动下面示例中每个点的滑块,查看元素圆角的变化:

特别声明,上面的Demo由@supremebeing09提供,源于《CSS Border-Radius Can Do That?》一文

border-image中的%

border-image也是一个简写属性,它的子属性有 border-image-sourceborder-image-sliceborder-image-widthborder-image-outsetborder-image-repeat。其中border-image-sliceborder-image-width取值也可以是%

border-image-slice: [<number> | <percentage>]{1,4} && fill?
border-image-width: [ <length-percentage> | <number> | auto ]{1,4}

其中,border-image-slice取值为%时,其计算是相对于border-image-source引入的图像源尺寸进行计算,其中x方向相对于图像的width计算,y方向相对于图像的height计算。比如引入的图像尺寸是240px x 240px

border-image-slice: 20% 30% 40% 50%;

其中20%是距离图像顶部边缘的距离(y轴),30%是距离图像右侧边缘的距离(x轴),40%是距离图像底部边缘的距离(y轴),50%是距离图像左侧边缘的距离(x轴),其计算出来的值:

距离顶部边缘 ⇒ 20% ⇒ 240px x 20% = 48px
距离右侧边缘 ⇒ 30% ⇒ 240px x 30% = 72px
距离底部边缘 ⇒ 40% ⇒ 240px x 40% = 96px
距离左侧边缘 ⇒ 50% ⇒ 240px x 50% = 120px

如下图所示:

border-image-widthborder-image-slice类似,也是相对于图像的widthheight来计算。border-image-width的四个值指定用于将边框图像区域划分为九个部分的偏移量。它们分别表示从该区域的顶部右侧底部左侧向内的距离。

具体的效果可以查看MDN提供的Border-image generator

background-position中的%

CSS中的background-position除了使用关键字来给图片指定位置之外,还可以使用具体的长度值,即,可以是%值。不过它的计算相对而言要更为复杂一些。

background-position可以分为background-position-xbackground-position-y,换句话说,background-position接受多个值,但我们习惯于两个值的用法,即background-position: 50% 20%,如果background-position只显式的设置了一个值,那么表示backgrounnd-position-xbackground-position-y的值相同。

刚才提到过了,background-position%值计算要比前面介绍过的属性更为复杂。那是因为它是相对于元素尺寸与背景图片尺寸差计算,也就是说:

  • 水平方向(x轴),即background-position-x%值是相对于元素的width与背景图片的width 计算
  • 垂直方向(y轴),即background-position-y%值是相对于元素的height与背景图片的height计算

特别声明,背景图片的大小将会受background-size值的影响,上面提到的图片的尺寸是指backgroud-size的值为auto的情景下的尺寸,即背景图片的初始尺寸(原始尺寸)

比如下面这个示例:

<!-- HTML -->
<div class="element">
</div>

.element {
    width: 500px;
    height: 300px;
    background-position: 70% 30%
}

比如我们在.element中使用像下面这样的一张背景图(图片原始尺寸:280px x 180px):

按照上面所说的:

background-position-x: 70% ⇒ (500px - 280px) x 70% = 154px
background-position-y: 30% ⇒ (300px - 180px) x 30% = 36px

效果如下图所示:

你也可以尝试着在下面的示例中拖动滑块,但看具体的效果:

background-size中的%

background-size属性可以用来指定背景图像的尺寸大小,同样background-size属性可以取%的值。当background-size%的值时,它的计算是相对于元素自身的尺寸进行计算:background-size的第一个值是x轴方向,其计算相对于元素的width计算;background-size的第二个值是y轴方向,其计算相对于元素的height计算。比如下面这个示例:

<!-- HTML -->
<div class="container">
</div>

.container {
    width: 500px;
    height: 300px;
    background-size: 50% 50%; // ⇒ 250px 150px
}

有的时候background-size只会显式的设置一个值,比如background-size: 50%,这个时候它相当于background-size: 50% auto。这个时候,background-size值的计算相对同时显式设置两个%值的计算要复杂的多。我们来看一个示例,有一背景图像,其尺寸是280px x 180px:

但元素的尺寸是150px x 150px,并且该元素显式设置了background-size: 100%(即100% auto):

.container {
    width: 150px;
    height: 150px;
    background-size: 100%
}

这个时候你看到的效果如下:

这个时候background-sizex轴是100%,即是元素的width,也就是150px。简单地说,背景图像从280px缩小到150px,它们的缩放的比例是150 / 280 = 0.5357(即53.57%)。因此,为了保持宽高比,背景图像的高度也被缩小到53.57%,即180px x 53.57% = 96.428px(约97px)。

针对上面的示例,还可以像下面这样计算。背景图像的尺寸是280px x 180px,它的宽高比180 / 280 = 0.642857,当我们显式设置background-size: 100%时,此时图像的宽度就是150px,对应的高度150px x 0.642857 = 96.428px

相比而言,第二种方式更易于理解,因为background-size的第二个值取值为auto时,就是为了满足背景图像宽高比,背景图像的高度会根据第一个值做相应的处理。同样拿上面的示例作为演示:

background-size: 100% ⇒ background-size: 100% auto;
⇒ x = 100% x 150px = 150px
⇒ r = 180 / 280 = 0.642857 ⇒ 背景图片宽高比
⇒ y = 0.642857 x 150px = 96.42857px

background-size: 50% ⇒ background-size: 50% auto
⇒ x = 50% x 150px = 75px
⇒ r = 180 / 280 = 0.642857 ⇒ 背景图片宽高比
⇒ y = 0.642857 x 75px = 48.21429px

background-size: 150% ⇒ background-size: 150% auto
⇒ x = 150% x 150px = 225px
⇒ r = 180 / 280 = 0.642857 ⇒ 背景图片宽高比
⇒ y = 0.642857 x 225px = 144.64286px

具体效果如下:

渐变中的%

CSS图像模块 Level 3CSS图像值和替换内容模块Level 4包含了CSS渐变相关的属性,比如linear-gradient()radial-gradient()conic-gradient()repeating-linear-gradient()repeating-radial-gradient()repeating-conic-gradient()。在这些属性的运用中也能看到%的身影,其中设置颜色位置可以用%,而且在径向渐变和锥形渐变的圆心位置也可以使用%

其中颜色位置的%值是相对于渐变线做计算,如下图所示:

而径向渐变和锥形渐变的圆心位置的%分为x轴和y,其中x轴是相对于元素的width计算,y轴是相对于元素的height计算,如下图所示:

@Patrick Brosset在CodePen写了一个线性渐变的Demo,查看相应的效果:

object-position中的%

CSS有两个运用于可替换元素(比如<img><iframe><object><video>等)的属性object-fitobject-position,其中object-fit的表现行为和background-size非常相似,而object-positionbackground-position非常相似。前面和大家一起聊了聊background-positionbackground-size: 100% 100%情况之下background-position%的计算方式。这里我们简单地聊一下object-position%值的计算方式。

就取%值而言,其中object-position要考虑的场景较多,换句话说,object-fit取不同的值其计算方式不同,而且可替换元素的宽高比对其计算也有直接的影响。这里我们看一个简单的场景,即 **object-fit取值为none**情景。在这种情景之下,%值的计算:

x ⇒ (容器宽度 - 图片宽度) x 百分比值
y ⇒ (容器高度 - 图片高度) x 百分比值

刚才也提到过了,如果object-fit的值不同时,object-position%值计算是有所不同,比如下面这个Demo:

特别声明,在后续将会和大家深入探讨object-positionbackground-position在不同场景下取%值计算方式!

clip-path中的%

在CSS中可以使用clip-path来绘制图形,比如:

clip-path: inset( <shape-arg>{1,4} [round <border-radius>]? )
clip-path: circle( [<shape-radius>]? [at <position>]? )
clip-path: ellipse( [<shape-radius>{2}]? [at <position>]? )
clip-path: polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )

其中<shape-arg><border-radius><position><shape-radius>取值都可以是%值。为了更易于向大家阐述相应属性值取%值计算方式,将会借助@Bennett Feely的 **CSS clip-path maker**在线工具为例。

先来看inset()中的<shape-arg><border-radius>

.element {
    width: 800px;
    height: 400px;
    clip-path: inset(12% 12% 12% 12% round 5% 6% 7% 8% / 8% 7% 6% 5%);
}

其中<shape-arg>的使用和margin属性类似,<border-radius>的使用border-radius类似。在<shape-arg>中的topbottom相对于元素的height计算;rightleft相对于元素的width计算。<border-radius>中的%值计算方式和前面介绍的border-radius相同。

我们再来看circle()中的<shape-radius><position>

.element {
    width: 800px;
    height: 400px;
    clip-path: circle(22.5% at 50% 36%);
}

其中<shape-radius>%计算是相对于半径r值计算,circle()r的计算是相对于参考框(Reference Box)widthheight来计算,具体的计算公式如下:

就上例的话,计算出来的结果即是:

r = Math.sqrt(Math.pow(800, 2) + Math.pow(400, 2)) / Math.sqrt(2) = 632.4555320336758px

那么r x 22.5%计算出来的值大约为142.3024947px

对于circle()中的<position>%值是相对于元素的widthheight值计算,其中第一个值是x轴,其相对于width计算,第二个值是y轴的值,相对于元素的height计算:

ellipse()中的<shape-radius>相比于circle()中的要简单地多,ellipse()中的半径分rxry,其中rx%是相对于元素的width计算;ry%是相对于元素的height计算。对于<position>而言,和circle()中的<position>相同。

polygon()用来绘制多边形(至少需要三个顶点才能构建一个多边形),在polygon()会有多个<shape-arg>,而每个<shape-arg>都是一个顶点在参考框中的x轴和y轴的坐标点。如果取%值时,x轴相对于元素的width计算,y相对于元素的height计算:

Masking中的%

CSS Maskingmask-positionmask-size属性取值的时候也可以是%的值。如果你理解了前面介绍的background-positionbackground-size属性中的%值计算方式的话,那也能理解mask-positionmask-size属性中的%值计算方式。其中mask-positionbackground-position相似,mask-sizebackground-size相似。

路径动画中的%

CSS路径动画offset属性中的子属性offset-positionoffset-distanceoffset-anchor在取值也可以使用%值。

offset-distance取值为%值时,它的计算是相对于偏移路径的长度(也就是偏移路径的初始位置和结束位置之间的距离)。

offset-position的计算方式可以参考background-position的方式。

offset-anchor%值时,它的计算是根据运动物体(元素)的widthheight来计算,其中x相对于width计算,y相对于height计算。比如我们所说的(50%, 50%)指的就是我们常说的center(或者center center)。

Transform中的%

transform中的translateX()translateY()%值时,它的计算是相对于元素自身的widthheight。其中translateX()相对于元素自身的width计算,translateY()相对于元素自身的height计算。