CSS基础---了解CSS2.1以后的知识(3)

99 阅读1小时+

六、面试喜欢问的布局

(3)网格布局

说明: 给HTML元素设置display:grid或者display:inline-grid,网格布局就创建完成了,这两种创建的区别是前者不存在内联特性,后者存在内联特性,跟flex的两种创建是一样的

==> grid-template-columns和grid-template-rows属性 <==

说明: 这两个属性用来指定网格的数量和尺寸,这个缺点就是属性很长,此时可以使用缩写属性grid-template或者grid来解决,最后就是这两个属性的语法是一致的

/* 网格分为3列,从左往右每列的尺寸分别是80px、auto和100px */
grid-template-columns: 80px auto 100px; 

/* 网格分为了4行,从上 往下每行的高度分别是25%、100px、auto和60px */
grid-template-rows: 25% 100px auto 60px;
/* 使用缩写属性,这两种写法和上面的效果是等价的 */
grid-template: 25% 100px auto 60px / 80px auto 100px;
grid: 25% 100px auto 60px / 80px auto 100px;

image.png

<== 网格线的命名 ==>

说明: 就是给每一条线命名,这种命名的作用在于方便描述grid子项占据的网格区域,但实际上使用场景有限且只有在语义非常明确的页面级的布局中才有必要使用,在常规的具体某个模块的布局中根本用不到

语法:

grid-template-columns: <line-name> <track-size> ...
  • <line-name>: 表示划分的街道的名称,命名规则和CSS动画的命名规则一样

  • <track-size>:表示划分出来的小区的尺寸,可以是长度值、百分比值、fr单位和尺寸关键字等多种类型的属性值

举例:

/* 命名使用[]就可以了,每两条网格线之间就是它们相隔的距离了 */
grid-template-columns: [最开始的网格线1名称] 
                       80px 
                       [网格线2名称]
                       auto 
                       [网格线3名称]
                       100px 
                       [最后的网格线4名称];

注意: 如果网格线存在奇数条,那么最中间的那根线是两边格子共用的,那么它就可以取两个名字

grid-template-columns: [广告区-起始]
                       120px
                       [广告区-右 内容区-左]
                       600px
                       [内容区-终止];

<== 网格尺寸的关键字 ==>

min-content: 指一行或者一列中最小内容最大的那个尺寸值,与之对应的就是max-content,那这个就是寻找最大内容最大的那个宽度了

<div class="container">
    <item>css</item>
    <item></item>
    <item>css_world</item>
    <item></item>
    <!-- 此时这一列的最小宽度就是这个单元格的宽度,因为它在这一列中最小内容尺寸是最大的 -->
    <item>css_new_world</item>
    <item></item>
</div>
.container {
    display: grid;
    grid-template-columns: min-content auto;
}

image.png

auto: 尺寸的最大值是最大内容尺寸的最大值,这个最大值是会受justify-content属性和align-content属性影响;尺寸的最小值是最小内容尺寸的最大值,当min-width、min-height的属性值比最小内容尺寸大的时候,最小尺寸就是min-width、min-height的属性值;在没有这些条件的影响下,计算规则跟上面一样

==> 网格布局专用单位fr <==

说明: 这个表示分数,由于网格布局往往有多列或者多行,其中有些列有固定的 宽度,有些列的宽度就由页面自动分配,从而fr就表示这些自动分配列的尺寸划分比例,这个自动分配和auto关键字根据内容来分配的情况不一样,它完全是按照比例来的,跟内容无关,它的计算规则如下

  • 如果所有fr值之和大于1,则按fr值的比例划分可自动分配尺寸

  • 如果所有fr值之和小于1,最终的尺寸是可自动分配尺寸和fr值的乘法计算值

<== 所有列都使用fr ==>

/* fr值的和是3,大于1,因此按照比例划分,比例是1:1:1,因此网格宽度为三等分 */
.container { 
    grid-template-columns: 1fr 1fr 1fr;
}

image.png

/* 虽然fr值的比例还是1:1:1,但是由于fr值的和是0.6,小于1,*/ 
/* 因此网格的宽度是容器尺寸和0.2的乘法计算值,会有40%的尺寸 */ 
/* 是没有网格元素的 */
.container { 
    grid-template-columns: .2fr .2fr .2fr;
}

image.png

<== 部分列是固定长度值 ==>

说明: 如果部分列是固定长度值,那么可自动分配尺寸就是容器尺寸减去固定的尺寸

.container { 
    grid-template-columns: 200px 1fr 1fr 1fr;
}

image.png

<== 和auto关键字混合使用 ==>

说明: 都是先用容器尺寸减去auto关键字元素的最小内容尺寸,依次得到可以自动分配的尺寸,如果fr之和大于1,则可分配尺寸按照比例分配;如果小于1,按照上面的规则将fr的尺寸分配完毕,之后肯定会存在剩余尺寸,这个尺寸就给auto关键字元素

/* 最终的尺寸表现就是最后3列按照1:1:1的比例平分了容器尺寸减去宽度auto */
/* 这几个字符的宽度得到的尺寸 */
.container { 
    grid-template-columns: auto 1fr 1fr 1fr;
}

image.png

/* fr值的可自动分配尺寸是容器尺寸减去宽度auto这几个字符宽度得到的尺寸,*/
/* 后面3个设置.25fr网格的宽度为可自动分配尺寸乘以0.25的值,剩余的尺寸 */ 
/* 就是第一个网格宽度 */
.container { 
    grid-template-columns: auto .25fr .25fr .25fr;
}

image.png

==> minmax()函数 <==

语法:

/* 完整语法 */
minmax( [ <length> | <percentage> | min-content | max-content | auto ] , [ <length> | <percentage> | <flex> | min-content | max-content | auto ] )

/* 可以简单理解为这样,表示尺寸范围限制在min~max中 */
minmax(min,max)

注意: 由于<flex>数据类型作为第二个参数,那么以fr为单位的值也支持在第二个参数处出现

/* 不合法 */
minmax(1fr, 200px) 1fr 1fr;

/* 合法 */
minmax(200px, 1fr)
minmax(400px, 50%)
minmax(30%, 300px)
minmax(100px, max-content)
minmax(min-content, 400px)
minmax(max-content, auto)
minmax(auto, 300px)

==> fit-content()函数 <==

作用: 尺寸由内容决定,内容越多尺寸越大,但不超过限定的尺寸

语法:

/* 只支持数值和百分比值,fr值是不合法的 */
fit-content( [ <length> | <percentage> ] )

计算公式:

/* minimum是尺寸下限,如果不考虑min-width/min-height属性, */
/* 这个尺寸就是最小内容尺寸 */
fit-content(limit) = max(minimum, min(limit, max-content))

举例:

.container {
    display: grid;
    grid-template-columns: fit-content(100px) fit-content(100px) 40px auto;
}

item {
    outline: 1px dashed;
}
<div class="container">
    <!-- 第一列文字内容很少,由于都是中文,因此,min-content尺寸 -->
    <!-- 是一个中文尺寸。假设字体是宋体,则尺寸是16px, -->
    <!-- 而max-content尺寸是“内容少”这3个字的尺寸,为48px。 -->
    <!-- 因此,最终尺寸是48px -->
    <item>内容少</item>
    
    <!-- 第二列文字内容较多,由于包含避头标点逗号,因此, -->
    <!-- min-content尺寸是2个中文尺寸。假设字体是宋体, -->
    <!-- 则尺寸是32px,而max-content尺寸是全部文字的尺寸, -->
    <!-- 为176px。因此,最终尺寸是100px -->
    <item>内容很多,多到足够换行</item>
    
    <item>40px</item>
    <item>auto</item>
</div>

image.png

==> repeat()函数 <==

作用: 当网格尺寸可以重复的时候简化属性值的书写

/* 12列的网格布局,你可能会这样写,这直接看的人麻了 */
grid-template-columns: 40px auto 60px 40px auto 60px 40px auto 60px 40px auto 60px;

/* 此时可以使用repeat函数来进行简写,这两组效果是一样的 */
grid-template-columns: repeat(4, 40px auto 60px);

语法:

repeat( [ <positive-integer> | auto-fill | auto-fit ] , <track-list> )
  • <track-list>:不包括repeat()函数在内的所有grid-template-columns 支持的属性值,包括fr值和min-content/max-content关键字,以及minmax()函数和fit-content()函数等

  • <positive-integer>: 就是正整数的意思,表示尺寸重复的次数

  • auto-fill和auto-fit:相当于一个变量,表示一个不确定的重复次数,究竟重复多少次,是由grid容器和每一个grid子项的尺寸计算得到的,有时候,我们无法确定网格布局的列数,例如希望网格布局的列 数随着容器宽度变化,这个时候就不能将重复次数设置为固定的整数值,而应该使用auto-fill关键字或auto-fit关键字代替

注意:

  • repeat()函数不能和auto关键字一起使用,但是可以与长度值和百分比值一起使用

  • repeat()函数只能作用在grid-template-columns和grid-template-rows这两个CSS属性上

<== auto-fill关键字 ==>

举例理解:

<div class="container">
    <item></item>
    <item></item>
    <item></item>
    <item></item>
    <item></item>
</div>
.container {
    /* 部分CSS */
    grid-template-columns: repeat(auto-fill, 100px);
}
  • 如果grid容器宽度是640px,可以放下6个100px宽的grid子项,则此时auto-fill关键字值等同于6。只不过,由于这里grid子项元素(<item>元素)只有5个,因此还有一个grid子项是空白的

image.png

  • 如果grid容器宽度是375px,最多每行可以放下3个100px宽的 grid子项,则此时auto-fill关键字值等同于3。

image.png

说明: 由于最后一行右侧会出现这种不对称的空白区域,因此auto-fill关键字往往会和其他网格布局函数一起使用,例如minmax()函数,如果容器的宽度还是375px,可以这么使用

/* 实现了无论grid容器多宽,grid子项都会等比例充满grid容器(因为设置了1fr)*/
/* 同时保证宽度不小于100px,网格布局的列数自动计算分配的智能弹性布局效果 */
.container {
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}

image.png

<== auto-fit关键字 ==>

说明: 假设计算出来有六个网格,而页面上Html中只存在五个,那么还有一个网格在页面上不显示但是占位置,这种网格称为空白匿名网格,这个关键字的作用就是将页面上存在的这个网格移除掉,而auto-fill的这个网格是不会移除的,因此auto-fit关键字可以配合fr值一起使用,可以保证无论grid容器宽度多大,grid子项都可以填满grid容器

.container {
    width: 675px;
    display: grid;
    grid-gap: 5px;
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    border: 1px dashed;
}

item {
    background-color: deepskyblue;
    padding: 50px 0;
}
<div class="container">
    <item></item>
    <item></item>
    <item></item>
    <item></item>
    <item></item>
</div>

image.png

image.png

==> grid-template-areas属性 <==

说明: 这个属性是用来划分网格区域的,其目的是更方便网格的管理与维护

语法:

/* .表示空的单元格 */
/* none表示没有定义网格区域 */
grid-template-areas: 对应网格区域的名称 | . | none

举例: 张老板承包了一块土地,希望开展农作物种植和水产养殖业务,那么就需要对这块土地进行区域划分,决定哪片区域搞种植,哪片区域搞养殖。于是张老板就把这片土地划分成了3×4共12个小 格子,最上面3个格子种葡萄,最下面3个格子种西瓜,中间6个格子,左边2个养龙虾,右边4个养鱼

.container {
    width: 400px;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 1fr 1fr 1fr 1fr;
    /* 将其划分为四片区域 */
    grid-template-areas:
        "葡萄 葡萄 葡萄"
        "龙虾 养鱼 养鱼"
        "龙虾 养鱼 养鱼"
        "西瓜 西瓜 西瓜";
    height: 400px;
}

.container item {
    outline: 1px dotted;
    display: flex;
    align-items: center;
    justify-content: center;
}

.putao {
    grid-area: 葡萄;
}

.longxia {
    grid-area: 龙虾;
}

.yangyu {
    grid-area: 养鱼;
}

.xigua {
    grid-area: 西瓜;
}
<!-- 由于只有四个部分,从而gird子项只需要四个元素就好 -->
<div class="container">
    <item class="putao">葡萄种植区</item>
    <item class="longxia">龙虾养殖区</item>
    <item class="yangyu">鱼类养殖区</item>
    <item class="xigua">西瓜种植区</item>
</div>

image.png

注意:

  • 如果我们给网格区域命名了,但是没有给网格线命名,则系统会自动根据网格区域名称生成网格线名称,规则是在区域名称后面加-start和-end。例如,某网格区域名称是“葡萄”,则左侧网格线名称就是“葡萄-start”,右侧网格线名称就是“葡萄-end”。 因此,表面上设置在grid容器上的grid-template-areas属性需要与设置在grid子项上的grid-area属性(区域名称匹配)配合使用才能发挥作用

  • 网格区域一定要形成规整的矩形区域,无论是L形,还是凹的或凸的形状都会认为是无效的属性值。

==> 缩写属性grid-template <==

说明: grid-template属性是grid-template-rows、grid-template-columns和grid- template-areas属性的缩写

语法:

/* 属性值none表示将3个CSS属性都设置为初始值none */
grid-template: none;

/* 表示行尺寸或列尺寸的设置,支持repeat、minmax和fit-content函数 */
grid-template: <grid-template-rows> / <grid-template-columns>;

/* 包含grid-template-areas属性的语法,主要写一下这种的写法 */
grid-template: [ <line-names>? <string> <track-size>? <line-names>?] + [ / <explicit-track-list>]?;
  • 其中<string>指的就是grid-template-areas属性值,更准确地说是每一行网格的区域名称

  • <string>数据类型是必需的,其他数据类型都可以省略。例如<track-size>(也就是尺寸)可以省略(会使用auto代替计算)

  • 网格线的名称(也就是<line-name>数据类型)也是可以省略的。但是,在书写的时候<line-name>(网格线名称)数据类型总是出现在<track-size>(网格尺寸)和<string>(区域名称)数据类型的两侧

  • grid-template缩写语法支持<line-name>分开书写

  • 另外,包含<string>(区域名称)的grid-template缩写属性是不支持repeat()函数的,而minmax()函数和fit-content()函数是可以无障碍使用的

