图解CSS: CSS 背景(Part2)

发布于 大漠

第一部分主要和大家一起探讨了 CSS background属性中的background-imagebackground-repeatbackground-attachment 等属性。今天接着和大家一起来探讨其另外三个子属性,即 background-positionbackground-clipbackground-origin。感兴趣的同学请继续往下阅读!

图像位置

你可能已经注意到了,当背景图像在背景层的平铺方式为 background-repeat:no-repeat时,背景图像位于元素背景层的左上角,也就是 Web 坐标系统的 0 0位置(边框盒子<border-box>内侧边缘,内距盒子<padding-box>外侧边缘,它是由 background-origin属性来定义的):

这是背景图像的初始位置,即 background-position 的初始值(left top,计算值是 0% 0%)。换句话说,可以使用 background-position 属性来调整背景图像在元素背景层中的初始位置

background-position: <bg-position>#

<bg-position> = [ left | center | right | top | bottom | <length-percentage> ]
                | [ left | center | right | <length-percentage> ] [ top | center | bottom | <length-percentage> ]
                | [ center | [ left | right ] <length-percentage>? ] && [ center | [ top | bottom ] <length-percentage>? ]

从语法规则上不难发现,background-position 属性的值可以是 toprightbottomleftcenter 等关键词,比如:

.element {
    background-position: left top; /* 等同于 top left */
}

background-position 属性值为关键词时,它的顺序并无关紧要,比如:

.element {
    background-position: left top;

    /* 等同于 */
    background-position: top left;
}

不过,使用关键词时,不能是同时使用同一轴的关键词,比如下面这样的声明是无效的:

/* 无效的 CSS 声明 */
.element {
    background-position: left right;
    background-position: right left;
    background-position: top bottom;
    background-position: bottom top;
}

background-position 属性值除了可取关键词之外,还可以是 CSS 的 <length-percentage>(长度百分比)值,比如《图解CSS:CSS 的值和单位》中介绍的 pxemremvw%等:

.element {
    background-position: 20px 20px;

    background-position: 2rem 10em;

    background-position: 20% 30%;

    background-position: 20px 50%;
}

如果background-position的值为<length-percentage>时,第一个值对应的x轴的偏移量,第二个值是对应的y轴的偏移量:

需要注意的是,background-position 取值为 % 时,它的计算相对来说要复杂一些,稍后我们会单独来阐述 background-position% 值的计算。

另外,background-position 属性还可以是关键词和<length-percentage>混合在一起使用,比如:

.element {
    background-position: 50px top;
}

只不过,关键词和<length-percentage>混合在一起使用时,顺序非常重要,第一个值表示水平轴,第二个值表示垂直轴。因此,在混合使用的时候,需要注意它们的顺序,比如:

.element {
    background-position: left 50%; /* 有效的声明 */

    background-position: 50% left; /* 无效的声明 */
}

从上面的示例中我们可以获知,<position> 定义了一组 (x, y) 坐标(我们所熟悉的 Web 坐标),默认是相对于<border-box>内边缘来计算位移量(即边框内缘边为起始位置),但可以使用 background-origin 来改变。而且 <position> 除了上面示例中展示的取两个值之外,还有可以是取一个值、三个值,甚至是四个值,而且不同个数的值,所代表的含义以及所起的使用是不同的。接下来,我们分别来看看 <position> 取不同数量的值,应该如何使用。

一个值

background-position 属性只显式设置了一个值时,它可能会有以下几种情景。

如果显式设置的值是 leftright,则表示x轴值,相应的y轴值默认为 center

.element {
    background-position: left; /* 等同 left center 或 left 50% */

    background-position: right; /* 等同 right center 或 right 50% */
}

如果显式设置的值是 topbottom,则表示y轴的值,相应的x轴的值默认为 center

.element {
    background-position: top; /* 等同 center top 或 50% top */

    background-position: bottom; /* 等同 center bottom 或 50% bottom */
}

如果显式设置的值是 center,则表示 x轴和y轴的值都是 center,相当于 center center50% 50%,这个时候,背景图片的中心位置和元素背景层的中心位置会完全重叠,即背景图片在背景层中水平垂直居中。

.element {
    background-position: center; /* 等同 center center 或 50% 50% */
}

如果显式设置的值是 <length-percentage><length><percentage>),则表示x轴的值,相应的y轴的值默认为 center50%

.element {
    background-position: 50px; /* 等同于 50px center 或 50px 50% */

    background-position: 2vw; /* 等同于 2vw center 或 2vw 50% */

    background-position: 4%; /* 等同于 5% center 或 4% 50% */
}

两个值

如果 background-position属性取两个值,则第一个是水平方向(x轴)的偏移量,第二个值是垂直方向(y轴)的偏移量。它可能会有以下几种情景。

每个值可以是toprightbottomleftcenter中的一个。如果给出leftright,那么它们定义的是 x 轴的位置,另一个定义的是y轴的位置;如果给出 topbottom,那么它们定义的是 y 轴的位置,另一个定义的是 x 轴的位置:

