css - position 的定义与解析

85 阅读6分钟

本文介绍 css 属性值 position 的定义与解析。内容来源于 drafts.csswg.org/css-values-…

<position> 值用于指定对齐主体(如背景图片)在对齐容器(如背景定位区域)内的位置,通过指定边缘之间的偏移量对(默认为左侧和顶部)来实现。其语法如下:

<position> = <position-one> | <position-two> | <position-four>

<position-one> = [  left | center | right | top | bottom |  x-start | x-end | y-start | y-end |  block-start | block-end | inline-start | inline-end |  <length-percentage>]

<position-two> = [  [ left | center | right | x-start | x-end ] &&
  [ top | center | bottom | y-start | y-end ]
|
  [ left | center | right | x-start | x-end | <length-percentage> ]
  [ top | center | bottom | y-start | y-end | <length-percentage> ]
|
  [ block-start | center | block-end ] &&
  [ inline-start | center | inline-end ]
|
  [ start | center | end ]{2}
]

<position-four> = [  [ [ left | right | x-start | x-end ] <length-percentage> ] &&
  [ [ top | bottom | y-start | y-end ] <length-percentage> ]
|
  [ [ block-start | block-end ] <length-percentage> ] &&
  [ [ inline-start | inline-end ] <length-percentage> ]
|
  [ [ start | end ] <length-percentage> ]{2}
]

语法说明

  1. 如果只指定一个值(<position-one>),第二个值默认为 center

  2. 如果指定两个值(<position-two>):

    • 第一个 <length-percentage> 值表示水平位置,即对齐主体和对齐容器左边缘之间的偏移量
    • 第二个 <length-percentage> 值表示垂直位置,即对齐主体和对齐容器顶部边缘之间的偏移量
  3. 如果两个关键字都是 startend,第一个表示块轴,第二个表示内联轴

块轴和内联轴是 CSS 中用于描述文本布局方向的两个重要概念。它们的方向取决于书写模式:

  • 水平书写模式(默认)(中文、英文):块轴是垂直方向(上下);内联轴是水平方向(左右)
  • 垂直书写模式(日文竖排、蒙古文):块轴是水平方向(左右);内联轴是垂直方向(上下)
  1. 如果指定四个值(<position-four>),每个 <length-percentage> 表示由前面关键字指定的边缘之间的偏移量。例如:background-position: bottom 10px right 20px; 表示从底部边缘向上偏移 10px,从右侧边缘向左偏移 20px。

值说明

position 的值是一对偏移量(水平和垂直),每个偏移量作为计算的 <length-percentage> 值给出,表示对齐主体和对齐容器的左边缘和上边缘(分别)之间的距离。

<length-percentage>

<length-percentage> 值指定了对齐主体和对齐容器的指定边缘之间的偏移量大小。

length 的正值表示从对齐容器边缘向内偏移;负值表示从对齐容器边缘向外偏移。以下声明给出了从左上角开始的(水平,垂直)偏移量:

background-position: left 10px top 15px;   /* 10px, 15px */
background-position: left      top     ;   /*  0px,  0px */
background-position:      10px     15px;   /* 10px, 15px */
background-position: left          15px;   /*  0px, 15px */
background-position:      10px top     ;   /* 10px,  0px */

percentage 的水平偏移的百分比相对于(对齐容器宽度 - 对齐主体宽度);垂直偏移的百分比相对于(对齐容器高度 - 对齐主体高度)。例如:

  • 0% 0%:对齐主体的左上角与对齐容器的左上角对齐
  • 100% 100%:对齐主体的右下角与对齐容器的右下角对齐
  • 75% 50%:对齐主体上 75% 宽 与对其容器上 75%宽的点对齐;对齐主体上 50% 高的点与对齐容器上 50% 高的点对齐。

下面是 background-position: 75% 50% 的例子

background-position: 75% 50%

<style>
.container {
  width: 300px;
  height: 200px;
  background-color: #f0f0f0;
  /* 背景图片尺寸为 100x100px */
  background-image: url('https://picsum.photos/100/100');
  background-repeat: no-repeat;
  /* 背景图片位置:水平方向 75%,垂直方向 50% */
  background-position: 75% 50%;
  position: relative;
}