/* 第一点举例 */
.container {
    grid-template:
        "葡萄 葡萄 葡萄" 1fr
        "龙虾 养鱼 养鱼" 1fr
        "龙虾 养鱼 养鱼" 1fr
        "西瓜 西瓜 西瓜" 1fr
        / 1fr 1fr 1fr;
}

/* 第二点举例 */
.container {
    grid-template:
        "葡萄 葡萄 葡萄" 1fr
        "龙虾 养鱼 养鱼"
        "龙虾 养鱼 养鱼"
        "西瓜 西瓜 西瓜" 1fr;
}

/* 第三点举例 */
.container {
    grid-template:
        [row-name1-start] "葡萄 葡萄 葡萄" 1fr [row-name1-end row-name2-start]
        "龙虾 养鱼 养鱼" 1fr [row-name2-end]
        "龙虾 养鱼 养鱼" 1fr [row-name3-end]
        [row-name4-start] "西瓜 西瓜 西瓜" 1fr [row-name4-end]
        / [col-name-start] 1fr [col-name-end] auto;
}

/* 第四点举例,这两种写法的效果是一样的 */
.container {
    grid-template:
        "a" 1fr [name1-end name2-start]
        "b";
}
.container {
    grid-template:
        "a" 1fr [name1-end]
        [name2-start] "b";
}

/* 第五点举例,第一种写法不合法,第二种合法 */
.container {
    grid-template:
        "葡萄 葡萄 葡萄" 1fr
        "龙虾 养鱼 养鱼" 1fr
        "龙虾 养鱼 养鱼" 1fr
        "西瓜 西瓜 西瓜" 1fr
        / repeat(3, 1fr);
}
.container {
    grid-template:
        "葡萄 葡萄 葡萄" 1fr
        "龙虾 养鱼 养鱼" 1fr
        "龙虾 养鱼 养鱼" 1fr
        "西瓜 西瓜 西瓜" 1fr
        / auto minmax(100px, 1fr) fit-content(400px);
}

注意: 由于grid-template属性不会重置一些隐式的grid属性(如 grid-auto-columns属性、grid-auto-rows属性和grid-auto-flow属性),因此,大多数时候,还是推荐使用grid缩写属性代替grid-template缩写属性

==> grid-auto-columns和grid-auto-rows属性 <==

说明: 这两个属性是用来指定隐式网格的尺寸大小,对于隐式网格,就是在设定网格范围外面的网格,比如在css中设定的网格是2*2的布局,但是在html中出现了五个网格,那么最后一个网格就是隐式网格

<div class="container">
    <item>1</item>
    <item>2</item>
    <item>3</item>
    <item>4</item>
    <item>5</item>
</div>
.container {
    width: 500px;
    display: grid;
    grid-template: 1fr 1fr / 1fr 1fr;
    /* 隐式网格高度是60px */
    grid-auto-rows: 60px;
}

image.png

注意: 这两个属性的默认值是auto,可以是长度值、百分比值和fr值,也可以是min-content关键字和max-content关键字,也支持mimmax()函数和fit-content()函数,但是不支持repeat()函数

==> grid-auto-flow属性 <==

作用: 定义子项目元素的自动流动状态,类似于弹性布局中的flex-direction属性

语法:

grid-auto-flow: [ row | column ] || dense
  • row: 默认值,表示没有指定位置的网格在水平(行)方向上自然排列

  • column: 表示没有指定位置的网格在垂直(列)方向上自然排列

  • dense: 表示如果稍后出现的网格比较小,则尝试看看其前面有没有合适的地方放置该网格,使网格尽可能排列紧凑

网格布局表现: grid子项从第一行开始,从左往右依次填入网格格子,全部格子填满后,继续转到下一行,从左往右再次填满格子

<div class="container">
      <item>格子1</item>
      <item>格子2</item>
      <item>格子3</item>
      <item>格子4</item>
      <item>格子5</item>
      <item>格子6</item>
      <item>格子7</item>
      <item>格子8</item>
      <item>格子9</item>
    </div>
.container {
    display: grid;
    grid-template-columns: 1fr 1fr;
    line-height: 40px;
}

image.png

解释: 这种布局表现的底层是由grid-auto-flow属性控制的。因为grid-auto-flow属性的默认值是row,所以才优先水平排列。如果将grid-auto-flow的属性值修改为column,则会不一样

.container {
    display: grid;
    grid-template-rows: 1fr 1fr;
    grid-auto-flow: column;
    line-height: 40px;
}

image.png

举例: 因此,网格布局更适合块状元素的布局,比如下面的的A|B+C布局效果

.container {
    width: 400px;
    display: grid;
    grid-template:
        "a . ." 1fr
        "a . ." 1fr
        / 1fr 1fr 1fr;
    grid-auto-flow: column;
    grid-gap: 6px;
}

.container item:first-child {
    grid-area: a;
}

.container img {
    display: block;
    width: 100%;
    height: 100%;
}
<div class="container">
    <item><img src="D:1.jpg" /></item>
    <item><img src="D:1.jpg" /></item>
    <item><img src="D:1.jpg" /></item>
    <item><img src="D:1.jpg" /></item>
    <item><img src="D:1.jpg" /></item>
</div>

image.png

<== dense关键字 ==>

举例理解: 村里9户人家,每一户都分得了一块田地,大家的田地都是紧密相连的。孤寡老人李大爷和王大爷相继去世,于是就有两块田地是空的,因为有空缺,所以这个时候,如果我们使用无人机从田地上方拍一个照片,就会看到照片中的田地是稀疏的,不是紧密的。这个时候,村里又来了两户新人家,也要分田地。如果使用 dense属性值,则优先分配之前李大爷和王大爷留下的空缺的土地, 因为这样会让整片田地没有空缺,让田地是密集的,用代码实现就是这样的

<div class="container">
    <item>格子1</item>
    <item>格子2</item>
    <item>格子3</item>
    <item>格子4</item>
    <item>格子5</item>
    <item>格子6</item>
    <item>格子7</item>
    <item>格子8</item>
    <item>格子9</item>
</div>
.container {
    display: grid;
    grid-template-columns: 1fr 1fr;
}

.container item:first-child {
    grid-column-start: 2;
}

image.png

说明: 如果我们第一个格子被充分利用,让整个排列是紧密相连的,则可以使用dense关键字属性值

.container {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-auto-flow: dense;
}

.container item:first-child {
    grid-column-start: 2;
}

image.png

==> 缩写属性grid <==

说明: grid是这些CSS属性的缩写集合:grid-template-rows、grid-template-columns、 grid-template-areas、grid-auto-rows、grid- auto-columns和grid-auto-flow

<== grid: none ==>

说明: none表示设置所有子属性为初始值

<== grid: <grid-template> ==>

说明: 这个和grid-template的缩写语法一模一样

<== auto-flow在后面 ==>

语法:

/* <grid-template-rows>表示grid-template-rows属性的值,也就是 */
/* 每一行的高度尺寸和网格线命名等信息,由于后面存在?,因此表示 */
/* 这个属性是可以省略的,省略时自动解析为auto */

/* <grid-auto-columns>就是grid-auto-columns属性的值,也就是 */
/* 隐式网格的宽度大小 */
grid: <grid-template-rows> / [ auto-flow && dense?] <grid-auto-columns>?;

说明: 对于auto-flow && dense来说,它的作用是关键字auto-flow既可以表示关键字row,又可以表示关键字column,如果auto-flow关键字在斜杠左侧,则解析为 row关键字;如果在斜杠右侧,则解析为column关键字。因为在网格布局的缩写属性中,斜杠左侧的都表示行控制,斜杠右侧的都表示列控制。

<== auto-flow在前面 ==>

语法:

grid: [ auto-flow && dense? ] <grid-auto-rows>? / <grid-template-columns>

说明: 此语法和auto-flow关键字在后面的语法类似,只是这里的语法表示斜杠前面是隐式网格,后面是显式网格,其它的都是一样的

==> 间隙设置属性column-gap和row-gap <==

语法:

.container {
    /* <line-size>是网格间的间隙尺寸,可以是长度值,也可以是百分比值 */
    column-gap: <line-size>;
    row-gap: <line-size>;
}

举例: 给定一个简单的2×2网格,设置水平间隙为20px,垂直间隙为10px

.container {
    display: grid;
    height: 150px;
    grid: 1fr 2fr / 2fr 1fr;
    /* 列间隙20px,行间隙10px */
    column-gap: 20px;
    row-gap: 10px;
}

image.png

==> 缩写属性gap <==

说明: 表示网格间隙的,它的语法就是行的间隙和列的间隙,如果只有一个值,那么行列间隙都相等,同时间隙支持数值和百分比值,也支持calc()函数,就是很简单的一个属性

column-gap: 20px; 
row-gap: 10px;

/* 上面的语法可以缩写成下面这样 */
gap: 10px 20px;

==> 元素对齐属性justify-items和align-items <==

说明: 在网格布局中,以-items结尾的对齐属性表示控制每一个元素在自己所在的网格中的对齐表现。justify-items属性用来定义元素在网格中的水平对齐表现,align-items属性则是用来定义元素在网格中的垂直对齐表现

<== 了解justify-items ==>

常用语法:

justify-items: stretch | start | end | center
  • stretch: 元素水平尺寸拉伸,填满整个网格的水平空间

  • start: 元素的水平尺寸收缩为内容大小,同时沿着网格线左侧对齐显示

  • end: 元素的水平尺寸收缩为内容大小,同时沿着网格线右侧对齐显示

  • center: 元素的水平尺寸收缩为内容大小,同时在当前网格区域内部水平居中对齐显示

不常见的关键字:

  • self-start: 它和start的区别在于相对当前元素所处的网格的起始线对齐

  • self-end: 它和self-start的区别在于相对当前元素所处的网格的结束线对齐

  • left: 无视文档流方向,元素尺寸收缩,同时容器的网格线左对齐

  • right: 无视文档流方向,元素尺寸收缩,同时容器的网格线右对齐

  • legacy: 默认值,让关键字属性值更有效地被子元素继承,通常和left关键字、right关键字或center关键字一起使用,和其他对齐关键字属性值一起使用是非法无效的

<== 了解align-items ==>

常用语法:

align-items: normal | stretch | start | end | center | baseline;
  • normal: 默认值,会根据使用场景的不同表现为stretch或者start

  • stretch: 元素的尺寸在垂直方向进行拉伸,以填满整个网格的垂直空间

  • start: 元素的垂直尺寸收缩为内容大小,同时沿着上网格线对齐显示

  • end: 元素的垂直尺寸收缩为内容大小,同时沿着下网格线对齐显示

  • center: 元素的垂直尺寸收缩为内容大小,同时在当前网格区域内部垂直居中对齐显示

  • baseline: 为每一行的各个grid子项沿着基线对齐。如果某一项都是块级元素,没有基线,则在Firefox浏览器中的对齐会失败;而在Chrome浏览器中,grid子项的底边缘会作为基线,依然会有基线对齐效果。因此,如果项目需要兼容Firefox浏览器,务必要保证grid子项中都有内联元素

==> 缩写属性place-items <==

语法:

place-items: <align-items> <justify-items>?

注意: place-items属性在弹性布局中也是有效的,不过其只能控制垂直方向上的对齐表现,此外,IE浏览器不支持这个属性,如果不考虑兼容性问题,使用CSS实现垂直居中的效果的最佳方法就是使用place-items

.container { 
    display: grid;
    place-items: center;
}

==> 整体对齐属性justify-content和align-content <==

说明: 这两个属性分别指定了网格元素整体水平方向和垂直方向上的分布对齐方式,要想justify-content属性和align-content属性起作用,就需要让grid子项的总尺寸小于grid容器的尺寸。要么给gird子项设置较小的具体的尺寸值,要么让gird子项的尺寸是auto,同时保证内容尺寸较小

/* 由于水平和垂直方向都有40px的剩余空间,此时的justify-content属性 */
/* 和align-content属性就有用武之地了 */
.container {
    display: grid;
    width: 240px;
    height: 240px;
    grid-template: 100px 100px / 100px 100px;
}

常用语法:

justify-content: normal | stretch | start | end | center | space-between | space-around | space-evenly; 

align-content: normal | stretch | start | end | center | space-between | space-around | space-evenly;
  • normal: 默认值,效果和stretch一样

  • stretch: 可以看成justify-content属性和align-content属性的默认值,表示拉伸,表现为尺寸填满grid容器。拉伸效果需要在网格目标尺寸设为auto的时候才有效,如果固定了宽高,则无法拉伸

  • start: 逻辑CSS属性值,与文档流方向相关。水平方向上默认表现为左对齐,垂直方向上默认表现为顶对齐

  • end: 逻辑CSS属性值,与文档流方向相关。水平方向上默认表现为右对齐,垂直方向上默认表现为底对齐

  • center: 表现为水平居中对齐或垂直居中对齐

  • space-between: 表现为grid子项两端对齐,中间剩余空间等分

  • space-around: 表现为每个grid子项的上下或左右两侧都环绕互不干扰的相同尺寸的空白间距,在视觉上表现为grid子项边缘处的空白尺寸只有中间空白尺寸的一半

  • space-evenly: 表现为每个grid子项上下或左右两侧的空白间距完全相等

==> 缩写属性place-content <==

说明: 可以让align-content属性和justify-content属性写在同一个CSS声明中

语法:

place-content: <align-content> <justify-content>?

注意: place-content属性在弹性布局中也是有效的,不过需要IE浏览器和Edge浏览器都不支持place-content这个属性

==> 区间范围设置属性 <==

说明: 列范围设置属性grid-column-start/grid-column-end和行范围设置属性grid-row-start/grid-row-end是应用在grid子项上的,通过指定grid子项行和列的起止位置来表明当前grid子项所占据的范围

语法:

grid-column-start: <integer> | <name> | <integer> <name> | span <number> | span <name> | auto;

grid-column-end: <integer> | <name> | <integer> <name>| span <number> | span <name> | auto;

grid-row-start: <integer> | <name> | <integer> <name>| span <number> | span <name> | auto;

grid-row-end: <integer> | <name> | <integer> <name>| span <number> | span <name> | auto;

<== 语法点 ==>

<integer>: 指起止于第几条网格线,可以是负整数,但是不能是0,负整数表示从右侧开始计数网格线

.container {
    display: grid;
    grid: auto / repeat(6, 1fr);
}