.element {
    background-position: left top;      /* x » left, y » top */

    background-position: top left;      /* x » left, y » top */

    background-position: left bottom;   /* x » left, y » bottom  */

    background-position: bottom left;   /* x » left, y » bottom  */

    background-position: right top;     /* x » right, y » top */

    background-position: top right;     /* x » right, y » top */

    background-position: right bottom;  /* x » right, y » bottom */

    background-position: bottom right;  /* x » right, y » bottom */

    background-position: left center;   /* x » left, y » center */

    background-position: center left;   /* x » left, y » center */

    background-position: top center;    /* x » top, y » center */

    background-position: center top;    /* x » center, y » top */

    background-position: right center;  /* x » right, y » center */

    background-position: center right;  /* x » right, y » center */

    background-position: bottom center; /* x » bottom, y » center */

    background-position: center bottom; /* x » center, y » bottom */

    background-position: center center; /* x » center, y » center */
}

正如上面代码所示,background-position属性值是两个关键词时,关键词出现的顺序无关紧要。比如 left toptop left 渲染出来的效果相同。需要注意的是,如果两个值都是同一个方向的关键词,则background-position会被视为无效:

/* 无效声明 */
.element {
    background-position: left right;
    background-position: right left;

    background-position: top bottom;
    background-position: bottom top;
}

background-position显式设置的值是 <length-percentage><length><percentage>)且另一个值是 leftright 时,则该值定义的是 y 轴的位置(x轴位置是由关键词 leftright决定)。比如:

.element {
    background-position: left 50px;  /* x » left = 0%, y » 50px */
    background-position: left 25%;   /* x » left = 0%, y » 25% */

    background-position: right 50px; /* x » right = 100%, y » 50px */
    background-position: right 25%;  /* x » right = 100%, y » 25% */
}

background-position显式设置的值是 <length-percentage>,且另一个值是 topbottom 时,则该值定义的是 x 轴的位置(y轴位置由关键词topbottom决定)。比如:

.element {
    background-position: 50px top;      /* x » 50px, y » top = 0% */
    background-position: 25% top;       /* x » 25%, y » top = 0% */

    background-position: 50px bottom;   /* x » 50px, y » bottom = 100% */
    background-position: 25% bottom;    /* x » 25%, y » bottom = 100% */
}

需要注意的是,当background-position的值是 <length-percentage> 和关键词 toprightbottomleft 组合在一起时(<length-percentage>与关键词配对时),两者的顺序很重要,定义x轴的值要放前面,定义y轴的值要放后面。即:

.element {
    background-position: left <length-percentage>;
    background-position: right <length-percentage>;

    background-position: <length-percentage> top;
    background-position: <length-percentage> bottom;
}

要是不按上面规则给background-position设置值,那么浏览器将会视其无效,比如,下面这样的声明就是无效规则:

.element {
    background-position: top <length-percentage>;
    background-position: bottom <length-percentage>;

    background-position: <length-percentage> left;
    background-position: <length-percentage> right;
}

left 20px20px left是不相同的,其中 background-position: 20px left声明无效(无效时,background-position将会取其初始值left top,即 0% 0%)。

不过,<length-percentage> 和关键词 center 组合在一起使用时,该值要是在 center 之后,则该值定义的是 y轴的位置(x轴是关键词center,相当于 50%),反之该值定义的是x轴的位置(y轴是关键词center,相当于 50%):

.element {
    background-position: center 50px;   /* x » center = 50%, y » 50px */
    background-position: center 25%;    /* x » center = 50%, y » 25% */

    background-position: 50px center;   /* x » 50px, y » center = 50% */
    background-position: 25% center;    /* x » 25%, y » center = 50% */
}

三个值

对于大多数开发者而言,在使用 background-position 属性来给背景图片定位时,常使用的方式就是一个值或两个值。对于使用三个值(或后面要介绍的四个值)还是很新鲜的。显式给background-position使用三个值与前面介绍的还是有很大的区别。

首先要说的是,background-position 同时使用三个关键词时,也会被浏览器视为无效的CSS声明,比如:

/* 无效样式规则 */
.element {
    background-position: left top center;
}

background-position 使用三个值时,往往是将<length-percentage>和两个关键词组合在一起使用:

.element {
    background-position: <keyword> <length-percentage> <keyword>
    background-position: <keyword> <keyword> <length-percentage>
}

<keyword> = top | right | bottom | left

即:

.element {
    background-position: left 30% top;
    background-position: right 30% bottom;

    background-position: left top 30%;
    background-position: right bottom 30%;
}

也就是说,当<length-percentage>background-position的第二个值时,它表示第一个值的偏移量,也就是水平方向(x轴)的偏移:

.element {
    background-position: left 30% top; 
}

示例中的 30% 表示背景图片最左侧边缘距离背景层最左侧边缘距离是30%

.element {
    background-position: right 300px bottom;
}

示例中的300px表示背景图片最右边距离背景层最右侧边缘距离是 300px

<length-percentage>background-position的第三个值时,它表示第个值的偏移量,也就是垂直方向(y轴)的偏移:

.element {
    background-position: left top 30%;
}

示例中的 30% 表示背景图片顶部边缘距离背景层顶部边缘的距离是 30%

.element {
    background-position: right bottom 300px;
}

示例中的 300px表示背景图片最底部边缘距离背景层底部边缘的距离是 300px

background-position属性取两个关键词和一个<length-percentage>组合时,<length-percentage> 不要置于关键词前面,否则会被视为无效值:

.element {
    background-position: <length-percentage> <keyword> <keyword>
}

<keyword> = top | right | bottom | left