/* 添加标记点来显示对齐位置 */
.container::before,
.container::after {
  content: '';
  position: absolute;
  border-radius: 50%;
}

/* 容器上的对齐点 */
.container::before {
  width: 15px;
  height: 15px;
  background: red;

  left: 75%;
  top: 50%;
  transform: translate(-50%, -50%);
}

/* 背景图片上的对齐点 */
.container::after {
  width: 10px;
  height: 10px;
  background: blue;

  left: 75%;
  top: 50%;
  transform: translate(-50%, -50%);
}
</style>
<div class="container"></div>

在这个示例中:

  1. 容器尺寸为 300x200px
  2. 背景图片尺寸为 100x100px
  3. background-position: 75% 50% 表示:
    • 水平方向:背景图片上 75% 宽度的点与容器上 75% 宽度的点对齐
    • 垂直方向:背景图片上 50% 高度的点与容器上 50% 高度的点对齐
  4. 红色圆点表示容器上的对齐点
  5. 蓝色圆点表示背景图片上的对齐点

关键字说明

  • toprightbottomleft:分别偏移对齐主体和对齐容器的上/右/下/左边缘
  • y-starty-endx-startx-end:计算方式与对应的物理边缘再 x/y 轴的关键字相同
  • block-startblock-endinline-startinline-end:计算方式与对应的物理边缘在 block/inline 轴关键字相同
  • center:在对应轴上计算为 50% 的偏移量

解析与序列化

解析 <position>

当在语法中,<position> 与其他关键字 <length><percentage> 一起使用时,<position> 会被贪婪解析;它会尽可能多地消耗组件。

例如,transform-origin 定义了一个 3D 位置,实际上是 <position> <length>?。其中:<position> 定义 x 和 y 轴的位置,<length>? 是可选的 z 轴位置。像 left 50px 这样的值会被解析为 2 值的 <position>,省略了 z 分量;而像 top 50px 这样的值会被解析为单值的 <position> 后跟一个 <length>

这种解析方式的区别在于,当第一个值是水平方向的关键字(如 left、right)时,后面的值会被解析为 y 轴位置;当第一个值是垂直方向的关键字(如 top、bottom)时,后面的值会被解析为 z 轴位置。

序列化 <position>

当序列化 <position> 的指定值时:

如果只指定了一个组件:

  • 添加隐含的 center 关键字,并序列化为 2 组件值。
/* 原始值 */
background-position: left;    /* 单组件 */
/* 序列化后 */
background-position: left center;  /* 添加隐含的 center */

如果指定了两个组件:

  • 关键字序列化为关键字。
  • <length-percentage> 序列化为 <length-percentage>
  • 组件先序列化水平方向,然后是垂直方向。
/* 原始值 */
background-position: left top;     /* 两个关键字 */
background-position: 50px 30px;    /* 两个长度值 */
background-position: 50% 30%;      /* 两个百分比 */
/* 序列化后保持不变 */
background-position: left top;     /* 关键字保持为关键字 */
background-position: 50px 30px;    /* 长度值保持为长度值 */
background-position: 50% 30%;      /* 百分比保持为百分比 */

如果指定了四个组件:

  • 关键字和偏移量都会被序列化。
  • 组件先序列化水平方向,然后是垂直方向;或者先序列化块轴,然后是内联轴。
/* 原始值 */
background-position: left 10px top 20px;  /* 四个组件 */
/* 序列化后 */
background-position: left 10px top 20px;  /* 保持关键字和偏移量 */

注意:<position> 值永远不会序列化为单个值,即使单个值会产生相同的行为。这是为了避免在某些语法中(如 transform-origin)当 <position> 后面跟着 <length> 时产生解析歧义。

<position> 的计算值序列化为一对 <length-percentage>,表示从左边缘和上边缘的偏移量,按此顺序。

<position> 的组合

<position>插值定义为每个组件(x, y)的独立插值,这些组件被规范化为从左上角开始的 <length-percentage> 偏移量。

例如:

/* 例如在动画中从 left top 到 right bottom */
@keyframes move {
  from {
    background-position: right top;     /* 0% 0% */
  }
  to {
    background-position: left bottom; /* 100% 100% */
  }
}

在这个动画中:

  • x 轴:从 100% 到 0%
  • y 轴:从 0% 到 100%