.item {
    /* 起始于从右边缘往左数第三条线(包括边缘线) */
    grid-column-start: -3;
    /* 终止于从左边缘往右数第二条线(包括边缘线) */
    grid-column-end: 2;
    background: deepskyblue;
}

image.png

<name>: 自定义的网格线的名称,浏览器在找不到名称为A的网格线的时候,会自动补全-start或者-end继续寻找,所以在命名的时候推荐用-start和-end给网格线 命名

.container {
    display: grid;
    grid-template-columns: [A-start] 100px [A-end B-start] auto [B-end] auto;
}

/* 这里是可以找到的 */
.item {
    grid-column-start: A;
    grid-column-end: B;
    background: deepskyblue;
}

image.png

<integer> <name>: 表示当前名称为<name>的第<integer>个网格线,从定义上看,网格布局中需要有很多个名称一样的网格线才能匹配对应的网格线,但如果没有这么多同样名称的网格线会怎样呢?此时,浏览器会自动创建符合数量的隐式网格,这些隐式网格的网格线都是指定的这个名称

.container {
    display: grid;
    grid-template-columns: [A] 80px [B] auto [C] 100px [D];
}

/* 在显式网格中,B名称只有1个,但是我们设置的是grid-column-start:B 4 */
/* 也就是第四个名称为B的网格线作为起始边缘,数量不够怎么办?系统会自动 */
/* 创建几个隐式网格。在网格布局中,隐式网格都创建于显式网格的后面或者下面, */
/* 这里是列的起始位置,因此创建在显式网格的后面,也就是网格线D的后面 */
/* 在这个例子中需要再额外创建3个格子,才能满足4个名称为B的网格线的需求 */
.item {
    grid-column-start: B 4;
    grid-column-end: C;
    background: deepskyblue;
}

image.png

span<number>: 表示当前网格会自动跨越指定的网格数量

span <name>: 表示当前网格会自动扩展,直到选中指定的网格线名称

auto: 默认值,表示自动,默认跨度是1个格子

<== span关键字 ==>

作用: 用来合并网格

举例:

.item-b {
    /* item-b网格左侧开始于<integer>为2的网格线 */
    grid-column-start: 2;
    /* item-b多列网格合并,合并的网格结束于<name>为“纵线3”的网格线 */
    grid-column-end: span 纵线3;
    /* item-b网格上方开始于<name>为“第一行开始”的网格线。 */
    grid-row-start: 第一行开始;
    /* item-b网格总共有3行进行合并 */
    grid-row-end: span 3;
}
<div class="container">
    <div class="item-b"></div>
</div>

image.png

注意:

  • span <number>中的<number>不能是负值,也不能是0,也不能是小数,这个语法不建议grid-column-start和grid-column-end同时使用

  • 对于span <name>而言,如果网格布局中有1个网格线的命名是B或者B-start,则有和没有span关键字的效果都是一样的,如果网格布局中有多个网格线的命名是B或者是B-start,则span B表示离grid-column-end位置最近的一个网格线B, 而如果属性值是B而不是span B,则起始位置会是离grid-column-end位置最远的一个网格线B;如果网格布局中没有网格线的命名是B或者B-start,则此时span关键字就会自己在显式网格对应方位的边上创建名称为B的隐式网格线

==> 缩写属性grid-column和grid-row <==

说明: grid-column和grid-row都是缩写属性,前者是grid-column-start/grid-column-end属性的缩写,后者是grid-row-start/grid-row-end属性的缩写。两者的缩写在语法上使用斜杠分隔,其实就是使用斜杠把原始CSS属性的值原封不动地区分开,且不讲究顺序,建议把起始范围写在前面

/* 这两组的效果是相同的 */
.item-b {
    grid-column: 2 / span 纵线3;
    grid-row: 第一行开始 / span 3;
}

.item-b {
    grid-column-start: 2;
    grid-column-end: span 纵线3;
    grid-row-start: 第一行开始;
    grid-row-end: span 3;
}

==> 缩写属性grid-area <==

说明: 它是grid-row-start、grid-column-start、grid-row-end和grid-column-end这4个CSS属性的缩写,除了常见的把某些子属性通过语法结合在一起,还可以直接使用grid-template-areas设置的名称作为属性值,从而非常便捷地实现区域范围设置

非正式语法:

/* 前半部分指区域名称,由grid-template-areas属性创建 */
/* 后半部分指占据网格区域的行列起止位置 */
grid-area: <area-name> | <row-start> / <column-start> / <row-end> / <column-end>

举例:

.container {
    /* 创建了一个4×3的显式网格 */
    grid: 1fr 1fr 1fr / 1fr 1fr 1fr 1fr;
}

.item {
    /* 水平网格线位置起止分别是1和3,垂直网格线位置起止分别是2和4。 */
    grid-area: 1 / 2 / 3 / 4;
}

image.png

<== 深入grid-area属性 ==>

正式语法:

grid-area: <grid-line> [ / <grid-line> ]{0,3}

说明: grid-area属性的范围全部都是通过网格线的名称或序号确定的,在使用grid-template-areas属性创建的网格名称的时候,浏览器会自动给当前网格周围的网格线进行命名,且命名不会被grid-template-rows或grid-template-columns属性中的网格线命名覆盖

/* 这里创建了一个3×3的网格,并将最中间的网格命名为A,此时 */
/* 浏览器会做一件事情,那就是会把网格A四周的网格线自动命名为Astart和A-end */
.container {
    display: grid;
    grid:
    ". . ." 1fr
    ". A ." 1fr
    ". . ." 1fr / 1fr 1fr 1fr;
}
/* 因此这两个设置是等价的 */
grid-area:A
grid-area: A-start / A-start / A-end / A-end;

image.png

注意: 这个属性支持1~4个网格线名称

  • 四个值: 则这4个值依次表示grid-row-start、grid-column-start、grid-row-end和grid-column-end这4个属性。这和其他非对齐控制的网格布局属性一样,row在前,column在后;start在前,end在后

  • 三个值: 也就是把grid-column-end值省略了。如果grid-column-start值是自定义的命名,则认为grid-column-end值也是这个自定义命名的值;如果grid-column-start值是其他值,则认为grid-column-end值是auto。

  • 两个值: 说明grid-row-end值也被 省略了。如果grid-row-start值是自定义的命名,则认为grid-row-end值也是这个自定义命名的值;如果grid-row-start值是其他值,则认为grid-row-end值是auto

  • 一个值: 说明grid-column-start值也被省略了。如果grid-row-start值是自定义的命名,则4个值都使用该命名;如果grid-row-start值是其他值,则认为剩下的其他3个值是auto。

/* 四个值举例,下面的写法都是等价的 */
grid-area: 4 A / span 4 / B / D;

grid-row-start: 4 A;
grid-column-start: span 4;
grid-row-end: B;
grid-column-end: D;
/* 三个值举例,下面的写法都是等价的 */
grid-area: A / B / C;
grid-area: A / B / C / B;

grid-area: 1 / 2 / 3;
grid-area: 1 / 2 / 3 / auto;
/* 两个值举例,下面的写法都是等价的 */
grid-area: A / B;
grid-area: A / B / A / B;

grid-area: 1 / 2;
grid-area: 1 / 2 / auto / auto;
/* 一个值举例,下面的写法都是等价的 */
grid-area: A;
grid-area: A / A / A / A ;

grid-area: 1;
grid-area: 1 / auto / auto / auto ;

<== 简单应用 ==>

举例: 可以让2个元素轻松实现层叠效果

figure {
    display: inline-grid;
}

figure > img,
figure > figcaption {
    grid-area: 1 / 1 / 2 / 2;
}

figure > figcaption {
    align-self: end;
    text-align: center;
    background-color: #0009;
    color: #fff;
    line-height: 2;
}
<figure>
    <img src="D:1.jpg" />
    <figcaption>游戏角色</figcaption>
</figure>

image.png

==> grid子项对齐属性justify-self和align-self <==

说明: justify-self属性用来设置单个网格元素的水平对齐方式,align-self属性则用来设置单个网格元素的垂直对齐方式,除了auto默认值属性值,其他各个属性值的含义与justify-items和align-items属性中属性值的含义是一样的

语法:

justify-self: auto | normal | stretch | start | end | center | baseline; 

align-self: auto | normal | stretch | start | end | center | baseline;

==> 缩写属性place-self <==

说明: 可以让align-self和justify-self属性写在单个声明中

语法:

place-self: <align-self> <justify-self>?

(4)CSS Shapes布局

说明: 这种布局可以实现不规则的图文环绕效果,它需要和float属性配合使用

==> 了解shape-outside属性 <==

语法:

shape-outside: none | <shape-box> | <basic-shape> | <image> 
  • none: 表示普通的矩形环绕

  • <shape-box>: <shape-box>(图形盒子)是图形相关布局中的一个名词,<shape-box>比clip-path属性中的<geometry-box>(几何盒子)支持的盒子类型要少一些,就只有CSS2.1中的4种基本盒模型,分别是margin-box、border-box、padding-box和content-box。<shape-box>的作用是指定文字环绕时依照哪个盒子的边缘来计算。

  • <basic-shape>: 指基本形状函数,它和clip-path剪裁属性支持的基本形状函数一模一样

  • <image>: 指图像类,包括URL链接图像、渐变图像、cross-fade()函数图像、element()函数图像等

/* 关键字属性值 */
shape-outside: none;
shape-outside: margin-box;
shape-outside: padding-box;
shape-outside: border-box;
shape-outside: content-box;

/* 函数值 */
shape-outside: circle();
shape-outside: ellipse();
shape-outside: inset(10px 10px 10px 10px);
shape-outside: polygon(10px 10px, 20px 20px, 30px 30px);

/* <url>值 */
shape-outside: url(image.png);

/* 渐变值 */
shape-outside: linear-gradient(
    45deg,
    rgba(255, 255, 255, 0) 150px,
    red 150px
);

<== 关键字属性值举例 ==>

说明: 注意文字和图像接触的位置

.container {
    width: 400px;
    outline: 1px dashed;
    outline-offset: 8px;
    margin: 0 8px;
}

.shape {
    float: left;
    width: 60px;
    height: 60px;
    padding: 10px;
    margin: 10px;
    border: 10px solid;
    border-radius: 50%;
    background-color: currentColor;
    background-clip: content-box;
    color: #cd0000;
}

.margin-box {
    shape-outside: margin-box;
}

.border-box {
    shape-outside: border-box;
}

.padding-box {
    shape-outside: padding-box;
}

.content-box {
    shape-outside: content-box;
}
<h4>shape-outside:none</h4>
<div class="container">
    <span class="shape"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

<h4>shape-outside:margin-box</h4>
<div class="container">
    <span class="shape margin-box"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

<h4>shape-outside:border-box</h4>
<div class="container">
    <span class="shape border-box"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

<h4>shape-outside:padding-box</h4>
<div class="container">
    <span class="shape padding-box"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

<h4>shape-outside:content-box</h4>
<div class="container">
    <span class="shape content-box"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

image.png

image.png

image.png

image.png

image.png

<== 基本的形状函数举例 ==>

  • circle():表示圆
  • ellipse():表示椭圆
  • inset():表示内矩形(包括圆角矩形)
  • polygon():表示多边形

circle()语法与举例:

/* 语法,这里表示圆的半径和圆心的位置都可以省略 */
circle( [<shape-radius>]? [at <position>]? )
<h4>shape-outside: circle()</h4>
<div class="container">
    <span class="shape"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>
.container {
    width: 400px;
    outline: 1px dashed;
    outline-offset: 8px;
    margin: 0 8px;
}

.shape {
    float: left;
    width: 100px;
    height: 100px;
    background-color: currentColor;
    shape-outside: circle(50% at 0 50%);
    clip-path: circle(50% at 0 50%);
    color: #cd0000;
}

image.png

ellipse()语法与举例:

/* 语法,前面指水平半径和垂直半径,后面指椭圆圆心 */
ellipse( [<shape-radius>{2}]? [at <position>]? )
/* 在前面css的基础上改动shape的代码就可以 */
shape-outside: ellipse(50px 75px at 50% 50%);
clip-path: ellipse(50px 75px at 50% 50%);

image.png

inset()语法与举例:

/* 其中shape-arg是必须参数,可以是1~4个值,这4个值分别 */
/* 表示以当前元素的4个边缘为起点,从顶部、右侧、底部和左侧向 */
/* 内偏移的大小,这就是这4个值可以定义矩形形状边缘位置的原理 */
/* 这4个值是支持缩写的,也就是我们可以根据场景使用1个、2个、3个 */
/* 或4个值,具体的缩写规则和margin、padding等属性的缩写规则一致 */

/* border-radius则表示矩形形状的圆角大小 */
inset( <shape-arg>{1,4} [round <border-radius>]? )
/* 在前面css的基础上改动shape的代码就可以 */
shape-outside: inset(10px 20px);
clip-path: inset(10px 20px);

image.png

polygon()语法与举例:

/* fill-rule表示填充规则,可以是nonzero和evenodd,默认值是nonzero */
polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
/* 在前面css的基础上改动shape的代码就可以 */
shape-outside: polygon(0 0, 0 100px, 100px 200px);
clip-path: polygon(0 0, 0 100px, 100px 200px);

image.png

<== 图像类举例 ==>

URL图像: 浏览器会解析图像的透明和非透明区域,在默认设置下,浏览器会让文字沿着图像的非透明区域边缘排列,实现文字环绕不规则图形布局的效果

/* 还是在前面的代码基础上更改 */
.shape {
    float: left;
    width: 150px;
    height: 200px;
    background-color: currentColor;
    /* 文字环绕这个鹦鹉 */
    shape-outside: url(../images/bird.png);
    -webkit-mask: url(../images/bird.png) no-repeat;
    mask: url(../images/bird.png) no-repeat;
    /* 鹦鹉赋色并显示 */
    color: #cd0000;
}

image.png

渐变图像: 渐变可以是线性渐变、径向渐变、锥形渐变和对应的重复渐变。例如绘制一个斜向线性渐变

/* 还是在前面的代码基础上更改 */
.shape {
    float: left;
    width: 150px;
    height: 120px;
    --gradient: linear-gradient(
        to right bottom,
        #cd0000,
        transparent 50%,
        transparent 90%,
        #cd0000
    );
    shape-outside: var(--gradient);
    background: var(--gradient);
}