例如,浏览器将会视为下面这个样式规则为无效的:

.element {
    background-position: 40em left top;
}

上面所示的示例用到的关键词都是围绕着toprightbottomleft展开的。除了这四个关键词之外,还有center关键词。background-position 取三个值时,其中使用到关键词 center 时,后面不能紧跟 <length-percentage>,不然该规则也会被视为无效:

/* 无效样式规则 */
.element {
    background-position: center <length-percentage> <top | center | bottom>;
    background-position: <left | center | right> center <length-percentage>;
}

但像下面这样使用是有效的:

.element {
    background-position: <top | right | bottom | left>  <length-percentage> center;
    background-position: center <top | right | bottom | left> <length-percentage>
}

比如:

.element {
    background-position: center right 30%;  /* x » right 30%, y » center */

    background-position: center left 30%;   /* x » left 30%, y » center */

    background-position: center bottom 30%; /* » center, y » bottom 30% */

    background-position: center top 30%;    /* x » center, y » top 30% */

    background-position: bottom 30% center; /* x » center, y » bottom 30% */

    background-position: top 30% center;    /* x » center, y »  top 30% */

    background-position: right 30% center;  /* x » right 30%, y » center */

    background-position: left 30% center;   /* x » left 30%, y » center */
}

简单地说,background-position属性取三个值时,在关键词center 之后不能紧跟<length-percentage>值,否则会被视为无效声明

前面说了,background-position属性使用三个值时,如果三个都是关键词是一个无效声明,除此之外,还有就是一个关键词和两个<length-percentage>值组合也会被视为无效声明:

.element {
    background-position: <length-percentage> <keyword> <length-percentage>;
    background-position: <keyword> <length-percentage> <length-percentage>;
    background-position: <length-percentage> <length-percentage> <keyword>;
}

这样一来,background-position 取三个值时,像下面这样使用的话,都会被浏览器视为无效样式规则:

.element {
    /* 三个值都是关键词组合 */
    background-position: <keyword> <keyword> <keyword>;

    /* <length-percentage> 不能置于两个关键词的前面 */
    background-position: <length-percentage> <keyword> <keyword>;

    /* center关键词后面不能紧跟<length-percentage>值 */
    background-position: center <length-percentage> <keyword>;
    background-position: <keyword> center <length-percentage>;

    /* 一个关键词和两个<length-percentage>值组合 */
    background-position: <length-percentage> <keyword> <length-percentage>;
    background-position: <keyword> <length-percentage> <length-percentage>;
    background-position: <length-percentage> <length-percentage> <keyword>;
}

四个值

background-position 还可以显式的设置四个值,在设置四个值的时候,一般是两个关键词与两个<length-percentage>值组合在一起使用。比如:

.element {
    background-position: <keyword> <length-percentage> <keyword> <length-percentage>
}

<keyword> = top | right | bottom | left

例如:

.element {
    background-position: top 30px right 50px;   /* x » right 50px, y » top 30px */

    background-position: top 30% left 50%;      /* » left 50%, y » top 30% */

    background-position: bottom 30px right 40vh;/* x » right 40vh, y » bottom 30px */

    background-position: bottom 30vh left 40%;  /* x » left 40%, y » bottom 30vh */

    background-position: left 40px top 50%;     /* x » left 40px, y » top 50% */

    background-position: left 40vh bottom 50px; /* x » left 40vh, y » bottom 50px */

    background-position: right 50px top 30%;    /* x » right 50px, y » top 30% */

    background-position: right 40% bottom 20px; /* x » right 40%, y » bottom 20px */
}

第一个值和第三个值是关键词 toprightbottomleft 之一,第二个和第四个值是 <length-percentage>,其中第二个值<length-percentage> 是用来设置第一个值的偏移量,第四个值是用来设置第三个值的偏移量。在使用关键词时,不能同时出现同一方向的值,比如 leftright 或者 topbottom 同时出现,否则样式规则会被视为无效规则:

.element {
    background-position: top 50px bottom 30px;
    background-position: left 50% right 10px;
}

另外,background-position 取四个值时,关键词不能是 center ,否则也会被视为无效样式规则:

.element {
    background-position: center 50px top 50px;
    background-position: center 50% bottom 30px;
    background-position: left 30px center 40px;
    background-position: right 30px center 40%;
}

background-position 取四个值时,<length-percentage> 一定是跟随在 <keyword> 关键词之后,如果<length-percentage> 放置在 <keyword> 之前,那么样式规则同样会被视为无效规则:

.element {
    background-position: <length-percentage> <keyword> <length-percentage> <keyword>
}

<keyword> = top | right | bottom | left 

比如:

.element {
    background-position: 20px left 40% top;
}

正如上面示例所示,取四个值时,它的表现将会类似下图这样:

background-position为什么需要新特性

这里所说的 background-position 新特性指的是 该属性可以显式的设置三个或四个值。那么为什么需要给 background-position 提供三个值或四个值,为什么说,什么时候使用三个值或四个值?接下来就以四个值为例来解释为什么?

CSS 的 background-position 属性最初只提共了一个值或两个值的语法规则,不管是一个值还是两个值,它们的作用都是用来设置背景图片在背景层中的位置。而且都是以左上角为相对计算的起始点。也就是说,元素背景层有一个左顶点(0 0位置),背景图片也有一个原始点,默认也是 0 0位置,两个顶部之间产生的间距就是背景图片在元素背景层中的位置(xy 轴的偏移值)。比如:

.element {
    background-position: 100px 200px;
}

示例代码告诉我们,背景图片原点距离元素容器层原点,水平方向(x轴)是 100px,垂直方向(y轴)是 200px,如下图所示:

相当于背景图片从最左侧向右移了 100px,同时从最顶部向下移了 200px

这对于距离左侧和距离顶部定位背景图片是很方便的。不过,在实际开发中,我们很多时候希望除了距离左顶点来定位背景图片之外,还希望能根据其他几个顶点来定位背景图片,比如右顶点、右下角和左下角等。比如下面这样的一个场景,我们希望背景图片最右侧边缘和最底部边缘距元素背景层右侧边缘和底部边缘分别是 100px200px

以往要实现这样的背景定位是有点让人头痛的。最早期 CSS 的 calc() 还未得到浏览器支持的时候,开发者需要知道元素大小,背景图片大小,然后根据这些尺寸去计算出背景图片距离左侧边缘和距离顶部边缘的大小。有了 calc() 之后,事情稍微变得简单些。

background-position 的基础知识中我们可以知道,背景图片在定位的时候,可以使用 100% 100%right bottom 将背景图片位于背景层的右下角,这个时候,使用 calc() 函数,从中减去需要偏移的量,即:

.element {
    background-position: calc(100% - 100px) calc(100% - 200px);
}

使用 background-position 新特性,可以更简单的实现上面的效果,我们可以使用 right 100pxbottom 200px 来告诉浏览器,背景图片需要从背景层最右侧边缘向左偏移 100px,最底部边缘向上偏移 200px

.element {
    background-position: right 100px bottom 200px
}

相当于移动了定位的坐标,从开始的左下角变成了右下角:

这也意味着,background-position 新特性,开发者可以根据需要,指定背景图片另一侧边缘与背景层相应边缘之间的偏移量,比如:

  • top <length-percentage>,背景图片顶部边缘与背景层顶部边缘之间的偏移量是 <length-percentage>
  • right <length-percentage>,背景图片右侧边缘与背景层右侧边缘之间的偏移量是 <length-percentage>
  • bottom <length-percentage>,背景图片底部边缘与背景层底部边缘之间的偏移量是 <length-percentage>
  • left <length-percentage>,背景图片左侧边缘与背景层左侧边缘之间的偏移量是 <length-percentage>

换句话说,在新特性中,运用于 background-position 属性中的关键词(toprightbottomleft)不再仅仅是用来计算背景图片的定位位置信息(坐标点),而且还可以用来指定背景图片的偏移原点,比如上面示例中 right 100px bottom 200px,就把偏移原点变成了右下角。那么,这些关键词具体的含义和所起的作用应该根据其使用的场景来决定。

  • top:如果给出一个值或两个值,则用于计算背景图片的位置(垂直距离),计算值为 0%,比如 background-position: topbackground-position: left top;否则将顶部边缘指定为下一个偏移的原点,比如 background-position: left top 20%background-position: left 20px top 200px
  • right:如果给出一个或两个值,则用于计算背景图片的位置(水平距离),计算值为 100%,比如 background-position: rightbackground-position: right top;否则将右边缘指定为下一个偏移的原点,比如 background-position: right 100px topbackground-position: right 100px bottom 200px
  • bottom:如果给出一个或两个值,则用于计算背景图片的位置(垂直距离),计算值为 100%,比如 background-position: bottombackground-position: right bottom;否则将底部边缘指定为下一个偏移的原点,比如 background-position: left bottom 100pxbackground-position: left 10% bottom 30vh
  • left:如果给出一个或两个值,则用于计算背景图片的位置(水平距离),计算值为 0%,比如 background-position: leftbackground-position: left bottom;否则将左边缘指定为下一个偏移的原点,比如 background-position: left 20px bottombackground-position: left 20vw top 30%

简单地说,关键词toprightbottomleft用于background-position时,如果background-position只有一个值或两个值,那么它们则用于计算图片的位置;反之,background-position 有两个以上(三个或四个)值时,它们则用于指定偏移原点,不为被计算为0%100%

注意,center 关键词与它们不一样,它只能被用于计算位置,计算值为 50%,而且当background-position的值为 centercenter时,表示背景图片在背景层中是垂直居中的,此时,背景图片的中心原点和背景层的中心原点是重叠的。

子属性

background-position 属性与 background-repeat 属性相似,该 background-position 属性允许您沿 xy 轴独立定位图像。你也可以单独使用background-position的子属性background-position-xbackground-position-y来指定单个轴的定位。比如:

.element {
    background-position-x: left;
    background-position-y: top;

    /* 等同于 */
    background-position: left top;
}

单独使用 background-position-xbackground-position-y 子属性时不能将关键词(toprightbottomleftcenter) 与 <length-percentage> 组合使用,否则会被浏览器视为无效样式规则,比如:

/* 无效样式规则 */
.element {
    background-position-x: left 30px;
    background-position-x: right 30px;
    background-position-y: top 30px;
    background-position-y: bottom 30px;
}

但使用简写属性时,浏览器计算值是有效的:

