CSS
CSS的全称是层叠样式表 (Cascading Style Sheets)。
- Cascading (层叠):规则如何叠加、冲突如何解决。
- Specificity (优先级):规则的重要性排序。
- Style (样式):如何高效、可维护地应用样式。
渲染过程
CSS 渲染过程分为以下几个主要阶段:
解析阶段(Parsing Stage)
在这个阶段,浏览器会处理和解析 HTML 和 CSS 文件,构建 DOM(Document Object Model)树和 CSSOM(CSS Object Model)树。
- DOM 树:由 HTML 文档解析生成的树形结构,表示文档的结构和内容。
- CSSOM 树:由 CSS 文件解析生成的树形结构,表示文档中所有样式规则的结构。
这两个树会被合并成一个渲染树(Render Tree),其中包含所有需要显示在页面上的元素以及它们的样式。
布局阶段(Layout Stage)
一旦 DOM 和 CSSOM 被解析,浏览器会开始进行 布局阶段。在这个阶段,浏览器计算每个元素的位置、尺寸、边距、边框等,确定它们如何在页面上显示。
- 计算元素的位置和大小:浏览器会根据样式信息(如
width
,height
,margin
,padding
,position
等)计算每个元素的位置和尺寸。 - 确定元素的几何信息:这会影响到元素在页面上的显示效果,确保页面的内容正确布局。
布局阶段的输出是 渲染树,它包含了元素的几何信息(如坐标、尺寸等),但还不涉及样式的细节。
绘制阶段(Painting Stage)
在布局计算完成后,浏览器会进入 绘制阶段。这个阶段负责根据 渲染树 来绘制页面的内容。
- 绘制每个元素的外观:包括背景、边框、文字、阴影、图像等。
- 绘制顺序:浏览器会根据元素在渲染树中的层级关系(z-index、层叠顺序)来决定绘制顺序。
绘制阶段是一个非常重要的过程,它决定了页面的视觉效果,比如文本颜色、背景色、边框、图片等元素的绘制。
合成阶段(Compositing Stage)
一旦页面的所有元素都绘制完成,浏览器进入 合成阶段。在这个阶段,浏览器会将页面分成多个图层进行合成(比如滚动、动画等效果时)。
- 图层合成:浏览器会把渲染树中的元素分配到多个图层上。这些图层可能包含背景、文本、动画等内容。
- GPU 加速:某些元素(如动画、过渡效果等)会被分配到 GPU 图层进行加速处理,从而提高性能。
- 合成图层:最后,浏览器会将这些图层合并成最终的页面,并呈现给用户。
合成阶段的重点是确保页面上的动态效果(如动画、滚动等)流畅地显示出来。
文本渲染阶段(Text Rendering Stage)
文本渲染阶段是一个特定于文本的渲染过程。它的目的是确保文本的显示符合预期的样式和排版要求。
- 字体渲染:浏览器会根据字体规则加载字体,计算文本的排版方式。
- 行高、字间距:确保文本的行高、字间距等样式正确应用。
- 伪元素的渲染:例如
::first-line
,::before
,::after
等伪元素会在这个阶段渲染,影响文本的显示。
这个阶段主要影响的是页面中文本的显示效果,确保文本在不同字体和排版要求下呈现正确。
重绘和重排(Repaint and Reflow)
在页面渲染完成之后,可能会出现一些需要更新页面内容的情况,例如改变元素样式、大小、位置等。这时,浏览器会触发 重绘(Repaint) 和 重排(Reflow)。
- 重绘(Repaint):当样式发生变化,但元素的布局没有变化时(例如颜色、背景等),浏览器只需要进行重绘。改变元素的外观属性(不影响布局),开销较小。(
color
,background-color
,visibility
)。 - 重排(Reflow):当元素的布局发生变化时(例如尺寸、位置、边距等),浏览器需要重新计算元素的位置和大小,重新进行布局并重绘。改变元素的几何属性(尺寸、位置),会触发重新计算布局,开销大。(
width
,height
,margin
,padding
,left
,top
)
重排 比 重绘 更加消耗性能,因为它涉及到元素位置和尺寸的重新计算。
合成与显示(Compositing and Display)
合成阶段完成后,最终的图层会被合成并呈现到屏幕上,用户看到的是最终渲染出来的页面内容。
层叠
层叠决定了当多个规则应用于同一个元素时,哪个规则最终生效。它取决于三个因素(按重要性降序):
-
来源 (Origin) 和
!important
:- 用户代理样式(浏览器默认样式) < 用户样式 < 作者样式(开发者写的) <
!important
作者样式 <!important
用户样式 - 切记:
!important
破坏了自然的层叠规则,仅在极其特殊的情况下使用(如覆盖第三方库的内联样式)。滥用会导致样式难以管理和覆盖。
- 用户代理样式(浏览器默认样式) < 用户样式 < 作者样式(开发者写的) <
-
优先级 (Specificity):
权重计算(从高到低):
组成部分 示例 权重值 (方便记忆) 内联样式 style="color: red;"
1000
ID 选择器 #header
100
类选择器、属性选择器、伪类 .class
,[type="text"]
,:hover
10
元素选择器、伪元素 div
,::before
1
通配符、关系选择器 *
,>
,+
,~
0
如何计算?
- 比较规则:从左到右(内联
a
->IDb
-> Classc
-> Elementd
),高位胜出后就不再比较低位。如果样式是在元素的style
属性中直接定义的(即内联样式),则a=1
。否则a=0
。注意: 只有内联样式会计入这里,外部或内部样式表里的规则a
永远为 0。 #header .nav-link.active
有一个IDb
,2个Classc
和0个Elementd
,所以它的优先级是 (1, 2, 0) -> 权重 120body #header .title
有一个IDb
,1个Classc
和1个Elementd
的优先级是 (1, 1, 1) -> 权重 111120 > 111
,所以前者胜出。
<style>body #header .title {color: aquamarine;}#header .nav-link.active {color: red;} </style> <body><div id="header"><span class="title nav-link active">我是红色还是绿色</span></div> </body>
- 比较规则:从左到右(内联
-
出现顺序 (Order)
如果冲突的规则具有相同的来源和相同的特异性,那么最后出现的规则胜出。
- 在样式表中,后定义的规则会覆盖先定义的规则。
- 对于引入的多个样式表文件,
<link>
和<style>
标签在 HTML 中的顺序至关重要。后引入的样式表具有更高的优先级。
-
特殊例子
<style> p#a {color: green; }div::first-line {color: blue; }</style> <div><p id="a">First Paragraph</p> </div>
运行上面代码会发现文字颜色不是绿色而是蓝色。根据 CSS 规范,
::first-line
伪元素的样式确实可能覆盖子元素的颜色样式,这是因为它是在 文本渲染阶段 直接应用的。在渲染时,浏览器会首先确定首行的样式,确保文本的视觉效果符合预期。由于
::first-line
直接作用于文本的渲染阶段,它的样式会覆盖 子元素(如p
标签中的文本)的颜色样式。
继承属性
某些属性(非全部)会自动从父元素传递给子元素。
- 可继承属性: 通常是与文本内容相关的属性,如
color
,font
,font-size
,font-family
,text-align
,line-height
,list-style
等。 - 不可继承属性: 通常是与盒模型和布局相关的属性,如
width
,height
,margin
,padding
,border
,background
,display
,position
等。 - 控制继承:
inherit
: 显式地从父元素继承该属性。initial
: 设置为此属性的初始默认值(不受浏览器默认样式影响)。unset
: 如果该属性是自然继承的,则表现为inherit
;否则,表现为initial
。revert
: 将属性值重置为浏览器的默认样式(用户代理样式表)。
- 例子:
/* Selects any element that is NOT a paragraph */
:not(p) {color: blue;
}
意思是,:not(p)
可以选择任何不是 <p>
标签的元素。然而,上面的 CSS 选择器,在如下的 HTML 结构,实测的结果不太对劲。
<p>p</p>
<div>div</div>
<span>span</span>
<h1>h1</h1>
由于 :not(p)
同样能够选中 <body>
,那么 <body>
的 color 即变成了 blue
,由于 color
是一个可继承属性,<p>
标签继承了 <body>
的 color 属性,导致看到的 <p>
也是蓝色。
如果把它改成一个不可继承的属性,这次 <p>
没有边框体现
/* Selects any element that is NOT a paragraph */
:not(p) {border: 1px solid;
}
单位
-
绝对单位:
px
(像素)。在Web中,它其实是一个相对单位(相对于设备像素),但通常被视为绝对单位。.px {font-size: 14px;width: 200px;height: 60px;border: 1px solid #ccc; } <div class="px">像素(px):展示了固定尺寸的元素,不随屏幕大小变化</div>
这个容器使用像素单位
定义尺寸,无论父容器或屏幕如何变化,它都保持固定大小。像素单位
提供精确控制,但在响应式设计中不够灵活,因为元素不能根据上下文调整大小。
-
相对单位(现代响应式设计的核心):
-
%
: 相对于父元素的相同属性的值(width
、padding
)或父元素的字体大小(font-size
)。.box {padding: 20px;border-radius: 8px;background-color: #e8f4fc;width: 500px; } .box .sub-box {width: 80%;padding: 5%;background-color: aquamarine; } <div class="box"><div class="sub-box"><p>百分比(%):展示了相对于父元素宽度的百分比布局</p></div> </div>
这个容器的宽度是其父元素的80%,内边距是自身宽度的5%。
说明: 百分比单位在创建流动布局时非常有用,允许元素根据父容器调整大小。
-
em
: 相对于当前元素的font-size
。2em
= 当前字体大小的2倍。用于margin
/padding
时,非常灵活。.box.em-box {font-size: 1.2em;padding: 1.5em; }<div class="box em-box">基于em单位的尺寸,这个容器的内边距(1.5em)是基于它的字体大小(1.2em)计算的。浏览器预设字体大小为16px,若无特别指定则会直接继承父元素字体大小,那么:.em-box的字体大小就是相对当前字体大小16* 1.2 = 19.2px, 1.5em =1.5 * 19.2=28.8px<br /><p>p的字体大小也是19.2px</p> </div>
.em-box盒子
原本继承根元素 (<html>
) 字体大小是16px
,但是样式设置为1.2em
的字体,所以相对当前元素的字体,它的实际字体大小是16*1.2 = 19.2px
,padding
大小则是1.5 *19.2 = 28.8px
。.em-box盒子
下的p元素
继承父元素的字体大小,也是19.2px
,如果它也设置了1.2em
的字体大小,则它的实际字体大小是19.2 * 1.2 = 23.04px
。说明:
em
单位在设置与文本大小相关的属性(如margin、padding)时非常有用,可以保持比例一致性。 -
rem
: 相对于根元素 (<html>
) 的font-size
。这使得它成为设置全局尺寸比例的完美选择,易于维护和响应式调整。.box.rem-box {font-size: 1.2rem;padding: 1.5rem; } <div class="box rem-box">这个容器的大小是基于根元素的字体大小(通常是16px)计算的。如果根字体大小是16px,那么:1.2rem = 19.2px, 1.5rem = 24px<p>p的字体大小也是19.2px</p> </div>
.rem-box盒子
原本继承根元素 (<html>
) 字体大小是16px
,但是样式设置为1.2rem
的字体,所以相对当前元素的字体,它的实际字体大小是16*1.2 = 19.2px
,padding
大小则是1.5 *16 = 24px
。.rem-box盒子
下的p元素
继承了1.2rem
的字体属性,所以它的实际字体大小也是16 * 1.2 = 19.2px
。说明:
rem
单位提供了全局一致性,只需调整根字体大小即可缩放整个布局,非常适合响应式设计。 -
vw
/vh
: 相对于视口宽度/高度的 1%。50vw
= 视口宽度的一半。.box.viewport-box {width: 10vw;height: 10vh; } <div class="box viewport-box">尝试调整浏览器窗口大小,观察这个容器的变化。 </div>
说明:视口单位非常适合创建全屏布局或与视口大小直接相关的元素。
-
ch
: 相对于当前字体下的数字 “0” 的宽度。常用于限制容器的字符数<div class="example"><div style="width: 5ch; overflow: hidden">012345678910</div> </div>
这个容器的宽度被设置为5个"0"字符的宽度。可以看到只显示到4,后面超过宽度的数字全被隐藏了。
说明:ch单位非常适合用于控制文本容器的宽度,以优化每行的字符数,提高阅读体验。
-
盒模型
盒模型的组成部分
每个HTML元素都被表示为一个矩形盒子,这个盒子由四个部分组成:内容(content)
、内边距(padding)
、边框(border)
和外边距(margin)
。
- 内容区域(Content):包含元素的实际内容,如文本、图片等。内容区域的大小由
width
和height
属性设置。 - 内边距(Padding):内容区域与边框之间的空间,内边距可以使内容与边框之间保持一定的距离。通过
padding
属性设置,四个方向的内边距可以单独定义或使用简写属性一次性设置。 - 边框(Border):围绕内边距和内容区域的边框,边框的样式、宽度和颜色可以通过
border
属性设置。 - 外边距(Margin):元素与相邻元素之间的空间,外边距用于调整元素的位置。通过
margin
属性设置,同样支持单独定义每个方向或使用简写属性。
盒模型的计算
元素的总宽度和高度的计算方式取决于盒模型的类型(box-sizing
属性):
标准盒模型 (content-box):
总宽度 = width + padding(左右) + border(左右)+margin(左右)
总高度 = height +padding(上下) + border(上下)+margin(上下)
IE盒模型 (border-box):
总宽度 = width (已包含padding和border)+margin(左右)
总高度 = height (已包含padding和border)+margin(上下)
现代CSS开发中,通常会将所有元素的box-sizing设置为border-box:
*{box-sizing: border-box;
}
这样可以更直观地控制元素的大小,因为padding和border不会增加元素的总宽度。
边距塌陷问题
边距塌陷(Margin Collapsing)是CSS中一个常见但令人困惑的现象,主要发生在垂直方向上。当两个或多个垂直相邻的块级元素的边距相遇时,它们会合并成一个边距,大小取其中较大的那个值。
* {margin: 0;padding: 0;
}
.box {width: 100px;height: 100px;background-color: bisque;border: 1px solid transparent;
}
.m-b-40 {margin-bottom: 40px;
}
.m-t-30 {margin-top: 30px;
}
.sub-box {width: 50px;height: 50px;background-color: aquamarine;
}//相邻元素塌陷
<div class="box m-b-40"></div>
<div class="box m-t-30"></div>//父子元素塌陷
<div class="box"><div class="sub-box m-t-30"></div>
</div>
注意:相邻元素的外边距合并是CSS的正常行为
解决父子元素塌陷有以下方法:
-
使用Flexbox或Grid布局 - 这些现代布局技术会创建新的布局上下文,阻止边距合并
display:flex; // grid
-
添加内边距(padding) - 在两个元素之间添加即使很小的内边距也可以阻止边距合并
padding:1px;
-
添加边框(border) - 边框会物理上分隔两个元素,阻止它们的边距合并
border:1px solid transparent;
-
使用overflow属性 - 设置overflow为非visible值会创建新的块格式化上下文(BFC)
overflow:auto;
-
使用inline-block显示 - inline-block元素不会与其它块级元素发生边距合并
display:inline-block;
BFC
BFC(Block Formatting Context)即块级格式化上下文,是Web页面的可视化CSS渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其他元素的交互限定区域。
通俗来讲,BFC是一个独立的布局环境,其中的元素布局不会影响到外部元素,同时外部环境的布局也不会影响到BFC内部。BFC具有一些特性,例如可以包含浮动元素,可以阻止边距合并等。
BFC的特性
-
内部块级元素垂直排列
BFC内的块级元素从上到下依次排列,垂直方向上的间距由margin决定
-
外边距合并
BFC内部相邻元素的垂直外边距合并(合并结果以最大的为准),但BFC内部与外部元素的外边距不会合并 -
包含浮动元素
如果父元素是BFC,则其内部的浮动子元素的高度会被计算到父容器中(解决高度塌陷问题) -
阻止浮动元素覆盖
BFC的区域不会与浮动元素重叠(例如,用BFC实现两栏自适应布局)
如何触发BFC
创建条件: float
(非 none
), position
(absolute
/fixed
), display
(inline-block
, table-cell
, flex
), overflow
(非 visible
)。
/* 任意一个属性即可触发 */
.element {overflow: hidden | auto | scroll; /* 非visible值 */float: left | right; /* 非none */position: absolute | fixed; /* 绝对定位 */display: flex | grid | inline-block | table-cell | table-caption | table | table-row | inline-flex | inline-grid | flow-root; /* flow-root适用于现代浏览器,不会产生副作用 */content: layout; /* 新特性,现代浏览器支持 */
}
BFC应用场景
-
解决外边距合并问题:
//相邻元素塌陷 实际间距为30 <div class="box m-b-40"></div> <div class="box m-t-30"></div>
将其中一个元素包裹在新的BFC容器中,阻断外边距合并
.bfc-container {display: inline-block; }<div class="box m-b-40"></div> <div class="bfc-container"><div class="box m-t-30"></div> </div>
-
清除浮动导致的父元素高度塌陷问题:
.parent {border: 1px solid #ccc; } .float-child {float: left;width: 100px;height: 100px;background-color: aquamarine; }<div class="parent"><div class="float-child">浮动元素</div> </div>
触发父元素的BFC,强制包裹浮动子元素
.overflow-auto {overflow: auto; } <div class="parent overflow-auto"><div class="float-child">浮动元素</div> </div>
-
解决使用浮动实现自适应两栏布局,右侧内容被左侧浮动元素遮挡问题:
.overflow-auto {overflow: auto; } <div class="parent"><div class="float-child">浮动元素</div><div class="child overflow-auto">非浮动右侧元素</div> </div>
触发右侧容器的BFC,形成独立布局区域
<div class="child overflow-auto">非浮动右侧元素</div>
-
阻止文本环绕浮动元素
.content {height: 200px;width: 200px;background-color: orange;border: 2px solid blue; } <div class="parent"><div class="float-child">浮动元素</div><div class="content">这是一段文本内容,它会环绕在浮动元素的周围。这是一段文本内容,它会环绕在浮动元素的周围。</div> </div>
触发文本容器的BFC,阻止文本环绕
<div class="content overflow-auto">这是一段文本内容,它会环绕在浮动元素的周围。这是一段文本内容,它会环绕在浮动元素的周围。 </div>
布局模式
元素通过 display
, position
, float
属性决定其布局方式。
display
属性
block
: 块级元素,独占一行,可设置宽高。inline
: 行内元素,不独占一行,不可设置宽高,垂直方向的padding
/margin
不生效。inline-block
: 行内块元素,不独占一行,但可以设置宽高。none
: 元素不显示,并从渲染树中移除,不占空间。flex
:一种用于按行
或按列
布局元素的一维
布局grid
:用于创建一个二维
的网格
布局
position
属性
值 | 描述 | 定位基准 | 是否脱离文档流 |
---|---|---|---|
static (默认) |
正常流布局 | - | 否 |
relative |
相对定位 | 自身原本的位置 | 否(原位置保留) |
absolute |
绝对定位 | 最近的非 static 定位的祖先元素 |
是 |
fixed |
固定定位 | 视口 (viewport) | 是 |
sticky |
粘性定位 | 在特定阈值内相对滚动父容器固定 | 否(但表现类似固定) |
- 脱离文档流:
absolute
,fixed
元素会脱离正常的布局流,不再占据空间,后续元素会填补其位。 - 包含块 (Containing Block): 对于
absolute
元素,理解其定位基准至关重要。
包含块的定义:
一般来说,元素的包含块是其最近的块级祖先元素的内容区域。但是,对于绝对定位的元素,包含块是最近的position值不为static(即relative、absolute、fixed或sticky)的祖先元素的内边距区域(padding area)。如果没有这样的祖先元素,那么包含块就是初始包含块(通常是视口或HTML元素)。
对于绝对定位元素:
- 绝对定位元素(position: absolute)的位置是相对于其包含块来计算的。
- 通过top、right、bottom、left属性来指定相对于包含块边缘的偏移。
float
属性
- 最初设计用于文字环绕图片,后被用于布局(现已过时)。
- 会使元素脱离文档流,并向左/右浮动直到碰到父容器或其他浮动元素的边缘。
- 清除浮动 (Clearfix): 父元素包含浮动子元素时,高度会塌陷(因为子元素脱离流)。
.parent::after {content: "";display: table;clear: both;
}<div class="parent"><div class="float-child"></div>
</div>
布局挑战
圣杯布局
(Header, Footer, 固定左右边栏+流动主内容区)
float实现圣杯布局:https://codepen.io/small-windmill/pen/ByoOpbV
思路:
- 给包裹左中右区域的主容器.middle 设置左右padding值(值为左右区域的宽度)
- 给左中右三个区域设置float:left
- 分别给左右两栏设置负margin值,左栏margin-left为-100%,右栏位margin-left为负的其自身宽度(利用了浮动元素的负margin到一定值后会使其自身往上一行移动的原理)
- 左右两栏设置position:relative定位,对它们进行定位,移动到两侧
双飞翼布局
不仅能满足main处于优先加载的地位,而且更好的解决了圣杯布局的错乱问题,且CSS代码更简单,缺点是增加了一个标签
float实现双飞翼布局:https://codepen.io/small-windmill/pen/NPGLpaZ
思路:
- 给.main区域包裹一个div标签.middle,.main区域设置左右margin为侧边栏宽度
- 给.middle设置float:left,宽度100%和高度
- 给侧边栏设置float:left,分别给左右两栏设置负margin值,左栏margin-left为-100%,右栏位margin-left为负的其自身宽度(利用了浮动元素的负margin到一定值后会使其自身往上一行移动的原理)
等高卡片布局
无论每个卡片的内容量如何,所有卡片都保持相同的高度,创造整洁统一的外观。
使用flex布局实现:https://codepen.io/small-windmill/pen/qEOMJvw
使用grid布局实现:https://codepen.io/small-windmill/pen/OPyoaNp
垂直水平居中(多种方法)
-
使用position定位
.relative-container {position: relative;width: 100px;height: 100px;background-color: bisque; } .absolute-item {position: absolute;top: 50%;left: 50%;width: 50px;height: 50px;background-color: aqua;/* margin-top: -25px; *//* margin-left: -25px; */transform: translate(-50%, -50%); } <div class="relative-container"><div class="absolute-item"></div> </div>
-
使用flex布局
.flex-container {display: flex;/* align-items: center;justify-content: center; */width: 100px;height: 100px;background-color: bisque; } .flex-item {width: 50px;height: 50px;margin: auto;background-color: aqua; } <div class="flex-container"><div class="flex-item"></div> </div>
-
使用grid布局
.grid-container {display: grid;place-items: center;width: 100px;height: 100px;background-color: bisque; } .grid-item {width: 50px;height: 50px;background-color: aqua; } <div class="grid-container"><div class="grid-item"></div> </div>
-
使用表格布局
.table-container {display: table;width: 100px;height: 100px;background-color: bisque; } .table-item {display: table-cell;text-align: center; /* 水平居中 */vertical-align: middle; /* 垂直居中 */background-color: aqua; } <!-- 使用表格布局实现水平垂直居中 --> <div class="table-container"><!-- 表格单元格会根据父表格单元的尺寸自动扩展。即使设置子元素的宽高,它仍然会尝试填满父容器的 100px 的高度和宽度。 --><div class="table-item">222</div> </div>
-
特殊场景使用line-height
.text-container {width: 100px;line-height: 100px; /* 等于容器高度 */text-align: center;background-color: bisque; } .text-item {display: inline-block;vertical-align: middle;line-height: normal; /* 重置文本行高 */background-color: aqua; } <div class="text-container"><div class="text-item">居中文本</div> </div>
瀑布流布局
使用column-count和column-gap:https://codepen.io/small-windmill/pen/bNVxzap
思路:
- 使用
column-count
属性指定列数- 使用
column-gap
属性设置列间距- 使用
break-inside: avoid
防止项目被分割到不同的列
使用flex:https://codepen.io/small-windmill/pen/WbQgmNE
- 创建一个flex容器
- 在容器内创建多个列
- 每个列也是一个flex容器,方向为column
- 需要手动将内容项分配到各列中
使用grid:https://codepen.io/small-windmill/pen/vENVMQd
思路:
- 使用
grid-template-columns
定义列数和列宽- 使用
gap
属性设置项目之间的间距- 使用
grid-auto-rows
设置基础行高- 使用
grid-row-end
控制每个项目在垂直方向上的跨度
background属性
background属性就是专门设置背景的属性,可以设置背景色,也可以设置背景图片。
属性 | 描述 |
---|---|
background-color: | 设置元素的背景颜色,可以使用颜色名称 、十六进制值 、RGB 、RGBA 、HSL 或HSLA 值。 |
background-image: | 设置元素的背景图像,可以使用url() 函数指定图像路径,或使用渐变函数创建渐变背景。 |
background-repeat: | 控制背景图像是否重复 |
background-position: | 控制背景图像在元素中的位置,可以使用关键词、百分比或长度值。 |
background-attachment: | 控制背景图像是否跟随窗口滚动,常用值:scroll 、fixed 、local 。 |
background-size | 设置背景图像的尺寸,可以使用cover 、contain 、具体尺寸或百分比值。 |
background-origin | 设置 background-position 属性相对于什么位置来定位背景图像,常用值:border-box 、padding-box 、content-box 。 |
background-clip | 设置背景图像的显示区域,常用值:border-box 、padding-box 、content-box 、text 。 |
background | 背景属性的缩写,可以在一个声明中设置所有的背景属性 |
background-origin
background-origin
属性用于指定背景图像的定位区域,即决定背景图像相对于哪些区域来定位。它控制了背景图像的坐标系,以决定背景图像如何与元素的边框和内容区域对齐。
border-box
(默认值):背景图像相对于元素的 边框区域 来定位。背景图像将覆盖元素的内容区域、内边距区域和边框区域。padding-box
:背景图像相对于元素的 内边距区域 来定位。背景图像不会覆盖边框区域,而是仅限于元素的内容区域和内边距区域。content-box
:背景图像相对于元素的 内容区域 来定位。背景图像仅会显示在内容区域内,完全不覆盖内边距或边框区域。
<style>
.box {display: inline-block;margin-bottom: 12px;width: 100px;height: 100px;
}
.bg-origin {border: 10px solid rgba(255, 0, 0, 0.1);padding: 40px;background-image: url(./NATOURS/img/hero.jpg);background-repeat: no-repeat;background-position: center;background-size: 100% 100%;
}
</style><div class="bg-item"><div class="box bg-origin bg-origin-border"></div><span> background-origin border-box效果示例</span>
</div>
<div class="bg-item"><div class="box bg-origin bg-origin-padding"></div><span> background-origin padding-box效果示例</span>
</div>
<div class="bg-item"><div class="box bg-origin bg-origin-content"></div><span> background-origin content-box效果示例</span>
</div>
background-clip
background-clip
是CSS中用于设置元素背景绘制区域的属性。它决定了背景(颜色或图像)是否延伸到边框、内边距或内容区域。
border-box
: 默认值。背景将扩展到整个元素,包括边框区域。背景图像/颜色会绘制在元素的内容区域、内边距区域和边框区域。padding-box
: 背景将限制在元素的 内边距区域 内,背景图像/颜色不会扩展到边框区域,但会扩展到内容区域和内边距区域。content-box
: 背景将限制在元素的 内容区域 内,背景图像/颜色不会覆盖内边距和边框区域。text
: 实验性属性。背景图像仅在 文本内容 区域内绘制,这个值主要用于文字背景。在文字上设置背景时,背景图像会只显示在文本上,而不是在整个容器内。
<style>
.bg-clip {border: 10px solid rgba(255, 0, 0, 0.1);padding: 40px;background-image: url(./NATOURS/img/nat-10.jpg);background-repeat: no-repeat;background-position: center;
}
.bg-clip-border {background-clip: border-box;
}
.bg-clip-padding {background-clip: padding-box;
}
.bg-clip-content {background-clip: content-box;
}
.bg-clip-text {width: 450px;font-size: 30px;font-weight: 900;color: transparent;-webkit-background-clip: text;background-clip: text;
}
</style><!-- background-clip -->
<div class="bg-item"><div class="box bg-clip bg-clip-border"></div><span>background-clip border-box效果示例</span>
</div>
<div class="bg-item"><div class="box bg-clip bg-clip-padding"></div><span>background-clip padding-box效果示例</span>
</div>
<div class="bg-item"><div class="box bg-clip bg-clip-content"></div><span>background-clip content-box效果示例</span>
</div>
<div class="bg-item"><div class="box bg-clip bg-clip-text">background-clip text效果示例</div>
</div>
渐变背景
CSS渐变是使用background-image
属性创建的图像,可以实现平滑的颜色过渡。
线性渐变 (linear-gradient)
创建沿着一条直线变化的颜色渐变。可以指定方向(角度或关键词)和颜色停止点(特定颜色出现的位置)。
background-image: linear-gradient(to right, #ccc, #000);
径向渐变 (radial-gradient)
创建从一个中心点向外辐射的颜色渐变。可以指定形状、大小和位置。
background-image: radial-gradient(#ccc, #000);
角向渐变 (conic-gradient)
创建围绕中心点旋转的颜色渐变。颜色围绕中心点旋转,而不是从中心辐射。
background-image: conic-gradient(#ccc, #000);
渐变技巧
在渐变中使用transparent
当我们需要实现这样一种遮罩效果的时候,就可以使用从白色到透明色的渐变,通过叠加在元素上方的方式,实现该效果。
<style>.search-container {display: flex;position: relative;width: 400px;column-gap: 12px;align-items: center;}.search-container ul {display: flex;overflow-x: auto;padding: 0;flex: 1;align-items: center;column-gap: 4px;}.search-container ul ::before {position: absolute;top: 0;right: 0;bottom: 0;width: 100px;background: linear-gradient(to right, transparent, #fff);content: "";}/* 完全隐藏滚动条 */::-webkit-scrollbar {display: none;}li {list-style: none;border: 1px solid #000;border-radius: 4px;padding: 4px;}.search-title {font-weight: bold;}.search-del {position: relative;border-radius: 4px;padding: 4px;font-size: 12px;background-color: #ccc;}</style><div class="search-container"><div class="search-title">搜索历史</div><ul><li>Button</li><li>Button</li><li>Button</li><li>Button</li><li>Button</li><li>Button</li></ul><div class="search-del">删除</div>
</div>
利用渐变绘制三角形
<style>.triangle {width: 100px;height: 100px;background: linear-gradient(45deg,#f00 0%,#f00 50%,transparent 50%,transparent 100%);}
</style><div class="triangle"></div>
如果不设置渐变方向,默认是从上到下,它将页面分为两部分:上半部分红色,下半部分透明。当使用 45deg
时,渐变方向改变为对角线(左上到右下)。这意味着红色和透明的分割线会沿对角线延伸。因为渐变是沿对角线进行的,所以渐变的 50%
分界点会从左上角到右下角的对角线上,这样就产生了一个三角形的效果。
渐变是可以叠加多层的
CSS允许为一个元素设置多个背景图像,用逗号分隔。先列出的图像会绘制在后续图像的上面。
<style>.internal {margin: 12px 0;width: 100px;height: 80px;background-image: linear-gradient(135deg, transparent 15px, deeppink 0),linear-gradient(-135deg, transparent 15px, deeppink 0),linear-gradient(-45deg, transparent 15px, deeppink 0),linear-gradient(45deg, transparent 15px, deeppink 0);background-repeat: no-repeat;background-position: top left, top right, bottom right, bottom left;background-size: 50% 50%, 50% 50%, 50% 50%, 50% 50%;}</style>
<div class="internal"></div>
利用repeating-linear-gradient节省代码
有的时候,我们需要用到不断重复的渐变。这个时候,除了 background-repeat: repeat
之外,官方还提供了一个 repeating-linear-gradient
,可以创建一个由线性渐变重复组成的图形。
.box {display: inline-block;margin-bottom: 12px;width: 100px;height: 100px;
}.bg-multi-img {background: repeating-linear-gradient(90deg,red 0,red 1px,transparent 1px,transparent 10px),repeating-linear-gradient(red 0,red 1px,transparent 1px,transparent 10px),#ccc;background-position: 5px 0, 0 5px; /* 第一层偏移 5px,第二层偏移 5px */
}<div class="box bg-multi-img"></div>
repeating-linear-gradient
做了什么事情呢?其实是按照每 1px 安排一段 red
,每 9px 安排一段 transparent
。
预留衔接空间消除渐变产生的锯齿
在使用渐变生成不同颜色的直接过渡时,非常容易就会产生锯齿效果。
.circle {margin: 12px 0;width: 100px;height: 100px;background: radial-gradient(#9c27b0 0%,#9c27b0 47%,#ffeb3b 47%,#ffeb3b 100%);}
使用 radial-gradient(#9c27b0 0%, #9c27b0 47%, #ffeb3b 47%, #ffeb3b 100%)
生成的一个图形,从一种颜色直接到另外一种颜色,但是,仔细看看衔接处,会发现有非常明显的锯齿效果。
遇到此类问题的解决方案是:在衔接处,适当留出一些渐变空间。
.circle {margin: 12px 0;width: 100px;height: 100px;background: radial-gradient(#9c27b0 0%,#9c27b0 47%,#ffeb3b 47.8%,#ffeb3b 100%);}
这多出来的 0.8% 就是为了消除锯齿的,0.8不是写死的,可以多调试选取既不会看出模糊,又能尽可能消除锯齿的一个范围幅度。
渐变色动画
线性渐变(径向渐变、角向渐变)是不支持 animation
的,单色的 background 是支持的。
因此,在传统的 CSS 中,我们通常想实现背景色渐变动画,有几种方式:
- 通过 background-position 的移动模拟渐变动画;
- 通过 background-size 的缩放过程来模拟渐变动画;
- 通过外层的大渐变图形的移动(transform)来模拟渐变动画。
通过 background-position 模拟渐变动画
<style>.bg-position {margin-bottom: 12px;width: 200px;height: 100px;background: linear-gradient(to right,#ffc700 0%,#e91e1e 50%,#6f27b0 100%);background-position: 0 0;background-size: 200% 100%;animation: bgPosition infinite 2s linear alternate;}@keyframes bgPosition {0% {background-position: 0 0;}100% {background-position: 100% 0;}}
</style><div class="bg-position"></div>
background-position
:指定图片的初始位置。这个初始位置是相对于以background-origin
定义的背景位置图层来说的。
background-size
:设置背景图片大小。当取值为百分比时,表示指定背景图片相对背景区的百分比大小。当设置两个参数时,第一个值指定图片的宽度,第二个值指定图片的高度。
通过 background-size: 200% 100%
将图片的宽度设置为两倍背景区的宽度,再通过改变 background-position
的 x 轴初始位置来移动图片,由于背景图设置的大小是背景区的两倍,所以 background-position
的移动是由 0 0
-> 100% 0
。
通过 background-size 模拟渐变动画
<style>.bg-size {margin-bottom: 12px;width: 200px;height: 100px;background: linear-gradient(to right,#ffc700 0%,#ff9100 33%,#e91e1e 66%,#6f27b0 100%);background-position: 100% 0;animation: bgSize infinite 5s ease-in-out alternate;}@keyframes bgSize {0% {background-size: 300% 100%;}100% {background-size: 100% 100%;}}
</style>
<div class="bg-size"></div>
上述两种方式,由于使用了 background-position
和 background-size
,并且在渐变中改变这两个属性,导致页面不断地进行大量的重绘(repaint),对页面性能消耗非常严重。
通过 transform 模拟渐变动画
使用伪元素配合 transform
进行渐变动画,通过元素的伪元素 before
或者 after
,在元素内部画出一个大背景,再通过 transform
对伪元素进行变换:
<style>.bg-transform {overflow: hidden;position: relative;margin-bottom: 12px;width: 200px;height: 100px;}.bg-transform::before {position: absolute;top: 0;right: -100%;bottom: 0;left: -100%;background-image: linear-gradient(to right,#ff9100 33%,#e91e1e 66%,#6f27b0 100%);background-size: 100% 100%;animation: bgTransform infinite 3s linear alternate;content: "";}@keyframes bgTransform {0% {transform: translateX(-30%);}25% {transform: translateX(-15%);}50% {transform: translateX(0%);}75% {transform: translateX(15%);}100% {transform: translateX(30%);}}</style>
<div class="bg-transform"></div>
通过滤镜 hue-rotate 实现渐变动画
hue-rotate
:为色相旋转滤镜,默认的值为 0deg
,当旋转 360deg
后,相当于回到了本身的颜色值。
通过滤镜 hue-rotate
,可以非常方便地实现背景色渐变动画,过渡效果也非常的自然,代码量也少。
<style>.bg-hue {margin-bottom: 12px;width: 200px;height: 100px;background-image: linear-gradient(to right, #ff9100, #6f27b0);background-size: 100% 100%;animation: bgHue infinite 3s linear alternate;}@keyframes bgHue {0% {filter: hue-rotate(0);}100% {filter: hue-rotate(360deg);}}
</style>
<div class="bg-hue"></div>
使用 CSS @property 实现背景色渐变动画
根据 MDN -- CSS Property,@property CSS at-rule 是 CSS Houdini API 的一部分,它允许开发者显式地定义他们的 CSS 自定义属性,允许进行属性类型检查、设定默认值以及定义该自定义属性是否可以被继承。
简单解读下,CSS @property 其实就是灵活度更高的 CSS 自定义属性,我们也可以称其为 CSS Houdini 自定义属性。其中:
@property --property-name
中的--property-name
就是自定义属性的名称,定义后可在 CSS 中通过var(--property-name)
进行引用。- syntax:该自定义属性的语法规则,也可以理解为表示定义的自定义属性的类型。
- inherits:是否允许继承。
- initial-value:初始值。
可以利用 CSS Houdini 自定义属性,将原本定义在 background
的过渡效果嫁接到了 color
之上,而 CSS 是支持一个颜色变换到另外一个颜色的。
<style>@property --colorA {syntax: "<color>";inherits: false;initial-value: fuchsia;}@property --colorB {syntax: "<color>";inherits: false;initial-value: #f79188;}@property --colorC {syntax: "<color>";inherits: false;initial-value: red;}.bg-property {margin-bottom: 12px;width: 200px;height: 100px;background-image: linear-gradient(to right,var(--colorA),var(--colorB),var(--colorC));background-size: 100% 100%;animation: bgProperty infinite 3s linear alternate;}@keyframes bgProperty {0% {--colorA: red;--colorC: #a93ee0;--colorF: fuchsia;}25% {--colorA: #ff3c41;--colorC: #e228a0;--colorF: #2e4c96;}50% {--colorA: orange;--colorC: green;--colorF: teal;}75% {--colorA: #ae63e4;--colorC: #0ebeff;--colorF: #efc371;}}
</style><div class="bg-property"></div>
background-attachment
background-attachment
是一个 CSS 属性,用于控制背景图像在滚动页面时的固定方式。它指定背景图像是随着页面的滚动一起滚动,还是固定在视口中,或者在其他方式下表现出来。
为了更好地理解,我们通常将页面内容分为几个层次:
- 元素内容:在最上层,随滚动条移动。
- 元素背景:在内容层之下。
- 视口(Viewport):就是你看网页的“窗口”,它本身是固定不动的。
background-attachment
控制的就是背景层相对于哪个“框”来移动。
scroll
(默认值)
-
行为:背景图片相对于元素本身固定,不会随元素内容的滚动而滚动。
-
通俗解释:图片被“粘”在了元素上。当你滚动元素的内容时(如果元素有滚动条),背景图不动;但当你滚动整个页面时,元素和它的背景图会一起滚动。
-
关键点:背景的定位基准是元素的边界,而不是它的内容。
示例:
一个<div>
设置了background-attachment: scroll;
,当你滚动这个div
的滚动条时,里面的文字向上移动,但背景图片保持在div
的顶部,不会随文字移动。<style>.attachment-scroll {overflow-y: auto;height: 400px;background-image: url("./NATOURS/img/nat-3-large.jpg");/* background-repeat: no-repeat; */background-attachment: scroll;background-size: cover;}.attachment-scroll p {font-size: 30px;font-weight: bold;text-align: center;color: #000;} </style><div class="attachment-scroll"><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p><p>10</p><p>11</p><p>12</p> </div>
fixed
-
行为:背景图片相对于视口(viewport)固定。
-
通俗解释:图片被“粘”在了浏览器窗口的玻璃上。无论你如何滚动页面或元素的内容,背景图片都纹丝不动,永远停留在窗口的同一个位置。
-
关键点:背景的定位基准是浏览器的视口。这会创造一种强烈的景深效果,背景仿佛在所有内容之后。
-
注意:在移动设备上,
fixed
值的行为可能不一致且支持不佳,通常表现不如桌面浏览器稳定。经典应用:创建“视差滚动”效果。页面内容在滚动,但背景图固定不动,营造出深度感和沉浸感。
background-attachment: fixed;
local
- 行为:背景图片相对于元素的内容固定。
- 通俗解释:图片被“粘”在了元素的内容上。如果你滚动元素的内容(比如一个带滚动条的盒子),背景图片会随内容一起滚动。当你滚动整个页面时,元素和它的背景内容也会一起滚动。
- 关键点:背景的定位基准是元素的内容区域。这个值是后来添加的,用于解决
scroll
无法满足的某些需求。
background-attachment: fixed;
三者对比
值 | 滚动页面时 | 滚动元素内容时(元素有滚动条) | 定位基准 |
---|---|---|---|
scroll (默认) |
背景随元素一起滚动 | 背景固定不动 | 元素本身 |
fixed |
背景固定不动 | 背景固定不动 | 浏览器视口 |
local |
背景随元素一起滚动 | 背景随内容一起滚动 | 元素的内容 |
使用background-attachment实现滚动视差
- 背景图都设定了
background-attachment: fixed
,所以背景图都是相对页面视口进行定位的。 - 通过滚动操作,改变的其实是不同的
<section>
在视口内的展示区域,通过这个区域的改变,这个区域内的背景图逐渐展现。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>使用background-attachment实现滚动视差</title><style>* {margin: 0;box-sizing: border-box;padding: 0;}section {box-sizing: border-box;height: 100vh;font-size: 20vh;line-height: 100vh;text-align: center;color: #fff;background: rgba(0, 0, 0, 0.7);background-attachment: fixed;background-position: center center;background-size: cover;}.bg-attachment1 {background-image: url("./NATOURS/img/nat-3.jpg");}.bg-attachment2 {background-image: url("./NATOURS/img/nat-4.jpg");}.bg-attachment3 {background-image: url("./NATOURS/img/nat-5.jpg");}</style></head><body><section class="bg-attachment1">IMG1</section><section class="bg-attachment2">IMG2</section><section class="bg-attachment3">IMG3</section></body>
</html>
使用 background-attachment
实现滚动阴影
像element-ui/plus的table如果左侧为固定栏,右侧列可以滚动时,会给垂直于滚动的一侧添加一个阴影,用于表明当前有元素被滚动给该滚出了可视区域,类似这样:
滚动阴影的难点在于,初始没有滚动的时候是没有阴影展现的,只有当开始滚动,阴影才会出现
需要借助 background-attachment: srcoll
和 background-attachment: local
两个属性,在滚动初始的时候,利用两层背景叠加在一起隐藏阴影背景,真正滚动的时候,将叠加的部分移走,只漏出阴影部分即可。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>使用 background-attachment 实现滚动阴影</title><style>* {margin: 0;box-sizing: border-box;padding: 0;}ul,li {list-style: none;}.container {display: flex;width: 500px;}.left-area {flex: 0 0 200px;}.right-area {display: flex;overflow-x: auto;background: linear-gradient(to right, #fff, transparent 100%),linear-gradient(to right, rgba(0, 0, 0, 0.5), transparent 100%);background-repeat: no-repeat;background-attachment: local, scroll;background-size: 100px 100%, 10px 100%;flex: 1;}.column {border: 1px solid #ccc;text-align: center;}li {border-bottom: 1px solid #ccc;min-width: 200px;white-space: nowrap;}li:last-child {border-bottom: none;}</style></head><body><div class="container"><div class="left-area"><div class="column"><div class="column-header">姓名</div><ul><li>小王</li><li>小赵</li><li>小白</li><li>小胡</li><li>小李</li></ul></div></div><div class="right-area"><div class="column"><div class="column-header">性别</div><ul><li>男</li><li>男</li><li>女</li><li>男</li><li>女</li></ul></div><div class="column"><div class="column-header">入学时间</div><ul><li>2025年9月5日17:39:32</li><li>2025年9月5日17:39:38</li><li>2025年9月5日17:39:40</li><li>2025年9月5日17:39:42</li><li>2025年9月5日17:39:44</li></ul></div><div class="column"><div class="column-header">自我介绍</div><ul><li>特别可爱,宇宙超级无敌</li><li>特别帅气,宇宙超级无敌</li><li>特别酷,宇宙超级无敌</li><li>特别牛,宇宙超级无敌</li><li>特别厉害,宇宙超级无敌</li></ul></div></div></div></body>
</html>
背景混合模式
background-blend-mode
是一个用于定义元素的背景图层如何相互混合的 CSS 属性。这里的“背景图层”指的是通过 background-image
、background-color
等属性设置的多层背景。
简单来说,它就像 Photoshop 中的图层混合模式,你可以让一张背景图片和它下面的另一张图片,或者与背景色进行融合,创造出各种视觉效果(如变暗、变亮、叠加、滤色等)。
background-blend-mode: <blend-mode>;
常用的混合模式值
混合模式主要分为以下几类,以下是几个最常用和实用的模式:
- 变暗类
normal
:默认值,正常显示,不混合。multiply
:正片叠底。将颜色值相乘,结果通常更暗。白色会变为透明,黑色保持不变。非常适合创建阴影效果或加深图像。darken
:变暗。比较混合层和基层的颜色,保留较暗的一个。
- 变亮类
screen
:滤色。将颜色值反转后相乘,再反转结果。效果通常更亮。黑色会变为透明,白色保持不变。适合创建高光效果或提亮图像。lighten
:变亮。比较混合层和基层的颜色,保留较亮的一个。
- 对比类
overlay
:叠加。结合了multiply
和screen
的特点。保留基层(下层)的明暗对比,同时混合颜色。基层的亮部区域会更亮,暗部区域会更暗,增加对比度。hard-light
:强光。类似于overlay
,但基于混合层(上层)的明度。如果上层比50%灰亮,则效果类似screen
(变亮);如果比50%灰暗,则效果类似multiply
(变暗)。
- 组分类
color
:颜色。使用混合层的色相和饱和度,以及基层的明度。非常适合为灰度图像上色。luminosity
:明度。与color
相反。使用混合层的明度,以及基层的色相和饱和度。
实用示例
示例 1:为图片添加色调
这是最常见的用法,用一张图片和一个背景色混合。
.tinted-image {background-image: url("portrait.jpg");background-color: #3498db; /* 蓝色 */background-blend-mode: multiply;/* 图片会蒙上一层蓝色调,高光部分保留,阴影部分变为蓝色 */
}
示例 2:创建双色调效果
使用两张相同的图片(比如一张原图,一张去色的),通过混合模式创造效果。
.duotone {background-image: url("portrait.jpg"), url("portrait.jpg");background-size: cover;background-color: #e74c3c; /* 红色 */background-blend-mode: luminosity, multiply;/* 解释:顶层图片(portrait.jpg)使用 luminosity 模式,只保留明度信息。下层图片(portrait.jpg)和红色背景色使用 multiply 模式混合。最终效果是:高光为红色,阴影为深红色的双色调效果。*/
}
示例 3:纹理叠加
将一张纹理图片叠加到纯色背景或另一张图片上。
.textured-box {background-image: url("paper-texture.jpg"), url("solid-color.jpg");background-blend-mode: overlay;/* 纸纹理会以 overlay 模式叠加在纯色背景上,增加质感 */
}
clip-path
clip-path
属性允许你通过定义一個裁剪区域(Clipping Region)来“剪掉”元素的某些部分。只有在裁剪区域内的内容才是可见的,区域外的内容(如内容、背景、边框等)都会被隐藏。
它可以创建出各种非矩形的视觉效果,比如圆形、椭圆形、多边形或自定义的复杂 SVG 路径形状。
inset()
- 内切矩形
定义一个矩形,类似于 border
或 padding
。你可以指定从元素各边向内偏移的距离。
语法: inset( <top> <right> <bottom> <left> round <border-radius> )
示例:
<style>.element-inset {width: 50px;height: 50px;background-color: aquamarine;clip-path: inset(5px 10px);}.element-inset-round {width: 50px;height: 50px;background-color: aquamarine;clip-path: inset(10px round 15px);}
</style><div class="element-inset"></div>
<div class="element-inset-round"></div>
circle()
- 圆形
定义一个圆形裁剪区域。
语法: circle( [ <radius> ]? [ at <position> ]? )
radius
: 圆的半径。可以使用长度(如20px
)或关键字(如closest-side
,默认值,意思为半径到最近边的距离)。at position
: 圆心的位置。默认为元素中心。
示例:
<style>.element-circle {width: 50px;height: 50px;background-color: deeppink;clip-path: circle(50%);}.element-circle2 {width: 50px;height: 50px;background-color: deeppink;clip-path: circle(closest-side at 25px 25px);}
</style><div class="element-circle"></div>
<div class="element-circle2"></div>
ellipse()
- 椭圆形
定义一个椭圆形裁剪区域。
语法: ellipse( [ <x-radius> <y-radius> ]? [ at <position> ]? )
x-radius
,y-radius
: X轴和Y轴的半径。默认为closest-side
。at position
: 椭圆中心的位置。默认为元素中心。
示例:
.element-ellipse {width: 50px;height: 50px;background-color: deepskyblue;clip-path: ellipse(10px 20px at center);
}
.element-ellipse2 {width: 50px;height: 50px;background-color: deepskyblue;clip-path: ellipse(10% 20% at center);
}
polygon()
- 多边形
通过定义一系列顶点坐标来创建任意复杂多边形。这是功能最强大的方式。
通过定义一系列顶点坐标来创建任意复杂多边形。这是功能最强大的方式。
语法: polygon( [ <fill-rule> , ]? [ <x> <y> ]# )
fill-rule
: (可选) 指定如何判断点在多边形内部还是外部,常用nonzero
(默认)和evenodd
。x
y
: 多边形的各个顶点的坐标。坐标是相对于元素自身的左上角(0,0)计算的。
示例:
<style>.element-polygon-triangle {width: 50px;height: 50px;background-color: coral;clip-path: polygon(50% 0, 100% 100%, 0 100%);}.element-polygon-diamond {width: 50px;height: 50px;background-color: hotpink;clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);}.element-polygon-bubble {width: 50px;height: 50px;background-color: cornflowerblue;clip-path: polygon(0% 0%,100% 0%,100% 75%,75% 75%,75% 100%,50% 75%,0% 75%);}
</style>
<div class="element-polygon-triangle"></div>
<div class="element-polygon-diamond"></div>
<div class="element-polygon-bubble"></div>
path()
- SVG 路径
使用 SVG 路径语法来定义极其复杂的形状。这是最灵活的方式,也是 SVG clipPath
在 CSS 中的直接对应物。
语法: path( [ <fill-rule>, ]? <string> )
string
: SVG 路径数据字符串。- 注意:
path()
的浏览器兼容性相比其他函数稍差
示例:
.element-path {width: 200px;height: 200px;background-color: burlywood;clip-path: path("M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80");
}
url()
- 引用 SVG <clipPath>
引用一个内联或外部 SVG 中的 <clipPath>
元素。这是复用复杂 SVG 形状的绝佳方式。
语法: url( #clipPathId )
示例:
<style>.element-url {width: 50px;height: 50px;background-color: tomato;clip-path: url(#myCustomClipPath);}
</style><svg width="0" height="0"><defs><clipPath id="myCustomClipPath"><circle cx="25" cy="25" r="25" /></clipPath></defs>
</svg><div class="element-url"></div>
参考框 (Reference Box)
clip-path
默认使用元素的边框盒 (Border Box) 作为坐标系参考。你可以使用 clip-path
的第二个参数来改变它:
margin-box
: 以元素的外边距框(Margin Box) 为参考。border-box
: (默认值) 以元素的边框框(Border Box) 为参考。padding-box
: 以元素的内边距框(Padding Box) 为参考。content-box
: 以元素的内容框(Content Box) 为参考。fill-box
: (用于 SVG 元素)stroke-box
: (用于 SVG 元素)view-box
: (用于 SVG 元素)
有一个如下样式的 div
:
.clip-box {margin: 20px;border: 10px solid blue;padding: 30px;width: 200px;height: 100px;line-height: 100px;text-align: center;background-color: lightcoral;/* 我们将改变这个属性的值 */clip-path: circle(25% at center); /* 默认基于 border-box */
}<div class="box">Clipping Example</div>
这个元素的盒子模型尺寸如下:
- 内容区域 (Content Box): 200 x 100 px
- 内边距区域 (Padding Box): (200 + 30**2) x (100 + 30*2) = 260 x 160 px
- 边框区域 (Border Box): (260 + 10**2) x (160 + 10*2) = 280 x 180 px
- 外边距区域 (Margin Box): (280 + 20**2) x (180 + 20*2) = 320 x 220 px
示例 1: border-box
(默认)
clip-path: circle(25% at center) border-box;
- 效果: 圆的半径是
border-box
宽度和高度的 25%。 - 计算:
- 半径 = 280px * 0.25 = 70px
- 圆心在
border-box
的中心(140px, 90px)
。
示例2:padding-box
clip-path: circle(25% at center) padding-box;
- 效果: 圆的半径是
padding-box
宽度和高度的 25%。 - 计算:
- 半径 = 260px * 0.25 = 65px
- 圆心在
padding-box
的中心(130px, 80px)
。
示例3:content-box
clip-path: circle(25% at center) content-box;
- 效果: 圆的半径是
content-box
宽度和高度的 25%。 - 计算:
- 半径 = 200px * 0.25 = 50px
- 圆心在
content-box
的中心(100px, 50px)
。
示例4:margin-box
clip-path: circle(25% at center) margin-box;
- 效果: 圆的半径是
margin-box
宽度和高度的 25%。 - 计算:
- 半径 = 320px * 0.25 = 80px
- 圆心在
margin-box
的中心(160px, 110px)
。
与 CSS 动画/过渡 (Animation/Transition) 的结合
示例:鼠标悬停时从圆形变为方形
<style>.clip-button {width: 100px;height: 50px;line-height: 50px;text-align: center;background-color: #4caf50;/* 添加过渡效果 */transition: clip-path 0.5s ease-in-out;cursor: pointer;/* 初始状态:圆形 */clip-path: circle(40% at 50% 50%);}.clip-button:hover {/* 结束状态:方形(inset矩形) */clip-path: inset(0% 0% 0% 0% round 0px); /* 也可以直接用`inset(0)` */}</style><div class="clip-button">clip me</div>
示例:动画关键帧实现多种形状变换
<style>.animated-element {width: 50px;height: 50px;background-color: #ccc;animation: morph 2s infinite ease-in-out alternate;}@keyframes morph {0% {clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);} /* 菱形 */100% {clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);} /* 矩形 */}
</style>
<div class="animated-element"></div>
过渡和动画
过渡
CSS过渡允许在CSS属性值发生变化时平滑地改变属性值,而不是立即生效。过渡可以控制动画的速度、延迟和时序函数。
/* 基本语法 */
transition: property duration timing-function delay;
过渡属性
属性 | 描述 | 示例值 |
---|---|---|
transition-property | 指定应用过渡的CSS属性 | all, width, opacity, transform |
transition-duration | 指定过渡效果持续的时间 | 0.3s, 1s, 200ms |
transition-timing-function | 定义过渡效果的速度曲线 | ease, linear, ease-in, ease-out, ease-in-out |
transition-delay | 指定过渡效果开始之前的延迟时间 | 0s, 0.5s, 1s |
过渡实例
<style>.box {width: 50px;height: 50px;background-color: aqua;transition: all 0.4s ease;}.box:hover {border-radius: 50%;background: #e74c3c;transform: rotate(45deg) scale(1.2);}
</style>
<div class="box"></div>
时序函数(Timing Functions)
时序函数定义了过渡动画的速度曲线,影响动画的加速和减速方式。
/* 使用不同的时序函数 */
.element {transition-timing-function: ease;transition-timing-function: linear;transition-timing-function: ease-in;transition-timing-function: ease-out;transition-timing-function: ease-in-out;transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
代码示例:
<style>.timing-demo {display: flex;margin: 20px 0;flex-direction: column;gap: 10px;}.timing-box {margin: 5px 0;width: 100px;height: 50px;background: #3498db;transition: all 1s;}.timing-demo:hover .timing-box {margin-left: calc(100% - 100px);}.ease {transition-timing-function: ease;}.linear {transition-timing-function: linear;}.ease-in {transition-timing-function: ease-in;}.ease-out {transition-timing-function: ease-out;}.ease-in-out {transition-timing-function: ease-in-out;}.cubic-bezier {transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);}
</style><div class="timing-demo"><div class="timing-box ease">ease(默认)</div><div class="timing-box linear">linear(线性)</div><div class="timing-box ease-in">ease-in(渐入)</div><div class="timing-box ease-out">ease-out(渐出)</div><div class="timing-box ease-in-out">ease-in-out(渐入渐出)</div><div class="timing-box cubic-bezier">cubic-bezier(贝塞尔曲线)</div>
</div>
动画
CSS 动画允许元素在不使用 JavaScript 的情况下,从一个样式配置逐渐变化到另一个样式配置。它由两个核心部分组成:关键帧 (@keyframes
) 和 动画属性 (animation-\*
)。
关键帧 (@keyframes
)
@keyframes
规则定义了动画在不同时间点上的样式状态。
语法:
@keyframes animation-name {from {/* 起始状态 (0%) */opacity: 0;}to {/* 结束状态 (100%) */opacity: 1;}
}/* 或者使用百分比来定义更复杂的过程 */
@keyframes bounce {0% {transform: translateY(0);}50% {transform: translateY(-30px);}100% {transform: translateY(0);}
}
- animation-name: 你为这个关键帧序列自定义的名称(如
fadeIn
,bounce
)。 - 关键帧选择器:
from
/0%
: 动画的起点。to
/100%
: 动画的终点。25%
,50%
...: 在动画序列中间定义的状态。百分比定义了该状态发生的时间点。
动画属性 (animation
)
将定义好的 @keyframes
应用到一个或多个元素上,并控制动画的具体行为(如持续时间、速度曲线等)。
属性 | 描述 | 示例值 |
---|---|---|
animation-name |
指定要使用的 @keyframes 名称。 |
fadeIn , bounce |
animation-duration |
必须设置。定义一个动画周期的时长。 | 1s , 200ms |
animation-timing-function |
定义动画的速度曲线,即如何完成一个周期。 | ease , linear , ease-in-out , cubic-bezier(0.1, 0.7, 1.0, 0.1) |
animation-delay |
定义动画开始前的等待时间。 | 2s , -1s (负值表示立即开始,但跳过指定时间) |
animation-iteration-count |
定义动画应播放的次数。 | 1 (默认), 3 , infinite (无限循环) |
animation-direction |
定义动画是否反向播放。 | normal (默认), alternate (正反向交替) |
animation-fill-mode |
定义动画在执行前后如何将样式应用于其目标。 | none , forwards (保留结束状态), backwards (应用起始状态), both |
animation-play-state |
允许暂停和恢复动画。 | running (默认), paused |
简写语法:
/* animation: name | duration | timing-function | delay | iteration-count | direction | fill-mode | play-state; */
.element {animation: bounce 2s ease-in-out 0.5s infinite alternate both paused;
}
重要属性
animation-timing-function
(速度曲线)
这是动画的灵魂,它决定了动画的“感觉”。
- 关键字值:
ease
: 默认值。慢开始,加速,然后慢结束。linear
: 匀速运动。ease-in
: 慢开始。ease-out
: 慢结束。ease-in-out
: 慢开始和慢结束。
cubic-bezier(n, n, n, n)
:- 贝塞尔曲线,允许你自定义速度曲线。
- 可以使用浏览器的开发者工具来调试和生成。
- 例如
cubic-bezier(0.68, -0.55, 0.27, 1.55)
可以创建一个弹跳效果。
steps(n, start|end)
:- 将动画分成 n 个等长的步进,而不是平滑过渡。
end
(默认):每一步在每段结束时发生。start
:每一步在每段开始时发生。- 非常适合制作逐帧动画或打字机效果。
animation-fill-mode
(填充模式)
控制动画非执行阶段(开始前/结束后)的样式。
none
(默认): 动画未执行时,元素使用其默认样式。forwards
: 动画完成后,元素将保留最后一帧的样式(由100%
或to
定义)。backwards
: 在动画延迟 (animation-delay
) 期间,元素将立即应用第一帧的样式(由0%
或from
定义)。both
: 同时应用forwards
和backwards
的规则。
animation-direction
(方向)
normal
: 每次迭代都从 0% -> 100% 正向播放。reverse
: 每次迭代都从 100% -> 0% 反向播放。alternate
: 奇数次正向 (normal
),偶数次反向 (reverse
)。alternate-reverse
: 奇数次反向 (reverse
),偶数次正向 (normal
)。
实例
示例 1: 淡入效果 (Fade In)
<style>.box {width: 50px;height: 50px;background-color: aquamarine;}.fade-in {animation: fadeIn 1.5s ease-out forwards;}@keyframes fadeIn {from {opacity: 0;}to {opacity: 1;}}
</style><div class="box fade-in"></div>
示例 2: 跳动效果 (Bounce)
<style>.bounce {border-radius: 50%;animation: bounce 2s infinite;}@keyframes bounce {0%,20%,50%,80%,100% {transform: translateY(0);}40% {transform: translateY(-30px);}60% {transform: translateY(-15px);}}
</style><div class="box bounce"></div>
实例3:打字机效果
<style>.typewriter {width: fit-content;height: 100px;display: flex;justify-content: center;align-items: center;margin: 20px 0;}.typewriter-text {font-weight: bold;color: #fdbb2d;overflow: hidden;border-right: 3px solid #fdbb2d;white-space: nowrap;animation: typing 4s steps(28) infinite,blink-cursor 0.7s step-end infinite;}@keyframes typing {0% {width: 0;}50% {width: 100%;}90%,100% {width: 100%;}}@keyframes blink-cursor {from,to {border-color: transparent;}50% {border-color: #fdbb2d;}}
</style><div class="typewriter"><div class="typewriter-text">CSS steps() 动画非常强大!</div>
</div>
性能与最佳实践
-
优先使用
transform
和opacity
- 更改这些属性不会触发昂贵的重排(Layout)或重绘(Paint),只会触发合成(Composition)。它们是性能最好的属性。
- 高性能属性:
transform
,opacity
,filter
- 低性能属性:
width
,height
,top
,left
,margin
,padding
等(会触发重排)
-
使用
will-change
提示浏览器如果你知道某个元素即将被动画化,可以提前告知浏览器以优化性能。
慎用:不要对所有元素都使用,这会消耗大量系统资源。只对需要复杂动画的元素使用。
.element-to-animate {will-change: transform, opacity; }
网格布局Grid
CSS Grid 布局是一个二维的布局系统,这意味着它可以同时处理行和列。Grid 允许你通过将 HTML 元素定义为一个网格容器,并将其子项目放置在预先定义或动态生成的网格单元格中,来构建复杂且响应式的布局。
概念
Grid Container(网格容器):通过设置 display: grid;
或 display: inline-grid;
的元素。它的所有直接子元素都会自动成为 Grid Items(网格项目)
Grid Item(网格项目):Grid Container 的直接子元素。孙子元素不受影响。
.container {display: grid;
}
网格容器(Grid Container)的属性
这些属性定义在容器上,用于控制整个网格的结构。
1. 定义网格轨道(行和列)
-
grid-template-columns
: 定义列的数量和宽度。grid-template-columns: 100px 200px 100px;
→ 三列,宽度分别为 100px, 200px, 100px。grid-template-columns: 1fr 2fr 1fr;
→ 三列,使用新的fr
单位(fraction,分数)。意思是“将可用空间分成 4 等份(1+2+1),第一列和第三列各占 1 份,第二列占 2 份”。grid-template-columns: repeat(3, 1fr);
→repeat()
函数,创建 3 列,每列宽度为1fr
。grid-template-columns: 200px 1fr auto;
→ 混合使用,非常灵活。
-
grid-template-rows
: 定义行的数量和高度。- 用法与
grid-template-columns
完全相同。
- 用法与
-
grid-auto-rows
/grid-auto-columns
: 定义那些被隐式创建(即超出template
定义范围)的网格轨道的大小。- 例如:你定义了
grid-template-rows: 100px;
(只有一行),但你有 10 个项目,它们创建了 10 行。后面的 9 行就是隐式网格。你可以用grid-auto-rows: 50px;
让它们都是 50px 高。
- 例如:你定义了
-
grid-auto-flow
:控制着自动放置的网格项(即没有明确指定位置的网格项)在网格中的流动方向。也就是说,当网格项没有被明确指定放在哪个网格区域时,浏览器会自动为它们分配位置,而grid-auto-flow
就决定了这个自动放置算法如何工作。-
语法:
grid-auto-flow: [ row | column ] | dense | row dense | column dense;
-
取值:
- row(默认值):自动放置算法按照行依次填充,必要时会添加新行。
- column:自动放置算法按照列依次填充,必要时会添加新列。
- dense:使用“密集”打包算法,如果后面出现较小的网格项,则尝试填充网格中较早的空隙。这可能会改变网格项的自然顺序,但可以填满网格中的空白。
- row dense:按行进行密集打包。
- column dense:按列进行密集打包。
-
如果没有设置dense,布局可能有空白,因为浏览器严格按顺序排列。如果设置dense,浏览器会尝试填充空白,布局更紧凑。
-
2. 网格间隙(Gutters)
gap
(或grid-gap
,grid-gap
是旧标准,现推荐使用gap
)row-gap
: 设置行与行之间的间隙。column-gap
: 设置列与列之间的间隙。gap: 20px 10px;
→ 第一个值是行间隙,第二个值是列间隙(类似于 margin 的简写)。
3. 网格区域和模板
-
grid-template-areas
: 通过命名网格区域来定义一种可视化的布局结构,非常直观。.container {grid-template-areas:"header header header""sidebar main main""footer footer footer"; }
然后,项目可以使用
grid-area: header;
来将自己放置到对应的命名区域。
4. 对齐内容(在网格容器内)
这些属性控制整个网格在容器内的对齐方式(当网格总尺寸小于容器时)。
justify-content
: 整个网格在水平方向上的对齐(start, end, center, stretch, space-around, space-between, space-evenly)。align-content
: 整个网格在垂直方向上的对齐(值同上)。place-content
:align-content
/justify-content
的简写形式。
网格项目(Grid Item)的属性
这些属性定义在单个网格项目上,用于控制该项目在网格中的位置。
1. 基于线的放置(Line-based Placement)
Grid 网格有编号的线(从 1 开始,而不是 0)。你可以通过指定从哪条线开始到哪条线结束来放置项目。
grid-column-start
: 项目开始的垂直网格线。grid-column-end
: 项目结束的垂直网格线。grid-row-start
: 项目开始的水平网格线。grid-row-end
: 项目结束的水平网格线。
简写形式:
grid-column: <start-line> / <end-line>;
grid-column: 1 / 3;
→ 从第1条垂直线跨越到第3条垂直线(即占据第1和第2列)。grid-column: 2 / span 2;
→ 从第2条垂直线开始,跨越2个单元格。
grid-row: <start-line> / <end-line>;
(用法同grid-column
)
2. 基于区域的放置(Area Placement)
-
grid-area
: 为项目指定一个在grid-template-areas
中定义的区域名称。css
.item {grid-area: header; }
它也可以是
grid-row-start
/grid-column-start
/grid-row-end
/grid-column-end
的简写形式:
grid-area: 1 / 1 / 3 / 2;
(行开始 / 列开始 / 行结束 / 列结束)
3. 对齐项目(在单元格内)
这些属性控制单个项目在其所在网格单元格内的对齐方式。
justify-self
: 项目在单元格内的水平对齐(start, end, center, stretch)。align-self
: 项目在单元格内的垂直对齐(值同上)。place-self
:align-self
/justify-self
的简写形式。
4. 例子
<!DOCTYPE html>
<html>
<head>
<style>
.container {display: grid;grid-template-columns: 1fr 3fr 1fr; /* 三列,中间列是两侧的三倍宽 */grid-template-rows: 80px auto 80px; /* 三行,首尾固定高度,中间自适应 */gap: 15px;height: 100vh;grid-template-areas: "header header header""sidebar main ads""footer footer footer";
}.header { grid-area: header; background: lightblue; }
.sidebar { grid-area: sidebar; background: lightgreen; }
.main { grid-area: main; background: lightyellow; }
.ads { grid-area: ads; background: lightcoral; }
.footer { grid-area: footer; background: lightgray; }/* 另一种基于线的放置方式(注释掉上面的 grid-area 规则即可启用) */
/*
.header {grid-column: 1 / 4;grid-row: 1;
}
.sidebar {grid-column: 1;grid-row: 2;
}
.main {grid-column: 2;grid-row: 2;
}
.ads {grid-column: 3;grid-row: 2;
}
.footer {grid-column: 1 / 4;grid-row: 3;
}
*/
</style>
</head>
<body><div class="container"><div class="header">Header</div><div class="sidebar">Sidebar</div><div class="main">Main Content</div><div class="ads">Ads</div><div class="footer">Footer</div>
</div></body>
</html>
综合练习1:https://codepen.io/small-windmill/pen/emJRXpZ?editors=1100
综合练习2:https://codepen.io/small-windmill/pen/MYKvOpa
min-content和max-content
min-content
- 最小内容尺寸:元素在不溢出情况下能容纳内容的最小可能宽度
- 对于文本:等于最长单词或不可断行元素的宽度
- 会尽可能收缩,让内容换行
max-content
- 最大内容尺寸:元素在不换行情况下能容纳内容的理想宽度
- 对于文本:等于所有文本在一行内显示所需的宽度
- 会尽可能扩展,避免内容换行
<style>.container {display: grid;gap: 10px;margin-bottom: 20px;width: 1000px;background-color: #ddd;}/* min-content 示例 */.min-content-example {grid-template-columns: min-content min-content min-content;}/* max-content 示例 */.max-content-example {grid-template-columns: max-content max-content max-content;}/* 固定宽度对比 */.fixed-example {grid-template-columns: 100px 100px 100px;}.item--1 {background-color: orangered;}.item--2 {background-color: yellowgreen;}.item--3 {background-color: blueviolet;}
</style><section class="grid-test"><!-- min-content-example / max-content-example / fixed-example--><div class="container max-content-example"><div class="item item--1">这是一个相对较长的文本内容</div><div class="item item--2">短文本</div><div class="item item--3">supercalifragilisticexpialidocious(超长单词)</div></div>
</section>
repeat(),minmax(), auto-fill和auto-fit
repeat()
repeat()是CSS Grid布局中的一个函数,它允许你重复一个轨道模式若干次,而不需要重复编写相同的值。
基本语法:repeat(重复次数, 轨道大小)
例如:创建4个等宽的列
.grid-container {/** grid-template-columns: 1fr 1fr 1fr 1fr; **/grid-template-columns: repeat(4, 1fr);
}
minmax()函数
minmax()函数允许你为网格轨道定义尺寸范围:
- 第一个参数是最小值
- 第二个参数是最大值
- 例如:
minmax(200px, 1fr)
表示轨道宽度至少200px,但最多不超过1fr
auto-fill
- 尽可能多地创建网格轨道,即使它们没有内容也会保留空间
- 适合需要固定网格线数量的布局
- 示例:
grid-template-columns:repeat(auto-fill,100px);
auto-fit
- 尽可能多地创建网格轨道,但会折叠空轨道并将空间分配给现有项目
- 适合内容数量可变的流体布局
- 示例:
grid-template-columns:repeat(auto-fit,100px);
区别
以下例子可以看到auto-fit和auto-fill的区别
<style>.grid-container {display: grid;margin: 12px auto;width: 1140px;column-gap: 12px;}.grid-item {background-color: deepskyblue;}.auto-fit {grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));}.auto-fill {grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));}
</style><div class="grid-container auto-fit"><div class="grid-item">测试1</div><div class="grid-item">测试2</div><div class="grid-item">测试3</div>
</div><div class="grid-container auto-fill"><div class="grid-item">测试1</div><div class="grid-item"></div><div class="grid-item">测试3</div>
</div>
例子
auto-fit / auto-fill 与minmax的搭配使用
https://codepen.io/small-windmill/pen/qEbXVwY?editors=1100
应用例子
以下是用float写的布局
<style>:root {--grid-width: 1140px;--gutter-vertical: 80px;--gutter-horizontal: 60px;}.row {margin: 0 auto;max-width: var(--grid-width);background-color: #eee;}/* 除了最后一个孩子之外的所有行 */.row:not(:last-child) {margin-bottom: var(--gutter-vertical);}/* 清除浮动 */.row::after {display: table;clear: both;content: "";}/* 以col-开头的class属性 */[class^="col-"] {float: left;background-color: orangered;}[class^="col-"]:not(:last-child) {margin-right: var(--gutter-horizontal);}.col-1-of-2 {width: calc((100% - var(--gutter-horizontal)) / 2);}.col-1-of-3 {width: calc((100% - 2 * var(--gutter-horizontal)) / 3);}.col-2-of-3 {width: calc(((100% - 2 * var(--gutter-horizontal)) / 3) * 2 +var(--gutter-horizontal));}.col-1-of-4 {width: calc((100% - 3 * var(--gutter-horizontal)) / 4);}.col-2-of-4 {width: calc(((100% - 3 * var(--gutter-horizontal)) / 4) * 2 +var(--gutter-horizontal));}.col-3-of-4 {width: calc(((100% - 3 * var(--gutter-horizontal)) / 4) * 3 +var(--gutter-horizontal) * 2);}
</style><section class="grid-test"><div class="row"><div class="col-1-of-2">Col 1 of 2</div><div class="col-1-of-2">Col 1 of 2</div></div><div class="row"><div class="col-1-of-3">Col 1 of 3</div><div class="col-1-of-3">Col 1 of 3</div><div class="col-1-of-3">Col 1 of 3</div></div><div class="row"><div class="col-1-of-3">Col 1 of 3</div><div class="col-2-of-3">Col 2 of 3</div></div><div class="row"><div class="col-1-of-4">Col 1 of 4</div><div class="col-1-of-4">Col 1 of 4</div><div class="col-1-of-4">Col 1 of 4</div><div class="col-1-of-4">Col 1 of 4</div></div><div class="row"><div class="col-1-of-4">Col 1 of 4</div><div class="col-1-of-4">Col 1 of 4</div><div class="col-2-of-4">Col 2 of 4</div></div><div class="row"><div class="col-1-of-4">Col 1 of 4</div><div class="col-3-of-4">Col 3 of 4</div></div>
</section>
如果用grid改写的话:
<style>.grid {display: grid;margin: 0 auto var(--gutter-vertical); /* 设置行间距 */max-width: var(--grid-width);background-color: #eee;grid-template-columns: repeat(12, 1fr); /* 创建12列等宽网格 */gap: var(--gutter-horizontal); /* 设置列间距 */}.grid-item {background-color: orangered;}.col-3 {grid-column: span 3;}.col-4 {grid-column: span 4;}.col-6 {grid-column: span 6;}.col-8 {grid-column: span 8;}.col-9 {grid-column: span 9;}
</style><!-- 2等分 -->
<div class="grid"><div class="grid-item col-6">1 of 2</div><div class="grid-item col-6">2 of 2</div>
</div><!-- 3等分 -->
<div class="grid"><div class="grid-item col-4">1 of 3</div><div class="grid-item col-4">2 of 3</div><div class="grid-item col-4">3 of 3</div>
</div><!-- 1/3 + 2/3 -->
<div class="grid"><div class="grid-item col-4">1 of 3</div><div class="grid-item col-8">2 of 3</div>
</div><!-- 4等分 -->
<div class="grid"><div class="grid-item col-3">1 of 4</div><div class="grid-item col-3">2 of 4</div><div class="grid-item col-3">3 of 4</div><div class="grid-item col-3">4 of 4</div>
</div><!-- 1/4 + 1/4 + 2/4 -->
<div class="grid"><div class="grid-item col-3">1 of 4</div><div class="grid-item col-3">2 of 4</div><div class="grid-item col-6">3 of 4</div>
</div><!-- 1/4 + 3/4 -->
<div class="grid"><div class="grid-item col-3">1 of 4</div><div class="grid-item col-9">3 of 4</div>
</div>
Flex布局
参考阮一峰老师的Flex布局介绍
概念
Flexbox 一次只能处理一个维度(一行或一列)的布局。这与 CSS Grid 布局(二维,同时处理行和列)形成对比。
- Flex容器:通过设置
display
属性为flex
或inline-flex
来创建。它的所有直接子元素都会成为Flex项目。 - Flex项目:Flex容器内的直接子元素。
Flex布局有两个轴:主轴和交叉轴。主轴的方向由flex-direction
属性定义,交叉轴则垂直于主轴。
Flex容器属性
display
定义容器为Flex布局。
.container {display: flex; /* 块级Flex容器 *//* 或者 */display: inline-flex; /* 行内Flex容器 */
}
flex-direction
.container {flex-direction: row; /* 默认值,主轴为水平方向,起点在左端 */flex-direction: row-reverse; /* 主轴为水平方向,起点在右端 */flex-direction: column; /* 主轴为垂直方向,起点在上沿 */flex-direction: column-reverse; /* 主轴为垂直方向,起点在下沿 */
}
flex-wrap
默认情况下,项目都排在一条轴线上。此属性定义如果一条轴线排不下,如何换行。
.container {flex-wrap: nowrap; /* 默认,不换行 */flex-wrap: wrap; /* 换行,第一行在上方 */flex-wrap: wrap-reverse; /* 换行,第一行在下方 */
}
flex-flow
是flex-direction
和flex-wrap
的简写形式,默认值为row nowrap
。
.container {flex-flow: row wrap;
}
justify-content
定义项目在主轴上的对齐方式。
.container {justify-content: flex-start; /* 默认值,左对齐 */justify-content: flex-end; /* 右对齐 */justify-content: center; /* 居中 */justify-content: space-between; /* 两端对齐,项目之间的间隔相等 */justify-content: space-around; /* 每个项目两侧的间隔相等,所以项目之间的间隔比项目与边框的间隔大一倍 */justify-content: space-evenly; /* 每个项目的间隔(包括与边框的间隔)都相等 */
}
align-items
定义项目在交叉轴上的对齐方式。
.container {align-items: stretch; /* 默认值,如果项目未设置高度或设为auto,将占满整个容器的高度 */align-items: flex-start; /* 交叉轴的起点对齐 */align-items: flex-end; /* 交叉轴的终点对齐 */align-items: center; /* 交叉轴的中点对齐 */align-items: baseline; /* 项目的第一行文字的基线对齐 */
}
align-content
定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
.container {align-content: stretch; /* 默认值,轴线占满整个交叉轴 */align-content: flex-start; /* 与交叉轴的起点对齐 */align-content: flex-end; /* 与交叉轴的终点对齐 */align-content: center; /* 与交叉轴的中点对齐 */align-content: space-between; /* 与交叉轴两端对齐,轴线之间的间隔平均分布 */align-content: space-around; /* 每根轴线两侧的间隔都相等,所以轴线之间的间隔比轴线与边框的间隔大一倍 */align-content: space-evenly; /* 每根轴线的间隔(包括与边框的间隔)都相等 */
}
align-content
生效的关键条件:
- ✅
flex-wrap: wrap
或wrap-reverse
- ✅ 实际存在多行布局(项目换行)
- ✅ 容器在交叉轴方向有明确尺寸(高度或宽度)
Flex项目属性详解
order
控制项目的排列顺序:
.item {order: 0; /* 默认值 */order: 1; /* 数值越大,排列越靠后 */order: -1; /* 数值越小,排列越靠前 */
}
flex-grow
定义项目的放大比例:
.item {flex-grow: 0; /* 默认,不放大 */flex-grow: 1; /* 等分剩余空间 */flex-grow: 2; /* 占据其他项目两倍的空间 */
}
flex-shrink
定义项目的缩小比例:
.item {flex-shrink: 1; /* 默认,空间不足时缩小 */flex-shrink: 0; /* 不缩小,保持原尺寸 */flex-shrink: 2; /* 比其他项目缩小得更快 */
}
flex-basis
定义项目在分配多余空间之前的主轴尺寸:
.item {flex-basis: auto; /* 默认,项目本来大小 */flex-basis: 200px; /* 固定宽度 */flex-basis: 20%; /* 百分比宽度 */flex-basis: content; /* 基于内容自动计算 */
}
flex
flex-grow
, flex-shrink
, flex-basis
的简写:
.item {flex: 0 1 auto; /* 默认值 */flex: 1; /* 等价于 flex: 1 1 0% */flex: auto; /* 等价于 flex: 1 1 auto */flex: none; /* 等价于 flex: 0 0 auto */flex: 0 0 200px; /* 固定200px,不放大不缩小 */
}
align-self
允许单个项目有不同于其他项目的对齐方式:
.item {align-self: auto; /* 默认,继承 align-items */align-self: flex-start;align-self: flex-end;align-self: center;align-self: baseline;align-self: stretch;
}
例子
经典水平垂直居中
<style>
.center-container {display: flex;justify-content: center;align-items: center;height: 500px;
}
.item{width:50px;height:50px;background-color:pink;
}
</style><div class="center-container"><div class="item"></div>
</div>
圣杯布局
<style>
.holy-grail {display: flex;flex-direction: column;height: 500px;background-color: #bbb;
}.main-content {display: flex;flex: 1;
}main {flex: 1;order: 2;background-color: pink;
}nav {flex: 0 0 200px;order: 1;background-color: cornflowerblue;
}aside {flex: 0 0 250px;order: 3;background-color: bisque;
}
</style><div class="holy-grail"><header>Header</header><div class="main-content"><main>Main Content</main><nav>Navigation</nav><aside>Sidebar</aside></div><footer>Footer</footer>
</div>