image.png

==> 了解shape-margin属性 <==

作用: 控制文字环绕图形时文字与元素边界的距离,这个距离的范围是从0到浮动元素的边界,超过浮动元素边界的时候,布局效果如同普通浮动布局效果,没有不规则的图形环绕效果

==> 了解shape-image-threshold属性 <==

作用: 指图像环绕时候的半透明阈值,默认是0.0,也就是图像透明度为0的区域边界才能被文字环绕。同理,如果属性值是0.5,则表示透明度小于0.5的区域都可以被文字环绕

.container {
    width: 400px;
    outline: 1px dashed;
    outline-offset: 8px;
    margin: 0 8px;
}

.shape {
    float: left;
    width: 200px;
    height: 120px;
    --gradient: linear-gradient(145deg, #cd0000, transparent);
    background: var(--gradient);
    shape-outside: var(--gradient);
    transition: shape-image-threshold 0.3s;
}

.threshold-1 .shape {
    shape-image-threshold: 0.3;
}

.threshold-2 .shape {
    shape-image-threshold: 0.6;
}

.threshold-3 .shape {
    shape-image-threshold: 0.8;
}
<h4>shape-image-threshold: 0</h4>
<div class="container">
    <span class="shape"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

<h4>shape-image-threshold: 0.3</h4>
<div class="container threshold-1">
    <span class="shape"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

<h4>shape-image-threshold: 0.6</h4>
<div class="container threshold-2">
    <span class="shape"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

<h4>shape-image-threshold: 0.8</h4>
<div class="container threshold-3">
    <span class="shape"></span>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容。
    </p>
    <p>
        文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字内容文字。
    </p>
</div>

image.png

==> 布局举例 <==

举例: 文字在圆形内沿着弧线边界排版

.circle {
        border-radius: 50%;
        width: 207px;
        height: 240px;
        color: white;
        background-color: deepskyblue;
        text-shadow: 1px 1px rgba(0, 0, 0, 0.5);
        padding: 10px;
        text-align: justify;
      }

      /* 使用两个元素来模拟这个椭圆,每个元素是这个椭圆的一半 */
      before {
        float: left;
        width: 50%;
        height: 100%;
        shape-outside: radial-gradient(
          farthest-side ellipse at right,
          transparent 100%,
          red
        );
      }
      
      after {
        float: right;
        width: 50%;
        height: 100%;
        shape-outside: radial-gradient(
          farthest-side ellipse at left,
          transparent 100%,
          red
        );
      }
<p class="circle">
    <before></before>
    <after></after>
    在CSSShapes布局问世之前,让网页中的文字像杂志一样有任意样式的排版是很难的,过去一直都是用网格、盒子和直线构造排版样式。CSSShapes布局允许我们定义文本环绕的几何形状,这些形状可以是圆、椭圆、简单或复杂的多边形,甚至是多彩的渐变图形。
</p>

image.png

七、不同设备的响应与匹配

(1)@media规则

/* 当设备屏幕的宽度小于480px的时候隐藏侧边栏 */
@media only screen and (max-width: 480px) {
    aside {
        display: none;
    }
}

说明: @media规则是用来匹配不同设备的,比如上面这段代码,其中@media only screen and (max-width: 480px)就是一条@media规则,它由媒体查询修饰符only、媒体类型screen、媒体条件and和媒体特性max-width四部分组成

==> @media规则的详细介绍 <==

<== 媒体查询修饰符 ==>

说明: 修饰符有两个,一个是only,这个关键字在现在已经没什么用了;另一个是not,它否定的是整条查询语句,而不是单单否定一个媒体类型

<== 媒体类型 ==>

说明: 这里只需要关注三个媒体类型就好,分别是screenprintall;其中屏幕阅读器都是识别screen,print查询中的CSS代码只会在打印和打印预览的时候生效;而在打印之外的场景均使用screen,因此,screen媒体类型最常用;all则表示匹配所有设备,无论是打印设备还是其他普通的现实设备

注意: 如果需要指定多个媒体类型,使用逗号隔开就好

@media screen, print {  }

<== 媒体条件 ==>

说明: 媒体条件有3个,即notandor,其中not表示否定某个媒体特性,而and和or则是有效且常用的,前者表示条件同时满足,后者表示满足之一即可

<== 媒体特性 ==>

说明: 媒体特性指的是对媒体特性的描述信息,包括浏览器、用户设备和使用环境等特性,常见的特性如下表

名称含义
aspect-ratio输出设备(可以认为是显示器)可视区域的宽度和高度的比例
color输出设备颜色的位数,如果是0,则表示不是彩色设备
color-index输出设备颜色查找表的完整数量
height用于设备的高度的查询与匹配
width最实用的媒体特性,表示设备的宽度的查询与匹配。在网页中,高度是无限的,宽度是有限的,因此,宽度的查询和匹配特性是高频使用的,是响应式布局和常规移动设备适配必用的媒体特性
monochrome表示与通过单色帧缓存区的每像素的字节数量进行匹配
orientation判断设备是横屏还是竖屏。支持landscape和portrait两个值,分别 表示横屏和竖屏,由于orientation媒体特性是通过对比视区的高度和宽度来确定设备是横屏还是竖屏的,因此也会存在软键盘弹出时把竖屏当作横屏的情况
resolution设备的分辨率检测
scan设备接受的图像投影的绘制方法检测
any-hover表示是否有任意输入设备可以经过某个元素
hover表示主输入设备是否可以经过某个元素
any-pointer表示是否有任意输入设备可以触控操作,以及如果可以触控操作,精度应为多少
pointer表示主输入设备是否可以触控操作,以及如果可以触控操作,精度应为多少
color-gamut表示对设备的色域的检测,也就是对支持的颜色范围的检测。支持 srgb、p3和rec2020这3个关键字属性值的特性检测
inverted-colors表示检测用户设备是否反转了颜色
prefers-color-scheme可以用来检测用户是否使用了深色模式
prefers-contrast表示检测当前Web内容是高对比度还是低对比度。此特性对提升无障碍阅读能力很有用
prefers-reduced-motion可以用来检测用户是否配置了没有必要的动画选项

==> 深色模式和动画关闭的检测 <==

<== prefers-color-scheme ==>

说明: 这个媒体特性可以用来检测当前网页是否处于深色模式,它支持以下参数值;如果需要在JavaScript代码中对系统的深浅主题进行判断,可以使用原生的window. matchMedia()方法进行判断

  • no-preference: 表示系统没有告知用户使用的颜色方案
  • light: 表示系统倾向于使用浅色模式
  • dark: 表示系统倾向于使用深色模式

举例: 对现有网页快速进行深色模式 image.png

@media (prefers-color-scheme: dark) {
    body {
        /* 这种深色做法适合不太重要的页面 */
        filter: invert(1) hue-rotate(180deg);
        background-color: #000;
    }

    img {
        filter: invert(1) hue-rotate(180deg);
    }
}

image.png

<== prefers-color-scheme ==>

说明: 它用来检测操作系统是否设置了关闭不必要动画的操作,支持的参数值如下

  • no-preference: 表示用户没有通知系统任何首选项

  • reduced: 表示用户已通知系统,他们更喜欢删除或者替换基于运动的动画,因为该类型动画会引发前庭功能紊乱患者的不适(类似晕车),或者一部分人就是单纯动画疲劳,也可能想要更省电

解释: 如果用户选择了关闭动画,那么要让Web应用中的动画同步关闭。例如,弹框出现就不需要有弹一下的动画效果,评论框也不需要从下方出现,直接让评论框显示出来就可以,此时就可以像下面这样设置

/* 这个地方我也不是很理解 */
@media (prefers-reduced-motion) {
    * {
        animation: none;
        transition: none;
    }
}

==> 鼠标行为和触摸行为的检测 <==

figcaption {
    display: none;
}

figure:hover figcaption {
    display: block;
}
<figure>
    <img src="1.jpg" />
    <figcaption>图片描述</figcaption>
</figure>

举例理解: 假设有这样一个交互效果,当鼠标经过图片的时候,在图片上方显示一段描述文字,这个交互效果可以使用:hover伪类轻松地实现,但是这种实现方式在移动端上面就存在体验问题,所以为了解决这个问题,决定在移动端figcaption元素默认显示,此时你可能会使用@media (max-width: 480px)类似这样的CSS来实现,但是仅通过屏幕宽度判断是不是触屏设备是非常片面和不准确的,只能应付大部分场景,而不能准确覆盖所有场景,这里就可以用到下面介绍的新特性了

<== any-hover ==>

说明: 可用于测试是否有任意可用的输入装置可以悬停在元素上,就像鼠标这个输入装置就可以控制鼠标指针的位置,以及悬停在元素上,那么可以这样简单的理解就是any-hover可以用来检测设备是否接入了鼠标的,但这是不严谨的,它支持两个关键字值

  • none:表示没有输入装置可以实现悬停效果,或者没有可以实现指向的输入装置

  • hover:表示一个或多个输入装置可以触发元素的悬停交互效果

/* 然后上面的例子中的优化应该这样写才对,此时在移动端设备上是没有伪类效果的 */
@media (any-hover: none) { 
    figcaption {
        display: block;
    } 
}

<== hover ==>

说明: hover媒体特性的语法和作用与any-hover是一样的,两者的主要区别在于,any-hover检测任意输入装置,而hover只检测主要的输入装置,它也支持两个属性值,由于hover媒体特性的兼容性更好,因此在选择的时候以hover为主

  • none: 表示主输入装置根本无法悬停或无法方便地悬停(例如,使用长点击来模拟悬停,而长点击这种交互并不方便),或者没有主输入装置

  • hover: 表示主输入装置可以触发元素的悬停交互

<== pointer和any-pointer ==>

说明: 这一组媒体特效是与点击事件相关的,主要用于识别当前环境,判断是否可以非常方便地进行点击操作,它们分别支持的属性值如下

  • none:表示主输入装置点击不可用
  • coarse:表示主输入装置点击不精确
  • fine:表示主输入装置点击很精准
  • none:表示没有可用的点击设备

  • coarse:表示至少有一个设备的点击不是很精确。例如,使用手指操作手机就属于点击不精确

  • fine:表示有点击很精准的设备。例如,用鼠标操作的计算机浏览器

举例: 如果用户的点击操作很精确,就可以使用一个传统按钮;如果用户的点击操作不是很精确,那就可以做一个大大的、宽宽的按钮,方便用户点击操作

复选框,点我:<input type="checkbox" />
@media (pointer: coarse) {
    input[type="checkbox"] {
        width: 30px;
        height: 30px;
    }
}

image.png

image.png

(2)了解环境变量函数env()

说明: 环境变量函数env()规范的制定和兴起是由于iPhone X这类带有刘海屏和底部触摸条的移动设备的出现,如果按钮和底部触摸条在一起显示,就会出现交互冲突的问题,而env()函数可以让网页内容显示在设备的安全区域范围

/* 这里设置的值都是浏览器自己定义的属性名称,可以理解为CSS环境变量值 */
/* 直接使用4个安全内边距值 */
env(safe-area-inset-top);
env(safe-area-inset-right);
env(safe-area-inset-bottom);
env(safe-area-inset-left);

/* 使用4个安全内边距值,同时设置兜底尺寸值 */
env(safe-area-inset-top, 20px);
env(safe-area-inset-right, 1em);
env(safe-area-inset-bottom, 0.5vh);
env(safe-area-inset-left, 1.4rem);

image.png

注意:

  • env()函数中的属性是区分大小写的

  • 要想使safe-area-inset-*属性表现出准确的间距,一定要确保viewport相关的信息是下面这样的

<meta name="viewport" content="viewport-fit=cover">

(3)rem和vw单位与移动端适配

说明: 不同的手机其屏幕大小会不相同,对于布局而言,可以使用弹性布局或者网格布局来解决自适应的问题,但是对于文字来说却无能为力,比如16px大小的文字在375px宽的屏幕下显示正合适,但是在414px宽的屏幕下就会显得偏小,阅读体验不太好,此时可能会想到使用rem单位来解决,这是一个相对单位,它相对于根元素的font-size来确定的,所以默认1rem=16px,然后在不同宽度的设备下设置不同的字体大小或者在头部嵌入一段JavaScript代码,根据屏幕尺寸设置对应的根字号大小来解决这个问题,表面上问题是会解决,但是日后想要推翻之前的适配策略则改动巨大,举步维艰,这时可以使用视区相对单位vw来进行优化

==> 了解视区相对单位 <==

说明: 这类单位是相对于浏览器而言的,它有以下4个,以vw为例,假设一块屏幕的视区宽度是1024px,然后这个宽度会被分成100份,每一份就是1vw,高度也是同理,从而vw单位和设备的宽度相关联,因此,在遇到需要根据设备宽度进行弹性根字号设置的需求时,就可以使用vw单位了

  • vw: 视区宽度百分值
  • vh: 视区高度百分值
  • vmin: vw或vh,取小的那个值
  • vmax: vw或vh,取大的那个值

vh单位的经典应用: 当内容高度不足一屏时,让底部栏贴在浏览器窗口的底部;当内容高度超过一屏时,让底部栏贴在页面最下方

.container {
    display: flex;
    flex-direction: column;
    /* 容器默认有一个视区的高度,因为只需要移动底部栏的位置就好了 */
    min-height: 100vh;
}

footer {
    margin-top: auto;
}
<div class="container">
    <content></content>
    <footer></footer>
</div>

==> calc()函数实践 <==

说明: 就是使用vw和cale()函数来实现基于设备宽度的移动端布局适配方案,那么此时就需要设置跟字体的大小了

举例: 375px~414px的宽度区间的根字号大小是16px~ 18px

html {
    font-size: 16px;
}

@media screen and (min-width: 375px) {
    html {
        /* 这个式子需要自己设计 */
        /* 375px宽度使用16px基准尺寸,414px宽度时根字号大小正好是18px */
        font-size: calc(16px + 2 * (100vw - 375px) / 39);
    }
}

@media screen and (min-width: 414px) {
    html {
        font-size: 18px;
    }
}

解释: 然后通过计算发现375px~414px的宽度区间的根字号大小确实是16px~ 18px,此时根字体的大小就解决了,之后就使用rem做为单位进行布局就好了

常用移动端适配代码:

html {
    font-size: 16px;
}

@media screen and (min-width: 375px) {
    html {
        /* 375px作为16px基准,414px宽度时正好对应18px的根字号大小 */
        font-size: calc(16px + 2 * (100vw - 375px) / 39);
    }
}

@media screen and (min-width: 414px) {
    html {
        /* 屏幕宽度从414px到1000px,根字号大小累积增加4px(18px-22px)
        */
        font-size: calc(18px + 4 * (100vw - 414px) / 586);
    }
}

@media screen and (min-width: 1000px) {
    html {
        /* 屏幕宽度从1000px往后每增加100px,根字号大小就增加0.5px */
        font-size: calc(22px + 5 * (100vw - 1000px) / 1000);
    }
}

注意: rem的布局并不是万能的,比如非整数尺寸偶尔会带来一些渲染的问题,当SVG图标尺寸不是整数的时候,边缘可能会出现奇怪的间隙等,在这些场景下,可以将对应元素的rem单位改成px单位进行表示

(4)touch-action属性

说明: 这个属性是移动端中与手势触摸密切相关的CSS属性

==> 常见应用 <==

取消300ms的点击延时: touch-action:manipulation表示浏览器只允许进行滚动和持续缩放操作,所以类似双击缩放这种非标准操作就不被允许,而click事件在移动端有300ms延时的原因是避免点击行为和手机双击行为发生冲突,在设置这个属性之后,取消了双击行为,从而300ms延迟也就不存在了

/* 避免点击后浏览器延时300ms的问题 */
html { 
    touch-action: manipulation;
}

解决treated as passive错误: 在页面上执行下面这段JS,在移动端滑动页面的时候就会报错,此时就可以使用这个属性来解决这个问题

document.addEventListener('touchmove', function (event) {
    event.preventDefault(); 
});

image.png

<div id="result" class="for-height"></div>
html {
    touch-action: none;
    overflow: hidden;
}

body {
    touch-action: auto;
    height: 100vh;
    position: relative;
    overflow: auto;
}

.for-height {
    width: 150px;
    min-height: 600px;
    background-color: #eee;
    margin: auto;
}

==> 了解属性值的含义 <==

说明: 它支持以下属性值

touch-action: auto;
touch-action: manipulation;
touch-action: none;
touch-action: pan-x;
touch-action: pan-y;
touch-action: pan-left;
touch-action: pan-right;
touch-action: pan-up;
touch-action: pan-down;
touch-action: pinch-zoom;
  • auto: 默认值,表示手势操作完全由浏览器决定(如<meta>元素的viewport属性通过设置user-scalable=no/yes来确定是否允许缩放)

  • manipulation: 表示浏览器只允许进行滚动和持续缩放操作,类似双击缩放这种非标准操作就不可以。此属性值可以用来解决点击后延时300ms的问题

  • none: 表示不进行任何手势相关的行为,例如,你想用手指滚动网页就不行,双击放大或缩小页面也不可以,所有这些行为都要自定义

  • pan-x: 表示支持手指头水平移来移去的操作

  • pan-y: 表示支持手指头垂直移来移去的操作

  • pan-left: 表示支持手指头往左移动,移动开始后往右可以恢复的操作

  • pan-right: 表示支持手指头往右移动,移动开始后往左可以恢复的操作

  • pan-up: 表示支持手指头往上移动,移动开始后往下可以恢复的操作

  • pan-down: 表示支持手指头往下移动,移动开始后往上可以恢复的操作

  • pinch-zoom: 表示支持手指头缩放页面的操作

(5)image-set()函数

说明: 它可以根据不同设备的屏幕密度或者分辨率来显示不同的背景图(background-image)或者遮罩图片(mask-image)等

.example { 
    /* 如果屏幕是1倍屏,也就是设备像素比是 1∶1的话,就使用1.jpg作为背景图; */
    /* 如果屏幕是2倍屏及以上,就使用 1-2x.jpg作为背景图; */
    /* 如果设备的分辨率大于600dpi,就使用1-print.jpg 作为背景图 */
    background-image: image-set(url(1.jpg) 1x, url(1-2x.jpg) 2x, url(1-print.jpg) 600dpi); 
}

注意: 图片地址需要写在url()函数里,url()函数中不用添加引号,此外,dpi表示每英寸点数。通常屏幕每英寸包含72点或96点,打印文档的dpi要大得多,一般dpi值在600以上,就可以认为是打印设备了。最后,1x、2x中的x其实是dppx的别称,表示每像素单位的点数,也可以理解为屏幕密度。

适用场景:

  • 不同屏幕密度下显示的是完全不同的图,而不是只有尺寸不一样的图。例如,在1倍屏下显示造型简单的图标,在多倍屏下显示细节丰富的图标

  • 用户体验和流量收益足够明显的场景。例如类似WeChat这种用户基数很大的产品;或者给流量费用较高、信号较差的地区开发的产品

八、变量函数var()和自定义属性

(1)CSS变量的语法

说明: CSS变量的语法由两部分组成,一部分是CSS变量的声明,另一部分是CSS变量的使用。其中,CSS变量的声明由CSS自定义属性及其对应的值组成,而CSS变量的使用则通过变量函数var()调用CSS自定义属性实现

:root {
    /* --primary-color表示CSS自定义属性名 */
    /* deepskyblue表示CSS自定义属性值 */
    /* --primary-color: deepskyblue表示CSS变量的声明 */
    --primary-color: deepskyblue;
}

button {
    /* var(--primary-color)表示CSS变量的使用 */
    background-color: var(--primary-color);
}

==> CSS自定义属性的命名 <==

说明: CSS自定义属性值支持数字、短横线、空格、CJK文字来进行命名,但是对于特殊字符就需要使用斜杠进行转义了

body {
    /* 支持数字 */
    --1: #369;
    background-color: var(--1);
}

body {
    /*支持短横线 */
    ---: #369;
    background-color: var(---);
    /* 支持空格 */
    --: deepskyblue;
    color: var(--);
}

body {
    /* 支持中文 */
    --深蓝: #369;
    background-color: var(--深蓝);
}

:root {
    /* 特殊字符需要转义 */
    --\$: deepskyblue;
    color: var(--\$);
}

==> var()函数的语法 <==

完整语法:

/* <custom-property-name>指的就是自定义属性名  */
/* <declaration-value>指的是声明值,也就是备选值,当自定义属性名一定无效的时候使用 */
var( <custom-property-name> [, <declaration-value> ]? )

举例:

<p>背景颜色是?</p>
/* 由于这个属性名没有定义,因此无效,此时p元素的背景色渲染为deepskyblue */
p {
    background-color: var(--any-what, deepskyblue);
}

注意: 备选CSS自定义属性值只在前面的自定义属性一定无效的时候才渲染,如果var()函数的第一个参数值可能有效,则后备CSS自定义属性值是不会渲染的,还可能会产生一些有趣现象

<== 参数无效 ==>

说明: 如果var()函数的第一个参数值是不合法的,则var()函数解析为当前CSS属性的初始值或继承值(如果有继承性),也就是按照unset全局关键字的规则渲染,但是不等同于直接设置unset关键字,从而可以利用这种特性让CSS变量同时开启和关闭一个或多个不同的属性值

/* 此时background-color的值是transparent */
body {
    --color: 20px;
    background-color: deeppink;
    background-color: var(--color, deepskyblue);
}

举例:

button {
    /* 这里的空格很重要 */
    --open: ;
    color: #2a80eb;
    -webkit-text-fill-color: #fff;
    border-radius: 4px;
    padding: 9px 20px;
    border: 1px solid var(--open, rgba(0, 0, 0, 0.1));
    box-shadow: var(--open, inset 0 1px 2px rgba(0, 0, 0, 0.1));
    background: var(--open, linear-gradient(#0003, transparent))
    currentColor;
    text-shadow: var(--open, -1px -1px #0003);
    transition: 0.15s;
}

button:active {
    /* 任意全局关键字都可以 */
    --open: inherit;
}
<button>点击我</button>

image.png

image.png

解释: --open: ;前面有一个空格,也就是-- open属性值是一个空格,在语法上是可能有效的。但是空格对于box-shadow和background等CSS属性是无效的,因此,box-shadow和background等CSS属性均解析为初始值,也就是默认按钮的表现,之后点击按钮触发:active伪类后,会运行--open: inherit这个CSS声明,全局CSS关键字作为CSS自定义属性值一定无效,因此var()函数会使用后备CSS属性值进行渲染

<== 空格尾随特性 ==>

举例说明:

html {
    font-size: 14px;
}

body {
    --size: 20;
    font-size: 16px;
    /* 首先var()函数语法上没问题,那么使用起来就会覆盖上面的16px */
    /* 注意这里多了一个空格,所以是不合法的font-size,因此会使用 */
    /* 继承的font-size的大小,因此这里的大小是14px */
    font-size: var(--size) px;
}

==> CSS自定义属性的作用域 <==

举例说明:

body {
    /* CSS自定义属性没有在body中生效,因为box元素是body元素的子元素 */
    /* 但是在body中的CSS自定义属性在box元素中是可以生效的 */
    background-color: var(--color);
}

.box {
    --color: deepskyblue;
}
<body>
    <div class="box"></div>
</body>

说明: 从而全局使用的自定义属性都设置在:root伪类中,因为这样可以保证所有页面和任意标签元素都能使用这个自定义属性

<== 本质是继承 ==>

说明: 在HTML文档树中,后代元素可以原封不动地继承祖先元素设置的CSS自定义属性值

<== 继承Shadow DOM中的元素 ==>

举例: 使用CSS自定义属性直接控制Shadow DOM样式

/* 借助CSS自定义属性穿透Shadow DOM中的CSS作用域限制,从而改变其样式 */
[type="parmary"] {
    --ui-button-border: 1px solid transparent;
    --ui-button-background: deepskyblue;
    --ui-button-color: #fff;
}
<!-- 自定义一个<ui-button>类型的按钮 -->
<h4>默认Shadow DOM创建的按钮</h4>
<ui-button>按钮</ui-button>

<!-- 为了保持按钮原本无障碍访问的特性,使用Shadow DOM插入一个原生的<button>按钮 -->
<h4>外部CSS改变Shadow DOM创建的按钮样式</h4>
<ui-button type="parmary">按钮</ui-button>
[].slice
  .call(document.querySelectorAll("ui-button"))
  .forEach(function (ele) {
      var shadow = ele.attachShadow({ mode: "closed" });
      shadow.innerHTML = `<style>
      button {
          padding: 9px 1em;
          border: var(--ui-button-border, 1px solid #ccc);
          border-radius: var(--ui-button-radius, 4px);
          background-color: var(--ui-button-background, #fff);
          color:  var(--ui-button-color, #333);
      }
      </style>
      <button>${ele.textContent}</button>`;
  });

image.png

==> CSS自定义属性值的细节知识 <==

<== 可以是任意值或表达式 ==>

举例:

.example {
    /* 渐变语法中to top可以使用CSS自定义属性表示 */
    --direction: to top;
    background: linear-gradient(var(--direction), deeppink, deepskyblue);
}

.example {
    /* 单个色值可以使用CSS自定义属性表示 */
    --fromColor: deeppink;
    --toColor: deepskyblue;
    background: linear-gradient(to top, var(--fromColor), var(-- toColor));
}

.example {
    /* 起止色值也可以使用CSS自定义属性表示 */
    --gradientColor: deeppink, deepskyblue;
    background: linear-gradient(to top, var(--gradientColor));
}

.example {
    /* 整个渐变函数的参数值也可以使用CSS自定义属性表示 */
    --gradient: to top, deeppink, deepskyblue;
    background: linear-gradient(var(--gradient));
}

.example {
    /* 整个渐变函数的表达式同样可以使用CSS自定义属性表示 */
    --linear-gradient: linear-gradient(to top, deeppink, deepskyblue);
    background: var(--linear-gradient);
}

<== 可以相互传递 ==>

举例:

body {
    /* 在定义CSS自定义属性的时候,可以直接引入CSS自定义属性 */
    --green: #4caf50;
    --successColor: var(--green);
}

body {
    /* CSS自定义属性还可以用在calc()函数中 */
    --columns: 4;
    --margins: calc(24px / var(--columns));
}

<== 不能自身赋值 ==>

:root {
    --primary-color: deepskyblue;
}

.some-class {
    --primary-color: var(--primary-color, #2a80eb);
    /* --primary-color会被认为是非法的,color的颜色为当前上下文的颜色 */
    color: var(--primary-color);
}

<== 媒体查询中不支持 ==>

举例:

:root {
    --maxWidth: 640px;
}

/* 不合法,语法无效 */
@media (max-width: var(--maxWidth)) {
}

(2)了解CSS自定义属性的设置与获取

==> HTML标签中设置 <==

说明: 在HTML标签中设置CSS自定义属性的方法和在HTML标签中设置普通的CSS属性的方法是一样的,直接将要设置的属性写在style属性中即可

<div style="--color: deepskyblue">
    <img src="1.jpg" style="border: 10px solid var(--color)" />
</div>

==> JavaScript中的设置和获取 <==

说明: 设置需要使用setProperty()方法,获取需要使用getPropertyValue()方法

<div id="box">
    <img src="1.jpg" style="border: 10px solid var(--color)" />
</div>
/* 设置 */
box.style.setProperty('--color', 'deepskyblue');

/* 获取 */
getComputedStyle(box).getPropertyValue('-- color');

(3)使用content属性显示CSS自定义属性

说明: 有时候需要让CSS变量中的自定义属性值能够同时作为字符内容在页面中呈现,此时可以借助是CSS计数器呈现CSS自定义属性值,虽然content属性本身不支持CSS自定义属性值,但是counter-reset属性后面的计数器初始值是支持的

举例: 进度条效果的实现

<label>图片1:</label>
<div class="bar" style="--percent: 60"></div>

<label>图片2:</label>
<div class="bar" style="--percent: 40"></div>

<label>图片3:</label>
<div class="bar" style="--percent: 20"></div>
.bar {
    height: 20px;
    width: 300px;
    background-color: #f2f2f2;
}

.bar::before {
    /* 进度值信息显示 */
    counter-reset: progress var(--percent);
    /* \2002表示空格,为了让进度值文字内容和进度条右侧边缘之间有一定的距离 */
    content: counter(progress) "%\2002";
    display: block;
    /* 宽度的设置 */
    width: calc(100% * var(--percent) / 100);
    font-size: 12px;
    color: #fff;
    background-color: #2486ff;
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
}

image.png

九、文本字符处理的升级版本

(1)文字的美化与装饰

==> 文字阴影text-shadow <==

说明: text-shadow是文字阴影,box-shadow是盒阴影,在语法上存在以下的区别,除了这些区别,其它的都是一样的

  • text-shadow不支持inset关键字,也就是text-shadow只有外阴影,没有内阴影

  • text-shadow不支持阴影扩展,也就是text-shadow最多支持3个数值,分别表示水平偏移、垂直偏移和模糊大小,而box-shadow属性最多支持4个数值,多出来的哪一个表示阴影扩展

举例:

.text-shadow-3d {
font-size: 60px;
color: deepskyblue;
text-shadow: 1px 1px #005a79,
             2px 2px #005a79,
             3px 3px #005a79,
             4px 4px #005a79,
             5px 5px #005a79,
             6px 6px #005a79,
             7px 7px #005a79,
             8px 8px #005a79;
}

.text-stroke-out {
    font-size: 60px;
    color: #fff;
    /* 如果描边宽度只有1px,则只需要4个方向的偏移,因为这里描边宽度为2px,所以使用了8个方向的偏移 */
    text-shadow: 0 2px deeppink,
                 2px 0 deeppink,
                 0 -2px deeppink,
                -2px 0 deeppink,
                2px 2px deeppink,
                -2px -2px deeppink,
                -2px 2px deeppink,
                2px -2px deeppink;
}
<span class="text-shadow-3d">立体投影</span>
<p class="text-stroke-out">外描边</p>

image.png

==> 文字描边text-stroke <==

<== 语法 ==>

说明: text-stroke属性是text-stroke-width和text-stroke-color这两个CSS属性的缩写,分别表示文字描边的宽度和文字描边的颜色。有别于border属性,text-stroke属性无法指定文字描边 的类型,只支持实线描边,不支持点线或者虚线描边,也无法指定描边是外描边还是内描边或居中描边

/* 这两种写法是等价的 */
.stroke { 
    -webkit-text-stroke: 2px red;
}

.stroke { 
    -webkit-text-stroke-width: 2px;
    -webkit-text-stroke-color: red;
}

注意: text-stroke-width属性的宽度默认值是0,因此使用text-stroke属性的时候,是一定要设置描边的宽度值的;其次描边的颜色默认是color属性的计算值,和文字自身的颜色一致,因此,如果不设置描边的颜色,最终的样式表现并不是描边,而是文字加粗效果

<p>加粗文本</p>
<p class="stroke">加粗文本</p>
.stroke {
    font-size: 40px;
    -webkit-text-stroke-width: 2px;
}

image.png

<== 居中描边 ==>

说明: 居中描边是这个属性使用频率很少的主要原因,因为文字的笔画粗细都是1px~2px,如果此时再设置一个居中描边效果,文字原本的颜色几乎都会被描边颜色覆盖,效果会很糟糕

<p>没有描边</p>
<p class="stroke">有描边</p>
.stroke {
    font-size: 40px;
    -webkit-text-stroke: 2px red;
}

image.png

举例: 不过使用这一特性可以模拟文字外描边的效果

<span class="text-stroke-out" data-content="外描边">外描边</span>
/* 设置两层文字,下层的文字有描边,上层的文字没有描边 */
.text-stroke-out {
    font-size: 60px;
    -webkit-text-stroke: 4px deeppink;
    letter-spacing: 4px;
}

[data-content]::before {
    content: attr(data-content);
    position: absolute;
    -webkit-text-stroke: 0;
    color: deepskyblue;
}

image.png

==> 颜色填充text-fill-color <==

说明: 可以对文字进行颜色填充,还可以覆盖color属性设置的颜色,注意,只是覆盖color的渲染表现,实际上元素的颜色计算值还是由color属性决定的

-webkit-text-fill-color: transparent; 
-webkit-text-fill-color: deepskyblue;
-webkit-text-fill-color: #228bff;
-webkit-text-fill-color: rgba(100, 200, 0, .6);

注意: 这个属性还可以在改变文字颜色的同时保护color属性,至于color属性为啥需要保护,有以下两个原因,可以通过下面的两个例子理解一下

  • color属性具有继承性,可以通过改变祖先元素的color值改变子元素的样式,方便维护与管理

  • CSS中很多事物的默认颜色都是由color属性决定的,例如输入框中光标颜色、边框色、盒阴影颜色和文字阴影颜色等。此时,如果希望光标颜色和文字颜色不一样,或者希望在CSS文字阴影语法上省略色值的同时让阴影颜色和文字颜色不一样,则文字颜色就不能使用color属性实现

<== 换肤效果 ==>

<section class="module">
    <h4>模块标题</h4>
    <content>
        <ul>
            <li>文字描述</li>
            <li>文字描述</li>
            <li>文字描述</li>
        </ul>
        <button>了解更多</button>
    </content>
</section>

<section class="module" data-theme="a">
    <h4>模块标题</h4>
    <content>
        <ul>
            <li>文字描述</li>
            <li>文字描述</li>
            <li>文字描述</li>
        </ul>
        <button>了解更多</button>
    </content>
</section>
.module {
    width: 300px;
    border: 1px solid;
}

.module h4 {
    margin: 0;
    padding: 5px 10px;
    -webkit-text-fill-color: #fff;
    background-color: currentColor;
}

.module button {
    border: 0;
    width: 100px;
    height: 32px;
    /* 覆盖按钮自身的颜色,但是color的值没变 */
    -webkit-text-fill-color: #fff;
    /* 继承父元素的颜色,便于统一的更改 */
    color: inherit;
    /* 继承主题色 */
    background-color: currentColor;
    margin-top: 10px;
}

.module content {
    display: block;
    padding: 10px;
}

.module ul {
    color: #333;
}

/* 主题颜色设置 */
[data-theme="a"] {
    color: deepskyblue;
}

image.png

<== 简化text-shadow ==>

举例: 以上面立体投影为例,颜色出现了多次,这并不便于管理和维护,此时可以使用text-fill-color属性进行优化

.text-shadow-3d {
    font-size: 60px;
    /* 这个负责文字颜色 */
    -webkit-text-fill-color: deepskyblue;
    /* 这个负责投影颜色,因为字体颜色会被覆盖 */
    color: #005A79;
    text-shadow: 1px 1px,
                 2px 2px,
                 3px 3px,
                 4px 4px,
                 5px 5px,
                 6px 6px,
                 7px 7px,
                 8px 8px;
}

==> 强调装饰text-emphasis <==

<== text-emphasis-color ==>

说明: 它用来设置强调的字符的颜色,初始值就是当前文字的颜色

<== text-emphasis-style ==>

说明: 用于指定强调字符的类型是啥

语法:

/* 默认声明,表示没有任何强调 */
text-emphasis-style: none

/* 实心(默认选择)、空心 | 点、圆、双层圆、三角、芝麻点 */
text-emphasis-style: [ filled | open ] || [ dot | circle | double-circle | triangle | sesame ]

/* 表示使用任意单个字符作为强调装饰符 */
text-emphasis-style: <string>

举例:

<span class="emphasis">我爱你</span>
.emphasis {
    -webkit-text-emphasis-style: "❤";
    text-emphasis-style: "❤";
}

image.png

注意:

  • 显示的强调装饰符的字号大小是主文字内容字号大小的一半,例如文字的大小是16px,则上方的强调字符的大小则是8px。因此,在文字字号不是很大的时候,尽量不要使用造型复杂且字符区域较小的字符,如星号(*)和井号(#)等符号会在普通的显示设备中缩成一团,用户完全看不出来是什么字符

  • 如果行高不是很高,则强调装饰符会自动增加当前这一行所占据的高度

  • 强调装饰符和正文之间的距离是无法通过设置行高等属性进 行调节的,距离的大小主要由字体决定

  • 如果指定的是多个字符,则只会使用第一个字符作为强调装饰符

<== text-emphasis-position ==>

说明: 用来指定强调装饰符的位置,默认位置是在文字的上方

语法:

/* 初始值:over right */
/* 表示文字横向排版装饰符在文字上面,竖向排版在文字右侧 */
text-emphasis-position: [ over | under ] && [ right | left ]

举例:

.chinese-emphasis {
    -webkit-text-emphasis: dot;
    text-emphasis: dot;
    -webkit-text-emphasis-position: under right;
    text-emphasis-position: under right;
}
<p class="chinese-emphasis">中文强调</p>

image.png

<== text-emphasis-position ==>

说明: text-emphasis是text-emphasis-color和text-emphasis-style这两个CSS属性的缩写

注意: text-emphasis是一个继承属性,也就是祖先元素设置了强调效果后,子元素也会应用。这一点就和text-decoration属性完全不同,text-decoration属性是没有继承性的,此外,text-emphasis属性会影响文字占据空间的高度,而text- decoration属性不会

(2)文字的旋转与阅读

==> 文字方向text-orientation <==

<p>这是一本与CSS有关的书籍</p>
p {
    writing-mode: vertical-rl;
}

image.png

说明: 竖向排版中的英文字符都是以顺时针旋转90°的方式呈现的,比如上面那句话,但是有些时候需要英文字符在旋转之后还是正着显示的,比如"A和C"这句话,其正立显示的时候最利于阅读,此时就可以使用text-orientation属性,它可以设置竖版排列时中文和英文的方向

语法:

text-orientation: mixed | upright | sideways
  • mixed: 默认值,表示中文和英文的文字显示方向是不一致的,中文字符是正立的,而英文字符则顺时针旋转90度后显示

  • upright: 表示中文和英文的文字显示方向都是默认的正立显示,没有旋转

  • sideways: 表示中文和英文的文字显示方向都是顺时针旋转90度显示

image.png

==> 横向合并text-combine-upright <==

作用: 可以让2~4个字符横向合并显示

.upright {
    writing-mode: vertical-rl;
}

.upright span {
    -ms-text-combine-horizontal: all;
    -webkit-text-combine: horizontal;
    text-combine-upright: all;
}
<p class="upright">
    <span>CSS</span>
    相关书籍
</p>

image.png

语法:

text-combine-upright: none | all | digits <integer>?
  • none: 默认值,表示字符不会参与横向合并

  • all: 表示所有类型的字符都会参与横向合并,不过一个标签内最多只能合并4个字符

  • digits <integer>?: 表示仅数字字符参与横向合并,这种语法多用在日期的垂直排版中,合并字符的数量范围为2-4

注意: 这种合并会让2-4个字符全部在一个字符宽度中,而普通的水平排版排列后的字符所占据的宽度是每个字符累加的宽度,而不是单个字符的宽度

==> unicode-bidi的属性值 <==

说明: 这里的属性值有六个,分别是传统属性值normal、embed、bidi-override和新属性值plaintext、isolate、isolate-override,这几个是一一对应的,其中有以下几点需要注意

  • 属性值isolate和embed的作用都是让中文字符和英文字符从左往右排列,让问号和加号等字符从右往左排列

  • 属性值isolate-override和bidi-override的作用都是让所有字符从右往左排列

  • 新属性值的不同之处就在于,isolate和isolate-override会让元素(即使是内联元素)作为独立的个体参与到兄弟元素之间的方位排列,其算法表现就如同图片、按钮元素,或者问号、加号等字符

span {
    background-color: skyblue;
}

.embed {
    unicode-bidi: embed;
}

.bidi-override {
    unicode-bidi: bidi-override;
}

.plaintext {
    unicode-bidi: plaintext;
}

.isolate {
    unicode-bidi: isolate;
}

.isolate-override {
    unicode-bidi: isolate-override;
}
<h4>正常direction</h4>
<p><button>button按钮?</button><span>span标签?</span>匿名内联元素?</p>

<div style="display: flex">
    <div>
        <h4>normal,默认</h4>
        <p dir="rtl">
            <button>button按钮?</button><span>span标签?</span>匿名内联元素?
        </p>

        <h4>embed</h4>
        <p dir="rtl">
            <button class="embed">button按钮?</button
            ><span class="embed">span标签?</span>匿名内联元素?
        </p>

        <h4>bidi-override</h4>
        <p dir="rtl">
            <button class="bidi-override">button按钮?</button
            ><span class="bidi-override">span标签?</span>匿名内联元素?
        </p>
    </div>

    <div>
        <h4>plaintext</h4>
        <p dir="rtl">
            <button class="plaintext">button按钮?</button
            ><span class="plaintext">span标签?</span>匿名内联元素?
        </p>

        <h4>isolate</h4>
        <p dir="rtl">
            <button class="isolate">button按钮?</button
            ><span class="isolate">span标签?</span>匿名内联元素?
        </p>

        <h4>isolate-override</h4>
        <p dir="rtl">
            <button class="isolate-override">button按钮?</button
            ><span class="isolate-override">span标签?</span>匿名内联元素?
        </p>
    </div>
</div>

image.png

注意: plaintext属性值可以在不改变当前文档的水平流向的前提下,让所有字符按照默认的从左往右的流向排列

(3)文本字符尺寸控制

==> text-size-adjust属性 <==

说明: 在iPhone的微信App或者原生的Safari浏览器中,在默认情况下,当手机从竖屏变成横屏的时候,由于页面变宽了,为了让文字阅读更轻松,iPhone会使用算法让文字变大,但是这种变大只是部分文字放大,因此带来了体验问题,因此真实项目中需要禁止这种字号自动调整的行为,由于iPhone这种横屏时字号自动调整的行为就是text-size-adjust属性决定的,所以我们只需要在全局设置text-size-adjust属性值为none即可

body { 
    -webkit-text-size-adjust: none;
}

==> 新单位ch <==

说明: ch与em、rem和ex一样,是CSS中为数不多和字符相关的相对单位。与单位em和rem相关的字符是m,与ex相关的字符是x,和ch相关的字符则是0,就是阿拉伯数字0。1ch表示1个0字符的宽度,由于字符的宽度不仅受字体影响,加粗和倾斜效果也会影响字符的宽度,因此,ch的尺寸是不固定的,在不同的浏览器和不同的操作系统中所占据的尺寸是不一样的。所以,ch单位并不适用于需要精确尺寸的场景,主要是为了英文阅读设计的

/* 比如对于中文,可以使用em进行宽度的指定 */
article {
    /* 一行的汉字是42个字 */
    max-width: 42em;
}

/* 每行60~100个字符 */
article { 
    /* 近似值,因为字符i和字符m的宽度都和0不一样,除非使用的是等宽字体 */
    max-width: 68ch;
}

注意: 如果想要使用ch单位精准限制字符个数,一定要在等宽字体的场景下使用

适用场景: 在尺寸与字符相关且对具体的尺寸值要求不严格的情况下,就可以使用ch单位

<== 场景举例 ==>

场景1: 4位验证码字符的验证码输入框

input { 
    /* 预留2ch的安全距离 */
    width: 6ch;
}

场景2: 按钮元素左右预留内边距

button { 
    padding: 0 1ch;
}

==> 代码缩进tab-size <==

作用: 可以控制Tab键输入的空格的长度大小

语法:

tab-size: <integer> | <length>
  • <integer>: 整数值,表示Tab键输入的空格的宽度等于几个Space键输入的空格的宽度,常用

  • <length>: 长度值,表示每个Tab键输入的空格的宽度值,用到的概率为0

应用场景: pre元素中一个tab键的缩进等于8个空格的缩进,而在编辑器中则是4个空格,这种数量不统一会产生代码在展示的过程中出现过度缩进的效果,此时就可以使用tab-size属性来解决

(4)文字渲染与字体呈现

==> text-rendering属性 <==

作用: 告诉浏览器,对于文字内容的渲染,是速度优先、可读性优先还是几何精度优先,不过浏览器会使用最佳的属性值

语法:

text-rendering: optimizeSpeed | optimizeLegibility | geometricPrecision | auto;
  • optimizeSpeed: 表示浏览器渲染文本的时候是速度优先,这个属性值会禁用文字的自动字距调整和字符相连特性

  • optimizeLegibility: 表示浏览器渲染文本的时候是可读性优先,这个属性值会启用文字的自动字距调整和字符相连特性

  • geometricPrecision: 表示浏览器渲染文本的时候是几何精度优先,也就是可以精确到小数的意思

  • auto: 表示浏览器自己判断文字渲染时是速度优先、可读性优先还是几何精度优先

==> 字符胖瘦font-stretch <==

说明: 由于常见的中文字体均没有包含多个字体面(字体面可以理解为不同的状态,就好比人拍照摆pose一样),因此它主要用来设置英文字体的字形缩放

语法:

/* 收缩50% */
font-stretch: ultra-condensed;
/* 收缩62.5% */
font-stretch: extra-condensed;
/* 收缩75% */
font-stretch: condensed;
/* 收缩87.5% */
font-stretch: semi-condensed;
/* 初始值,也就是正常字体的表现 */
font-stretch: normal;
/* 拉伸112.5% */
font-stretch: semi-expanded;
/* 拉伸125% */
font-stretch: expanded;
/* 拉伸150% */
font-stretch: extra-expanded;
/* 拉伸200% */
font-stretch: ultra-expanded;

font-stretch: 50%;
font-stretch: 100%;
font-stretch: 200%;

注意: 虽然font-stretch属性支持多达9个不同拉伸程度的关键字属性值,但是很多字体是没有这么多的字体面的,有一些经典的英文字体也仅仅包含一个窄面和一个宽面而已。这个时候,所有font-stretch属性的百分比计算值小于100%的文字会被渲染为窄面,所有font-stretch属性的百分比计算值大于100%的文字会被渲染为宽面;如果希望它的每一个关键字属性都能显示对应的拉伸效果,就只能通过自定义字体来实现了

==> 中文增强font-synthesis <==

作用: 可以让英文依然保持倾斜,而中文永远是最佳的非倾斜状态

语法:

/* 粗体和斜体都不需要合成 */
font-synthesis: none;

/* 如果需要,可以合成粗体字体 */
font-synthesis: weight;

/* 如果需要,可以合成斜体字体 */
font-synthesis: style;

/* 默认值,表示就算字体中没有对应的粗体和斜体,也会通过字形变化 */
/* 合成粗体效果和斜体效果,这对于CJK文字而言,简直糟糕透了 */
font-synthesis: weight style;

说明: 因此,在一段中英文混合的文字中,如果希望英文字符倾斜而中文字符不倾斜,可以设置font-synthesis:none

注意: 不过这个属性Chrome浏览器是不支持的,而Firefox浏览器和Safari浏览器都支持该属性

(5)字体特征和变体

说明: 字体特征和变体指OpenType字体中包含的不同字形或字符样式(指连字、字距、分数、数字样式和其它一些字符),其中的OpenType字体是Adobe和微软联合开发的一种字体,字体文件的原始后缀可能是.otf、.otc、.ttf或.ttc;其中每一种字体特征或变体都有一个对应的字符特征值,均采用4个字符形式进行表示

/* kern就是字符特征值,表示应用字体的间距特征 */
font-feature-settings: "kern" 1;

image.png

注意: 并不是所有字体都包含字体特征,也不存在某个字体包含所有字体特征,字体特征最终的表现效果取决于字体设计师而不是程序算法

==> 升级版的font-variant <==

说明: 在CSS2.1中这个属性的作用就是实现英文字母的小型大写效果,也就是字母是大写形式,但是尺寸比标准的大写字母要小一些,而在CSS3中,它升级成一堆属性的缩写形式,使其专门用来显示对应字体特征的变体效果,它让所有特征值全部使用完整的关键字属性值代替,这样在看到属性值时就知道使用的是字体的哪种变体了

语法:

font-variant: normal;
font-variant: none;
font-variant: font-variant-caps || font-variant-numeric ||
font-variant-alternates || font-variant-ligatures || font-variant-east-asian;

注意: 这个属性支持任意多个font-variant子属性值的叠加,且没有顺序要求

<== font-variant-caps ==>

说明: 它的作用跟CSS2.1中的font-variant的作用是一样的,此外它还多支持几个属性值,其语法如下

语法:

font-variant-caps: normal | small-caps | all-small-caps | petite-caps | all-petite-caps | unicase | titling-caps
  • normal: 表示字符的大小写由其他CSS属性决定,例如text-transform属性

  • small-caps: 表示小型大写字母,对应的OpenType特征值是smcp

  • all-small-caps: 表示无论是大写字母还是小写字母,全部都变成小型大写字母,对应的OpenType特征值是c2sc和smcp

  • petite-caps: 表示特小型大写字母,对应的OpenType特征值是pcap。特小型大写字母和小型大写字母的区别在于两者与字母x高度的对比,一般来说,小型大写字母的高度等于字母x的高度,也就是和大部分小写字母的高度一样,都是1ex。但是在部分字体中,小型大写字母会比字母x略高一些,如Tiro Typeworks设计的一些字体中的小型大写字母比字母x高30%。于是,和字母x高度一致的字体形式就称为特小型大写字母,而比字母x高的字体形式则称为小型大写字母。在常规字体下,petite-caps属性值和small-caps属性值的表现一致,都是浏览器通过缩小大写字母的尺寸而模拟出来的小型大写字母的效果。实际上,真正设计过的小型大写字母效果在笔画上是被优化过的,其保持了更合适的纵横比以保证可读性

  • all-petite-caps: 表示无论是大写字母还是小写字母,全部都变成特小型大写字母,对应的OpenType特征值是c2pc和pcap

  • unicase: 是一种混合模式,可以有小型大写字母、大写字母或大型小写字母。对应的OpenType特征值是unic。Windows操作系统下的Arial字体就包含unic特征值

  • titling-caps: 表示使目标字符显示为标题大写字母,对应的OpenType特征值是titl。标题大写字母是专门为标题设计的大写字母的变体,通过减小笔画宽度避免全大写的标题的表现过于强烈

<== font-variant-numeric ==>

作用: 用来设置数字的变体效果

语法:

/* 支持两种书写形式,一种是初始值normal,一种是五类关键字随机组合 */
font-variant-numeric: normal;
font-variant-numeric: [ lining-nums | oldstyle-nums] || [ proportional-nums | tabular-nums] || [ diagonal-fractions | stacked-fractions] || ordinal || slashed-zero;
  • normal: 表示使用正常的数字效果,不使用变体字形

  • ordinal: 表示强制使用序数标记特殊的标志符号,它对应的OpenType特征值是ordn,操作系统中常规的英文字体并没有包含此特征,需要使用专门设计过的字体才可以实现数字序列化的效果

  • slashed-zero: 表示强制使用带斜线的0。当需要明确 区分字母O和数字0时,此关键字非常有用。其对应的OpenType特征值是zero

  • lining-nums和oldstyle-nums: 用来控制数字的样式,其中,lining-nums表示数字沿着基线对齐,对应的OpenType特征值是lnum;oldstyle-nums表示数字采用传统对齐方式,如数字3、4、5、7、9会下沉,oldstyle-nums对应的OpenType特征值是onum

  • proportional-nums和tabular-nums: 用来控制数字的尺寸。其中,proportional-nums表示每个数字占据的宽度并不一致,宽度大小由字形大小决定,其对应的OpenType特征值是pnum,tabular-nums表示每个数字占据的宽度都是一样的,数字就好像被约束在宽度一致的表格中,其对应的OpenType特征值是tnum

  • diagonal-fractions和stacked-fractions: 用来控制分数的样式。其中,diagonal-fractions表示让分子和分母尺寸变小并将两者用斜线隔开,其对应的OpenType特征值是frac,而stacked-fractions表示让分子和分母尺寸变小并将两者使用水平线隔开,其对应的OpenType特征值是afrc

<== font-variant-alternates ==>

说明: 主要用来让字体发生变化,包括样式和风格的变化,以及字符集和字符的变化,从而让字体变得花哨,或者变成装饰字符、注释字符等。这些变化对应的OpenType参数往往包含序号数字,因此,在语法上,font-variant-alternates属性值以函数为主

语法:

font-variant-alternates: normal;
font-variant-alternates: stylistic() || historical-forms || styleset(#) || character-variant(#) || swash() || ornaments() || annotation();
  • historical-forms: 表示启用历史常用但现在不常用的字形,对应的OpenType特征值是hist

  • stylistic(): 函数允许对单个字符进行样式替换,其对应的OpenType特征值是salt

  • styleset(): 函数启用字符集的样式变化,其对应的OpenType特征值是从ss01到ss20

  • character-variant(): 函数启用字符的样式变化。该函数和styleset()函数类似,不同之处在于character-variant()函数下的单个字符的样式具有独立性,和一组其他字符显示的时候,不一定具有连贯的样式。character-variant()函数对应的OpenType value特征值是从cv01到cv99

  • swash(): 函数表示启用花式字形,例如夸张的衬线、端点、尾部、笔锋等,其对应的OpenType特征值是swsh和cswh

  • ornaments(): 函数启用装饰字形,其对应的OpenType特征值是ornm

  • annotation(): 函数启用注释字形,如带圆圈的数字或虚实反转的字符,其对应的OpenType特征值是nalt

<== font-variant-ligatures ==>

说明: 用来设置文字的连字变体,总共支持10个非全局关键字属性值

语法:

font-variant-ligatures: normal;
font-variant-ligatures: none;
font-variant-ligatures: [ common-ligatures | no-common-ligatures] || [ discretionary-ligatures | no-discretionary-ligatures] || [ historical-ligatures | no-historical-ligatures] || [ contextual | no-contextual];
  • normal: 默认值,表示可以使用常规的连字效果和上下文形式,以便有更好的阅读体验,至于具体什么时候使用,是由字体、语言和脚本类型共同决定的

  • none: 表示禁用所有连字和上下文形式,甚至禁用常用连字和上下文形式

  • common-ligatures和no-common-ligatures: 用来控制最常用的连字效果。其中,common-ligatures表示使用连字效果,如fi连字、ffi连字、th连字等,对应的OpenType特征值 是liga和clig;no-common-ligatures表示不使用连字效果

  • discretionary-ligatures和no-discretionary-ligatures: 用来控制特殊的连字效果,至于效果表现则由设计师决定。其中,discretionary-ligatures表示使用特殊连字效果,对应的OpenType特征值是dlig。图9-50所示的就是一种特殊连字效果,其并非严格意义上的连字效果,主要用于协调字形与周围上下文的形状。no-discretionary-ligatures表示不使用特殊连字效果。

  • historical-ligatures和no-historical-ligatures: 用来控制古老的历史书中文字的连字效果。其中,historical-ligatures表示使用古代连字效果,对应的OpenType特征值是hlig;no-historical-ligatures表示不使用古代连字效果

  • contextual和no-contextual: 用来控制上下文连字效果,也就是连字效果是否出现由前后的字母决定。其中,contextual表示使用上下文连字效果,其对应的OpenType特征值是calt,no-contextual表示不使用上下文连字效果

<== font-variant-east-asian ==>

说明: 用来设置CJK语言字符的字形变化

语法:

font-variant-east-asian: normal;
font-variant-east-asian: ruby;
font-variant-east-asian: [ jis78 | jis83 | jis90 | jis04 | simplified | traditional];
font-variant-east-asian: [ proportional-width | full-width];
  • normal: 表示字形不会发生变化

  • ruby: 指日文排版中经常会出现的上标形式的尺寸较小的假名,其作用是标注读者可能不熟悉的汉字的含义(有点类似汉字的汉语拼音标注)。由于ruby字体通常较小,为了便于阅读,字体创建 者通常会专门针对这种场景进行设计,例如会让字体的笔画稍微加粗以提高对比度。这里的ruby关键字属性值就可以触发字体中针对ruby设计的字形,其对应的OpenType特征值也是ruby

  • jis78、jis83、jis90和jis04: 表示对应年份的日语字符集

  • simplified和traditional: 分别表示使用简体字形和使用繁体字形,对应的OpenType特征值是smpl和trad,这里的简繁体转换是需要字体本身有专门的简繁体设计才行的,对于中文来说,由于汉字数量多,如果每个字都有繁体字形,这工作量将是巨大的

  • proportional-width、full-width: 用来控制CJK语言字符的尺寸。其中,proportional-width表示字符可以有不同的尺寸,其对应的OpenType特征值是pwid,通常情况下,中文标点符号和中文汉字的宽度是一样的,即宽度为1em。proportional-width关键字的含义就是中文标点符号占据的宽度由标点符号的字形宽度决定,尺寸可以和汉字的宽度不一致。full-width表示字符都是相同的尺寸(包括数字等字符),单个字符占据空间近似正方形,且宽度为1em。其对应的OpenType特征值是fwid。虽然在通常情况下,中文标点符号和中文汉字的宽度是一样的,但是数字和字母的宽度却是由字形决定的。full-width关键字的作用就是让数字和字母占据与中文汉字一样的宽度

==> 字距调整font-kerning <==

作用: 调整字形间距,其底层的作用机制就是激活字体的kern特征值以实现文字的变体效果,这个CSS属性在日文(假名)和英文中比较实用。以英文举例,英文字符的形状是不规则的,有的宽,有的窄,有的圆润光滑,有的棱角分明,这就会导致不同英文字符排列在一起的时候字符间的距离不一致。而font-kerning可以有效利用字符间的间隙,使字间距更紧密

image.png

语法:

font-kerning: auto | normal | none
  • auto: 默认值,表示浏览器自己决定是否要调整字距。例如当字号,也就是font-size属性值比较小的时候,如果进行字距调整就会显得很奇怪,因此,浏览器会禁止字距调整

  • normal: 表示应用字距调整

  • none: 表示不根据字体文件中的字距信息进行字距调整

(6)可变字体

说明: 这是一种新的字体技术,运用该技术可以使用更小的字体文件在Web上实现更丰富的排版效果,甚至可以实现字形的动画效果

==> 可变字体是啥 <==

解释: 传统字体中不同的字重往往都是不同的字体文件,而独立的字体文件会存在字体文件很大和支持的字重效果有限的缺点,为了解决这个问题,就出现了可变字体,可变字体将原本不同的字体文件合并在一个字体文件中,但是这种合并并不是单纯地把静态文件一个接一个地合在一起,而是基于某种规则将它们进行整合,其中一个显著的特点就是不同笔画粗细、不同拉伸程度的字形的控制点的数量是一致的,因此文字笔画从细到粗的变化只需要改变控制点的位置就能精确实现各种字重效果,但是由于可变字体中不同笔画粗细、不同拉伸程度的字形包含了大量相同的信息,因此可变字体的大小要远远小于独立的静态字体的大小,一般来说,可变字体文件的大小为一个一个独立的字体文件的总大小的50%左右

==> 可变字体的变化轴 <==

说明: 变化轴又分为注册变化轴自定义变化轴。注册变化轴可以理解为官方定义的变化轴,总共有5个,分别是字重、字宽、斜体、倾 斜和视觉尺寸;自定义变化轴是字体设计师根据需要自己创造的变化轴,没有任何限制,数量几乎无限,只需要给它一个4个字母的标签以便在外部识别即可

/* 'wght'就是注册变化轴,'GRAD'就是自定义变化轴 */
font-variation-settings: 'wght' 375, 'GRAD' 66;

注意: 在命名的时候注册变化轴都是小写的,自定义变化轴使用大写

<== Weight轴 ==>

说明: 用来控制文字的粗细变化,当然,随着文字变粗,其所占据的宽度也会跟着变大,Weight轴对应传统的font-weight属性,如果字体不是可变字体,则font-weight属性值的范围只能是100~900,且必须是100的倍数;但是如果应用的字体是可变字体,且包含Weight轴,则font-weight属性值的理论范围可以是1~1000的任意整数

注意: 除了可以使用font-weight属性对可变字体的笔画粗细进行精细化设置,还可以使用font-variation-settings属性进行设置

font-weight: 480; 
/* 或者 */
font-variation-settings: 'wght' 480;

image.png

<== Width轴 ==>

说明: 对应传统的font-stretch属性,可以对文字进行拉伸设置。理论上,只要拉伸值大于0都可以渲染,但是大多数字体的Width轴的拉伸值范围都在默认的100%左右,不会太大,也不会太小

font-stretch: 75%; 
/* 或者 */
font-variation-settings: 'wdth' 75;

image.png

注意: font-stretch属性下的拉伸效果并不是单纯的缩放,而是设计师精心设计过的窄文字或宽文字效果,可以让窄屏或者宽屏下的文字有更好的排版效果

<== Italic轴 ==>

说明: 对应传统的font-style:italic声明,表示是否应用斜体。斜体的变化没有中间值,只有0和1这两个值,0表示正立状态,1表示斜体状态

font-style: italic;
/* 避免非可变字体应用font-style:italic也发生倾斜 */
font-synthesis: none;
/* 或者 */
font-variation-settings: "ital" 1;

image.png

<== Slant轴 ==>

说明: 对应传统的font-style:oblique声明,可以对文字进行倾斜设置,可设置的倾斜角度范围为−90~90deg,不过常用的倾斜角度都在0~20deg这个范围

/* 倾斜15°的两种写法,推荐第二种写法 */
font-style: oblique 15deg; 
font-variation-settings: 'slnt' 75;

image.png

<== Optical-size轴 ==>

说明: 用来改变文字的视觉尺寸,包括笔画粗细、字宽和拉伸,对应是的font-optical-sizing属性,由于属性值并不是具体的数值大小,而是normal或者none这两个关键字属性值。因此,在实现可变字体的Optical-size轴的变化效果的时候,只能使用font-variation-settings属性

font-variation-settings: 'opsz' 10;

image.png

==> font-variation-settings属性 <==

作用: 该属性可以用来设置可变字体中的注册变化轴或者自定义变化轴中设计好的具体的样式

语法:

font-variation-settings: normal | [ <string> <number> ]#
  • normal: 默认值,表示使用默认的文字设置效果

  • <string> <number>: string是一个由4个ASCII字符组成的字符,表示可变字体中的注册变化轴或者自定义变化轴的名称,其中字符范围为(U+20)~(U+7E)。注册变化轴使用小写名称,自定义变化轴使用大写名称,而number表示需要设置的值,该值为数值,不带任何单位,具体数值范围由前面的轴的类型决定

注册变化轴名称对应的CSS属性
wghtfont-weight
wdthfont-stretch
italfont-style: italic
slntfont-style: oblique + angle
opszfont-optical-sizing

注意: 在实际开发中,可变字体往往需要作为自定义字体引入,此时要注意,其Mime Type值与传统的字体是有所不同的,可变字体的后缀可以是format("truetype-variations")、format("woff-variations")或format("woff2-variations")

==> font-optical-sizing属性 <==

说明: 这个属性决定是否允许可变字体通过Optical-size变化轴改变文字渲染尺寸

语法:

font-optical-sizing: auto | none
  • auto: 默认值,表示可变字体的视觉尺寸可以调整变化
  • none: 表示浏览器不会修改字形的形状以获得最佳阅读效果

十、多媒体元素的视觉处理

(1)图片与视频元素的内在控制

理解: 图片或者视频等替换元素存在内在尺寸100%适应于外部指定的尺寸这样的特点,此时如果外部CSS设置的宽高比与内在尺寸不一致的情况时,图片或者视频就会被拉伸,在以前,可以利用具有内 部尺寸的元素在没有指定高宽的情况下依然保持比例的特性,仅设置宽度值,或者仅设置高度值,让图片既有自适应的特性,又能保持比例,比如下面这种设置,但是,这种做法有一个缺点,那就是在第一次加载图片的时候,如果图片还没有加载完毕,浏览器会认为内在尺寸是0,等图片加载完毕,高度又会恢复。这样一个突然的高度变化在视觉层面就表现为页面内容跳动出现,图片会不断触发重绘,不仅性能不佳,用户体验也不好,由此推出object-fitobject-position这两个属性,它们都可以让图片的视觉区域在保持比例的情况下适应外部设定的尺寸

img { 
    width: 100%;
}

==> object-fit属性 <==

语法: 只支持五个关键字属性值

object-fit: fill | contain | cover | none | scale-down

使用场景: 缩略图使用cover关键字,列表图使用contain关键字,全屏大图预览使用scale-down关键字

<== fill ==>

说明: 默认值,表示填充,替换内容会拉伸,填满整个content-box的尺寸,不保证保持原有的比例

img {
    /* 实际的宽高比是1:1,可以看见与图片的宽高比不符合,导致图片拉伸 */
    width: 200px;
    height: 200px;
    padding: 20px;
    border: 20px solid rgba(20, 30, 255, 0.5);
    object-fit: fill;
}

image.png

<== contain ==>

说明: contain表示包含,替换内容保持原有尺寸比例,同时替换内容一定可以在content-box中完整显示,至少一个方向的尺寸和content-box保持一致,也就是content-box可能会出现留白

img {
    width: 200px;
    height: 200px;
    padding: 20px;
    background: #abceab;
    border: 20px solid rgba(20, 30, 255, 0.5);
    object-fit: contain;
}

image.png

<== cover ==>

说明: cover表示覆盖,替换内容同样会保持原始的尺寸比例,同时替换内容会完全覆盖content-box区域,至少一个方向的尺寸和content-box保持一致。此关键字可能会让替换内容的部分区域不可见

img {
    /* 一般是长度小的边会完整显示,另一条边会等比缩放,如果content-box */
    /* 放不下,则会溢出隐藏起来 */
    width: 200px;
    height: 200px;
    padding: 20px;
    border: 20px solid rgba(20, 30, 255, 0.5);
    object-fit: cover;
}

image.png

<== none ==>

说明: none表示替换内容的尺寸显示为原始的尺寸,无视外部的尺寸设置。如果图片尺寸较小,就会在四周产生大量留白;如果图片尺寸较大,则会有较大面积的图片区域被剪裁。在实际开发中,此关键字很少被使用

<== scale-down ==>

说明: 其样式表现就好像依次设置none和contain关键字,然后选取呈现的尺寸较小的那个效果

==> object-position属性 <==

说明: 它的作用是控制替换内容的位置,其默认值是50% 50%,也就是说默认是居中效果。所以,无论object-fit的值是哪一个关键字属性值,图片都是水平、垂直居中的。因此,要实现尺寸大小不固定图片的水平、垂直居中效果,可以试试先将img元素的宽度设置为容器的宽度大小,然后设置object-fit:none;其次它的值类型是<position>,因此其拥有很强的定位能力

/* 定位在content-box区域的右下角 */
object-position: 100% 100%;

/* 相对于content-box区域右下角20px 10px的地方定位 */
object-position: right 20px bottom 10px;

(2)image-orientation属性纠正图片的方向

场景: 有时候可能会存在上传到服务器的图片和在前台预览的图片的方向是不一致的,这是因为手机拍摄的照片的可交换图像文件格式(Exif)信息中都会包含旋转信息,常见的图像查看软件、手机App或者Chrome浏览器新标签窗口打开图片时,可以根据图形中的旋转信息自动对图像的方向进行纠正,但是旧版浏览器中的网页中的img元素不能自动旋转,为了满足这个需求,就产生了image-orientation属性,其目的是让网页中的图片也能根据Exif信息中的Orientation值进行旋转

语法:

image-orientation: from-image | none;
  • from-image: 初始值,表示如果图片包含旋转信息,则自动旋转图片
  • none: 表示 无论照片是否包含Exif信息,都不进行旋转

(3)image-rendering属性与图像的渲染

说明: 这个属性属性用来设置图像的缩放算法,主要针对PNG和JPG这类位图,它可以设置在img元素上,也可以设置在img祖先元素上,值得注意的是,这个属性只有在图像发生缩放的时候才会有效果

标准语法:

image-rendering: auto | crisp-edges | pixelated
  • auto: 表示浏览器自动选择使用何种图像缩放算法,通常表现为平滑缩放

  • crisp-edges: 表示不使用平滑缩放算法,因此,缩放的图像会有较高的对比度和较锐利的边缘,也不会有模糊的感觉。常用的算法包括邻近算法和其他像素艺术算法,如2×SaI和hqx系列算法

  • pixelated: 表示当放大图像时,必须使用邻近算法,使图像看起来由大像素块组成;当缩小图像时,使用与auto关键字属性值相同的算法

注意: pixelated和crisp-edges应该合并在一起,互相起到兜底作用;而auto应该单独使用;此外,在常规的场景中,图像采用平滑效果肯定是更好的,因此是用不到image-rendering属性的。但是,如果网页的设计风格是像素化风格或者锐化风格(例如一些像素风格的游戏介绍页面),就可以使 用image-rendering属性改变图像的缩放算法,进而改变图像的渲染效果

(4)不常用的图像类型函数

==> 半透明叠加的cross-fade()函数 <==

作用: 可以让两张图像半透明混合

传统语法:

/* 效果为:第一张图像完全不透明和第二张半透明图像进行叠加 */
cross-fade( <image>, <image>, <percentage> )

注意: 经cross-fade()函数处理后的图像的实际尺寸是受透明度值影响的。如果透明度值是0%,则处理后图像的尺寸是第一张图片的尺寸;如果透明度值是100%,则 处理后图像的尺寸是第二张图片的尺寸;如果透明度值在0%~100%,则处理后图像的尺寸在两张图片的尺寸范围内变化

<== 新语法 ==>

说明: 就是可以指定任意数量的透明叠加图像,同时可以分别指定每张图像的透明度

cross-fade(url(red.png) 20%, url(yellow.png) 30%, url(blue.png) 50%);

注意: 也可以不指定透明度值,则未指定透明度值的图像的透明度值是用100%减去已经指定的透明度百分比值,然后除以未指定透明度值图像的数量得来的

<== 实际应用 ==>

适用场景: 在不影响元素中的文字内容的透明度的情况下实现元素的背景图像半透明

做法: 使用一张透明图片作为第一张混合图片,这样就可以随意调整背景图像的透明度了

.dark {
    background-image: cross-fade(url("透明图片"), url(2.jpg), 40%);
}

==> 神奇的element()函数 <==

作用: 可以让页面中任意DOM元素的渲染效果变成图像

语法:

/* id就是页面中DOM元素的id值 */
element(#id)

举例解释: 例如,页面上有一个按钮的id为button,如果想让div元素的背景图片是这个按钮,则可以使用下面的CSS代码,此时,背景图并不是静止的,而是随着原始元素的样式同步变化的。例如,当鼠标指针经过按钮时,按钮会改变颜色并高亮显示,此时,背景图中的按钮也同步变化了

div { 
    background: -moz-element(#button);
    background: element(#button);
}

image.png

注意: 这个函数只有Firefox浏览器支持