.element {
    background-position: left 30px top;
        » background-position-x: left 30px;
        » background-position-y: top;

    background-position: right 30px bottom;
        » background-position-x: right 30px;
        » background-position-y: bottom;

    background-position: left top 30px;
        » background-position-x: left;
        » background-position-y: top 30px;

    background-position: right bottom 30px;
        » background-position-x: right;
        » background-position-y: bottom 30px;
}

background-position在取不同个数值时,浏览器计算出来的background-position-xbackground-position-y 类似下面这个示例:

.element {
    background-position: left;
        » background-position-x: left;
        » background-position-y: center;

    background-position: left 30px;
        » background-position-x: left;
        » background-position-y: 30px;

    background-position: left 30% top;
        » background-position-x: left 30%;
        » background-position-y: top;

    background-position: left top 30%;
        » background-position-x: left;
        » background-position-y: top 30%;

    background-position: left 20px top 30vh;
        » background-position-x: left 20px;
        » background-position-y: top 30vh;
}

另外,给子属性background-position-xbackground-position-y 显式指定关键词时,在background-position-x 不能显式指定垂直方向的关键词topbottom,同样的在background-position-y上不能显式指定 leftright,否则也会被视为无效的 CSS 规则:

/* 可以这样指定 */
.element {
    background-position-x: left;
    background-position-x: right;
    background-position-x: center;
    background-position-y: top;
    background-position-y: bottom;
    background-position-y: center;
}

/* 不可以这样指定 */
.element {
    background-position-x: top;
    background-position-x: bottom;
    background-position-y: left;
    background-position-y: right;
}

前面在介绍background-position属性时有说过,关键词的顺序无关紧要。因为现代浏览器是聪明的,运用于 background-positionleftright 关键词始终会计算为 background-position-x的值,同样 topbottom 始终会计算为 background-position-y的值,比如:

.element {
    background-position: left top;
        » background-position-x: left;
        » background-position-y: top;
    
    background-position: top left;
        » background-position-x: left;
        » background-position-y: top;
}

而且,在background-position-xbackground-position-y子属性使用关键词时,它们始终只用于计算位置,无法像简写属性那样做为原点信息:

.element {
    background-position-x: left;
        » background-position-x: 0%;    /* left » 0% */
    
    background-position-x: right;
        » background-position-x: 100%;  /* right » 100% */

    background-position-y: top;
        » background-position-y: 0%;    /* top »  0% */
    
    background-position-y: bottom;
        background-position-y: 100%;    /* bottom » 100% */

    background-position-x: center;
        » background-position-x: 50%;   /* center » 50% */
    
    background-position-y: center;
        » background-position-y: 50%;   /* center » 50% */
}

这也意味着,如果你想将关键词toprightbottomleft 作为下一个偏移原点,则只能使用background-position简写属性,而且还需要是三个值或四个值的赋值。

在实际开发中,显式使用 background-position-xbackground-position-y 相对而言比较少,众多开发者还是更喜欢使用简写属性 background-position,尤其是有新特性(三个值或四个值)时,我更建议开发者少使用子属性。这样做除了让你的代码更简洁之外,还可以尽可能的避免语法规则的错误,造成样式规则失效。

背景定位中的百分比

background-position取值除了关键词之外还可以是 <length-percentage>,即长度值<length> 和 百分比值<percentage>,对于长度值<length>来说,就的计算是简单明了的(除非你使用的是相对单位或视窗单位,需要相对于参照物来计算,详细请阅读《图解CSS:CSS 的值和单位》一文),但在 background-position 属性中使用百分比值,它的计算是复杂的。

在 CSS 中,属性值是百分比值时,它原本就是复杂的一个计算体系,不同属性使用百分比时,计算的能照物都有所不同,有关于这方面的介绍请移步阅读《CSS中百分比单位计算方式》一文。

在开始介绍 background-position 取百分比值的计算原理之前,我们先来看一个有关于图片定位的示例。如果你使用定位的方式让一张图片在容器中处置居中,往往是像下面这样:

figure {
    width: 400px;
    aspect-ratio: 4 / 3;
    position: relative;
}

figure img {
    width: 100px;
    aspect-ratio: 1;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

这主要是因为,使用 left: 50% 时,图像 img 左侧边缘将位于容器 figure 中间点(水平方向的中间点),如果要将其水平居中,需要使用 translateX(-50%)或负的margin-left值(图片宽度的一半-50px)往左拉;垂直方向是同样的原理。

而背景图片在背景层中水平垂直居中,只需要将 background-position 设置为 center(相当于 50% 50%),浏览器就会计算出该图像的大小,并将其中心定位在元素的中心:

上面示例中,左上角和图片是background-position: 0 0定位的背景图,右下角的是绝对定位(left: 50%;right: 50%,还未居中)的img图,如果将background-position0 0 变成 50% 50%,同时定位的图片加上transform: translate(-50%, -50%),它们会完全重合在一起:

不难发现,background-position属性值为 0% 时背景图像的左(或上)边界与容器背景层的相应左(或上)边界对齐,或者说图像的 0% 标记将位于容器的 0% 标记上;background-position属性值为 100% 表示背景图像的右(或 下)边界与容器背景的右(或下)边界对齐,或者说图像的 100% 标记将位于容器的 100% 标记上。因此 50% 的值表示水平或垂直居中背景图像,因为图像的 50% 将位于容器的 50% 标记处。

这主要是因为,background-position取百分比值时,会同时考虑容器的尺寸和背景图片的尺寸,即相对于两者尺寸做计算。 W3C规范中是这样描述的

A percentage for the horizontal offset is relative to (width of background positioning area - width of background image). A percentage for the vertical offset is relative to (height of background positioning area - height of background image), where the size of the image is the size given by ‘background-size’.

也就是说,当背景图片尺寸(background-size)不做任何的重置(也就是100% 100%)时,水平百分比的值等于容器宽度百分比值减去背景图片宽度百分比值。垂直百分比的值等于容器高度百分比值减去背景图片高度百分比值。即 background-position取值为百分比值(%)时,它会先从相应的容器尺寸中减去背景图像尺寸,然后将结果值的百分比用作从左(或顶部)边界的直接偏移量。相应的计算公式如下:

(容器宽度 - 背景图像宽度) x (background-position-x设置的百分比值) = background-position-x 的计算值
(容器高度 - 背景图像高度) x (background-position-y设置的百分比值) = background-position-y 的计算值

也就是说:

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

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

比如下面这个示例:

.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 等于给定轴的容器大(100%),那么该轴的百分比值将不起作用,因为“容器图像差”将为零。将需要使用绝对值进行偏移。

负值

background-position 取值为 <length-percentage> 时,可以是负值。它的表现行为与正值是相反的。先来看 background-position 为正值(<length-percentage>)的示例:

.element {
    background-position: 0 0;
    background-position: 40px 40px;              /* 原点在左上角 */
    background-position: left 40px top 40px;     /* 原点在左上角 */
    background-position: right 40px top 40px;    /* 原点在右上角 */
    background-position: left 40px bottom 40px;  /* 原点在左下角 */
    background-position: right 40px bottom 40px; /* 原点在右下角 */
}

background-position 的值为正值,且:

  • 原点在左上角(默认原点,或显式设置left <length-percentage> top <length-percentage>),背景图片将沿着 x 轴向右偏移,沿着y轴向下偏移
  • 原点在右上角(显式设置 right <length-percentage> top <length-percentage>),背景图片将沿着 x 轴向左偏移,沿着 y 轴向下偏移
  • 原点在左下角(显式设置left <length-percentage> bottom <length-percentage>),背景图片将沿着 x 轴向右偏移,沿着 y 轴向上偏移
  • 原点在右下角(显式设置right <length-percentage> bottom <length-percentage>),背景图片将沿着 x 轴向左偏移,沿着y轴向上偏移

background-position 属性的值为负值(<length-percentage>)时,只会改变背景图像在xy偏移的方向。比如,将上面示例的正值更换成负值:

.element {
    background-position: 0 0;
    background-position: -40px -40px;              /* 原点在左上角 */
    background-position: left -40px top -40px;     /* 原点在左上角 */
    background-position: right -40px top -40px;    /* 原点在右上角 */
    background-position: left -40px bottom -40px;  /* 原点在左下角 */
    background-position: right -40px bottom -40px; /* 原点在右下角 */
}

从效果中不难发现,background-position取负值时,原点位置还是由关键词lefttoprightbottom 来决定的,并且偏移量是设置的 <length-percentage> 决定。也就是说,当background-position的值为负值,且:

  • 原点在左上角(默认原点,或显式设置 left <length-percentage> top <length-percentage>),背景图片将沿着x轴向左偏移,沿着y轴向上偏移
  • 原点在右上角(显式设置 right <length-percentage> top <length-percentage>),背景图片将沿着x轴向右偏移,沿着y轴向上偏移
  • 原点在左下角(显式设置left <length-percentage> bottom <length-percentage>),背景图片将沿着x轴向左偏移,沿着y轴向下偏移
  • 原点在右下角(显式设置right <length-percentage> bottom <length-percentage>),背景图片将沿着 x 轴向右偏移,沿着 y 轴向下偏移

如上图所示,当background-position的值为负值时,背景图片将会沿着相应的方向移出元素的背景层,默认之下,从边框盒子(<border-box>)内边缘往背景层外面偏移。这个时候在背景层中看到背景图片看上去是被裁剪了一样。如果<length-percentage>的值大于背景图片的尺寸,那么背景图片将会完全被移出元素的背景层:

.element {
    background-position: -100px -100px; /* 假设背景图片尺寸是 100px x 100px */
}

在不知道背景图片具体尺寸时,又想将背景图片完全移出元素背景层,那么最简单的方式就是给background-position设置一个非常大的负值,比如:

.element {
    background-position: -99999rem -99999rem; 
}

同样的,使用一个较大的正值,也可以将背景图片移出背景层:

.element {
    background-position: 999999rem 999999rem; 
}

介绍完 background-position 属性之后,那么background 最基础的几个子属性就介绍完了。接下来的几个属性对于 CSS background 来说,是在 CSS 2.0 版本之后新增的子属性,而且这些子属性的出现,可以帮助我们更好的控制背景图片。比如:

  • background-clip 控制背景图像绘制区域(背景图片的剪切)
  • background-origin 控制背景图像定位区域(背景坐标原点)
  • background-size 控制背景图像尺寸

我们先从 background-clip 开始!

背景图像的剪切

CSS 的 background-clip 属性是用来控制背景图像的绘制区域(或者说渲染区域)。该属性的值主要有:

background-clip: <box>#

<box> = border-box | padding-box | content-box

这几个值的具体含义是:

  • border-box:背景图像被绘制在边框盒子(<border-box>)内,即背景图像延伸至边框(border)外边缘(但是在边框下层)
  • padding-box:背景图像被绘制在内距盒子(<padding-box>)内,即背景图像延伸至内边距(padding)外边缘。不会绘制到边框处(边框下层不会有背景图像的延伸)
  • content-box:背景图像被绘制在内容盒子(<content-box>)内,即背景图像被裁剪至内容区外边缘。边框(border)和内距(padding)层不会有背景图像的延伸

注意,background-clip 属性除了这三个值之外,在部分浏览器有另外一个私有属性值,即 text

在详细阐述这几个值的使用之前,我们先来重温一下这几个<box>。这里所说的 <box> 分别指的是 <border-box><padding-box><content-box>,在 CSS 中任何一个元素都有这三个盒子,它们之间的关系是:

但这三个盒子,有时候是等同的。我们用一个简单的示例来向大家展示,什么时候这三个盒子是相同的:

.element {
    width: 600px;
    aspect-ratio: 16 / 9;
    border: 10px solid rgb(0 0 0 / .5);
    padding: 10px;
}

我们同时给元素 .element 显式设置了borderpadding,此时,它的盒模型大致如下图所示:

我们把:

  • 边框的外沿线称为边框限制线
  • 边框的内沿线和内距的外沿线称为内距限制线
  • 内距的内沿线和内容的外沿线称为内容限制线

也就是说,任何一个盒子(<box>)层都有自己的大小,有一个最大的限制线:

  • <border-box>的限制线是边框的外沿线
  • <padding-box>的限制线是内距的外沿线(或边框的内沿线)
  • <content-box>的限制线是内容的外沿线(或内距的内沿线)

当元素未显式设置padding时,元素的<padding-box><content-box>大小是等同的,此时,内容限制线就等同于内距的限制线

同样的,当元素未显式设置border时(或border-width:0),元素的<border-box><padding-box>大小是等同的,此时,内距限制线就等于边框限制线

要是,元素同时未显式设置paddingborder(或border-width:0),这个时候元素的三个盒子(<border-box><padding-box><content-box>)是完全等同的,最终就是<content-box>,此时,边框限制线和内距限制线就是内容限制线,三条限制线完全重叠:

注意,CSS 的 background-origin属性在指定background-positionbackground-size值为%计算时,也与这三个框有关系。background-positionbackground-size值为百分比时,计算都是相对于<padding-box>的。我们可以通过改变background-origin来改变。稍后在介绍background-origin属性,会详细介绍!

从前面的众多示例中我们不难发现,默认情况之下,背景图像会覆盖整个<border-box>(从视觉上看,他在边框盒子的下面),但背景图片的定位(background-position)却是相对于<padding-box>左上角(0 0位置,因为background-position的默认值是left top,也就是0% 0%):

注意,如果 border-color 不带透明度,那么位于边框下面的背景图像是看不到。

虽然说背景图像是位于边框盒子下面,但并不意味着所有边框下都会有背景图像的延伸。比如,在元素中运用了一张足以铺满(甚至大于)背景层的背景图像,且将background-repeat 设置为 no-repeatbackground-sizeauto 时,背景图像只会延伸到其中的两条边框下,具体与定位原点有关:

正如上图所示:

  • 定位原点在左上角(left 0 top 0)时,背景图片会延伸到右边框和下边框下,但不会延伸到左边框和上边框下
  • 定位原点在右上角(right 0 top 0)时,背景图片会延伸到左边框和下边框下,但不会延伸到右边框和上边框下
  • 定位原点在左下角(left 0 bottom 0)时,背景图片会延伸到右边框和上边框下,但不会延伸到左边框和下边框下
  • 定位原点在右下角(right 0 bottom 0)时,北影图片会延伸到左边框和上边框下,但不会延伸到右边框和下边框下

这主要是因为background-position默认状态下是相对于<padding-box>来做的计算。

上面看到的都是背景图像在客户端(浏览器)中默认的渲染行为,但在某些时候,我们却不需要这样的默认渲染效果。有的时候希望背景图片只在边框区域显式(比如制作一个图像边框,有点类似border-image的);有的时候却只希望背景图片在内距区域显式;还有的时候只希望背景图像在元素的内容区域显式。在还没有background-clip的时候,要实现相似的效果,开发者只能额外的添加 HTML 元素或者使用伪元素::before::after来模拟,但该属性的出现,就可以让开发者更灵活的控制背景图像如何显式,或者说如何裁剪。

换句话说,开发者可以显式指定background-clip的值(裁剪方式),让背景图像只在指定的盒子中显式

border-box

border-boxbackground-clip的默认值。它会将背景图片铺满整个边框盒子(<border-box>),溢出边框限制线的的图像会被裁剪掉,如果内距盒子(<padding-box>)和内容盒子(<content-box>)背景是透明的话,那么放置在<border-box>的背景图像在内距层和内容层都是可见的;反之,如果内距盒子和内容盒子不是透明的,比如也设置了背景图片(即使是纯色的背景图像),那么此时,背景图片只在边框区域可见(边框要带有透明度才可见):

padding-box

如果显式将background-clip设置为padding-box,它会将背景图片铺满整个内距盒子(<padding-box>),溢出内距限制线的图像会被裁剪掉,此时背景图像不会延伸到边框限制线,元素边框颜色是否为透明,边框区域下都看不到背景图片。同样的,如果内容盒子(<content-box>)不是透明的,那么背景图像只在内距区域可见:

content-box

如果将background-clip设置为content-box,它会将背景图片铺满整个内容盒子(<content-box>),溢出内容限制线的图像会被裁剪掉,此时在内距区域和边框区域都看不到背景图像的延伸:

有了background-clip之后,开发者就可以做很多有创意的效果了,比如说渐变边框的效果:

.gradient-border {
    border: 10px solid transparent;
    background-image: linear-gradient(#222, #222), var(--gradient);
    background-origin: border-box;
    background-clip: padding-box, border-box;
}

注意,示例中用到了 CSS 的多背景以及background-origin属性,有关于这方面的内容,稍后再阐述!

text

众所周之,在 CSS 中给文本设置颜色是通过 color 属性来控制的。不过自从有了 background-clip 属性之后,开发者多了一种途径给文本设置颜色。我们可以给 background-clip 属性设置一个 text 属性,让背景被裁剪成文字的前景色。只不过,到目前为止,background-clip 取值为 text 还只是部分浏览器的私有属性,在使用的时候需要添加 -webkit前缀。

也就是说,有了background-clip: text之后,可以将背景图填充到文本中,实现图像文本,渐变文本,也可以实现类似color设置的纯色文本。使用background-clip: text给文本设置填充效果时,需要将 color设置为transparent或者配合-webkit-text-fill-color: transparent一起使用。比如:

.element {
    background-image: url(image1.jpg);
    -webkit-text-fill-color: transparent;
    -webkit-background-clip: text;
}

也可以使用多个渐变来填充文本,实现多色文本的效果。比如 @Shireen Taj写的一个示例

你甚至还可以结合其他的一些 CSS 特性实现更有创意的效果,比如 @Bramus 提供的案例,结合CSS的圆锥渐变(conic-gradient())和CSS的自定义属性,使圆锥渐变的圆心跟随鼠标移动:

border-radius 对背景绘制区域的影响

在 《图解CSS:border-radius》一文中,详细与大家阐述了 border-radius的使用,尤其是当元素显式设置了边框(border-width)和内距(padding)时,会造成border-radius的嵌套。它会产生外圆角和内圆角:

可以给另何一个元素的每个方向设置不同的边框和内距尺寸,如果border-radius和它们产生的差值大于0就会产生内部圆角,而且圆角的半径就等于其差值。

我们已然知道,背景图像可以绘制在边框区域(<border-box>)和内距区域<padding-box>以及内容区域(<content-box>)。正如上图所示,当元素显式设置了border-radius时,就有可能会对背景的绘制区域产生相应的影响。众所周知,圆角是由border-radius决定的(外圆角),而内距区域(<padding-box>)的圆角(内圆角)是由border-widthborder-radius差值决定的。@Ana Tudor 在Codepen上制作了一个Demo,可以通过平面和3D状态来查看border-radius在这三种盒子框中,半径是如何计算的:

如果将背景运用进来,并且调整同不同的border-widthpaddingborder-radiusbackground-clip在不同盒子框中的绘制效果如下:

背景定位区域

在介绍 background-position 属性时说过,默认情况之下,它的起始点是在左上角边框内沿上,事实上就是 <padding-box> 的左上角。换句话说,background-position 的定位计算都是相对于<padding-box> 来计算的。

如果要改变这个原点位置,比如说,移到<border-box><content-box>顶点上,那就需要使用 CSS 的 background-origin 属性了。该属性指定背景图片定位区域。它和background-clip类似,接受三个值:

background-origin: <box>#

<box> = border-box | padding-box | content-box
  • border-box:背景图片定位原点以 <border-box> 区域为参考
  • padding-box:背景图片的定位原点以 <padding-box> 区域为参考
  • content-box:背景图片的定位原点以 <content-box> 区域为参考

其中,padding-box是其默认值。

可以结合不同 background-position 值,来看background-origin 取不同值给 background-position 带来的影响:

注意:当使用 background-attachmentfixed 时,该属性将被忽略不起作用。

background-origin 属性除了对 background-position 属性有影响之外,还对取值为百分比值的background-size有影响。比如下面这个示例:

.element {
    background-size: 50% 50%;
    background-origin: var(--origin);
}

正如上面示例所示,取%值的background-size,它的计算是相对于对应盒子的宽高来计算的:

  • background-origin 取值为 border-box时,background-size相对于<border-box>框的宽高计算
  • background-origin 取值为 padding-box时,background-size相对于<padding-box>框的宽高计算
  • background-origin 取值为 content-box时,background-size相对于<content-box>框的宽高计算

注意,只有background-size取百分比值的时候,才受background-origin的值影响!稍后我们在介绍background-size的时候还会详细介绍!

前面介绍background-clip的时候,向大家展示了一个渐变边框的效果,制作该效果的时候,就有用到 background-origin 属性。尤其在使用多背景来制作一些背景效果(比如纹理),就会将 background-clipbackground-originbackground-position 以及接下来要介绍的background-size 结合在一起使用。但有一点需要注意,如果 background-clippadding-boxbackground-originborder-boxbackground-position是左上角(初始值),并且元素有非零边框,那么背景图的左上角将被剪裁。

待续...