CSS 复习三月 30, 2026基础核心# - 基础核心 - CSS 基础与选择器 - CSS 的三种引入方式 - ```html <!-- 1. 行内样式:直接写在标签上,优先级最高但不推荐大量使用 --> <p style="color: red; font-size: 16px;">这是行内样式</p> <!-- 2. 内嵌样式:写在 <head> 的 <style> 标签里 --> <head> <style> p { color: blue; } </style> </head> <!-- 3. 外链样式(推荐!):单独的 .css 文件 --> <link rel="stylesheet" href="style.css"> ``` - 一般使用外链样式,它能让 HTML 和 CSS 分离,方便维护和缓存 - 基础选择器 - ```css /* 元素选择器 —— 选中所有该标签 */ p { color: #333; } h1 { font-size: 24px; } /* 类选择器 —— 最常用,可复用 */ .card { background: #fff; padding: 16px; } .highlight { color: orange; } /* ID 选择器 —— 唯一的,一个页面同一个 ID 只用一次 */ #header { height: 60px; } /* 通配符选择器 —— 选中所有元素,常用于重置 */ * { margin: 0; padding: 0; box-sizing: border-box; /* 第 3 天会详细讲 */ } ``` - 优先使用类选择器,ID 选择器优先级太高不好覆盖,元素选择器范围太大容易误伤 - 组合选择器 - ```css /* 后代选择器(空格)—— 选中所有后代,不管嵌套多深 */ .article p { line-height: 1.8; } /* 子代选择器(>)—— 只选直接子元素 */ .nav > li { display: inline-block; } /* 相邻兄弟选择器(+)—— 紧挨着的下一个兄弟 */ h2 + p { font-size: 18px; /* 紧跟在 h2 后面的第一个 p */ } /* 通用兄弟选择器(~)—— 后面所有的兄弟 */ h2 ~ p { color: #666; /* h2 后面的所有 p */ } ``` - ```html <div class="nav"> <li>直接子元素 ✅ 被 .nav > li 选中</li> <li>直接子元素 ✅ <li>孙子元素 ❌ 不会被 .nav > li 选中</li> <li>孙子元素 ❌ 但会被 .nav li 选中</li> </li> </div> ``` - 多选择器组合技巧 - ```css /* 并集选择器(逗号)—— 同时给多个选择器设置相同样式 */ h1, h2, h3 { font-weight: bold; color: #222; } /* 交集选择器(紧挨着写)—— 同时满足多个条件 */ p.highlight { /* 既是 p 标签,又有 highlight 类 */ background: yellow; } /* 链式类选择器 —— 同时拥有多个类 */ .btn.primary.large { /* class="btn primary large" 的元素 */ padding: 12px 24px; } ``` - 伪类选择器 - 伪类用 : 开头,表示元素的某种状态或位置 - 交互状态伪类 ```css /* 鼠标悬停 */ .btn:hover { background: #2563eb; color: white; } /* 获得焦点(键盘 Tab 到或点击输入框时) */ input:focus { border-color: #2563eb; outline: none; } /* 点击瞬间 */ .btn:active { transform: scale(0.98); } /* 已访问的链接 */ a:visited { color: #666; } ``` - 结构位置伪类 ```css /* 第一个 / 最后一个子元素 */ li:first-child { font-weight: bold; } li:last-child { border-bottom: none; } /* 第 n 个子元素 —— 最灵活的选择器 */ tr:nth-child(2n) { background: #f9f9f9; /* 偶数行加背景,实现斑马纹 */ } tr:nth-child(odd) { background: #fff; /* odd = 奇数,even = 偶数 */ } /* 前 3 个 */ li:nth-child(-n+3) { color: red; } /* 每隔 3 个 */ li:nth-child(3n) { font-weight: bold; } /* 同类型的第一个(区分标签类型) */ p:first-of-type { font-size: 18px; } /* 唯一的子元素 */ p:only-child { text-align: center; } ``` - 其他实用伪类 ```css /* 否定伪类 —— 排除某些元素 */ li:not(:last-child) { border-bottom: 1px solid #eee; /* 除了最后一个都加下划线 */ } input:not([type="submit"]) { border: 1px solid #ddd; /* 除了提交按钮外的 input */ } /* 空元素 */ div:empty { display: none; /* 隐藏没有内容的 div */ } ``` - 伪元素选择器 - 用 :: 开头,会创建一个虚拟元素插入到页面中 - ```css /* ::before 在元素内容前面插入 */ .required::before { content: "* "; color: red; } /* ::after 在元素内容后面插入 */ a.external::after { content: " ↗"; font-size: 12px; } /* 经典用法:清除浮动(了解即可) */ .clearfix::after { content: ""; display: block; clear: both; } /* 装饰性下划线 */ .fancy-title::after { content: ""; display: block; width: 60px; height: 3px; background: #2563eb; margin-top: 8px; } /* 选中文本的样式 */ ::selection { background: #2563eb; color: white; } /* 输入框占位符 */ input::placeholder { color: #aaa; font-style: italic; } ``` - ::before 和 ::after 必须有 content 属性,哪怕是空字符串 "",否则不会显示。 - 属性选择器 - 根据 HTML 属性来选择元素,在表单样式中特别好用: - ```css /* 精确匹配 */ input[type="text"] { border: 1px solid #ddd; } input[type="password"] { letter-spacing: 4px; } /* 开头匹配 ^= */ a[href^="https"] { color: green; /* https 开头的链接标绿 */ } /* 结尾匹配 $= */ a[href$=".pdf"] { color: red; /* PDF 链接标红 */ } /* 包含匹配 *= */ img[src*="avatar"] { border-radius: 50%; /* src 中含 avatar 的图片变圆 */ } /* 有这个属性就选中 */ [disabled] { opacity: 0.5; cursor: not-allowed; } ``` - 优先级(权重)计算 - 当多条规则冲突时,浏览器按优先级决定谁生效 - ```css !important → 10000(尽量不用) 行内样式 style="" → 1000 ID 选择器 #id → 100 类/伪类/属性选择器 → 10 元素/伪元素选择器 → 1 通配符 * → 0 ``` - ```css /* 权重:0-0-1(一个元素) */ p { color: black; } /* 权重:0-1-0(一个类) */ .text { color: blue; } /* 权重:0-1-1(一个类 + 一个元素) */ p.text { color: green; } /* 权重:1-0-0(一个 ID)→ 这条赢 */ #intro { color: red; } /* 权重:0-2-1(两个类 + 一个元素) */ .card .content p { color: gray; } ``` - 盒模型深入 - 盒模型四层结构 -  - ```css .box { /* 内容区 */ width: 200px; height: 100px; /* 内边距 —— 内容到边框的呼吸空间 */ padding: 16px; /* 四边相同 */ padding: 16px 24px; /* 上下 16 左右 24 */ padding: 8px 16px 24px; /* 上 8 左右 16 下 24 */ padding: 8px 16px 24px 32px;/* 上 右 下 左(顺时针) */ /* 边框 */ border: 1px solid #ddd; border-radius: 8px; /* 圆角 */ /* 外边距 —— 盒子与其他元素的间距 */ margin: 20px; margin: 0 auto; /* 水平居中的经典写法 */ } ``` - padding 和 margin 的简写顺序是顺时针:上 → 右 → 下 → 左。两个值时是"上下"和"左右",三个值是"上""左右""下"。 - box-sizing:最重要的一个属性 - ```css /* 默认值:width 只算内容区 */ .content-box { box-sizing: content-box; width: 200px; padding: 20px; border: 2px solid #000; /* 实际占据宽度 = 200 + 20*2 + 2*2 = 244px 😰 */ } /* 推荐值:width 包含 padding 和 border */ .border-box { box-sizing: border-box; width: 200px; padding: 20px; border: 2px solid #000; /* 实际占据宽度 = 200px 🎉 内容区自动缩小为 156px */ } ``` - content-box 的计算方式违反直觉,设了 width: 200px 结果实际更宽。 - 所以现代开发第一行永远是: ```css /* 全局重置 —— 背下来,每个项目都加 */ *, *::before, *::after { box-sizing: border-box; } ``` - margin 合并 - 相邻兄弟的 margin 合并 ```css .box-a { margin-bottom: 30px; } .box-b { margin-top: 20px; } /* 两者之间的间距不是 50px,而是 30px(取较大值) */ ``` - 父子元素的 margin 穿透 ```html <div class="parent"> <div class="child">子元素</div> </div> <style> .parent { background: #f0f0f0; } .child { margin-top: 30px; } /* 这个 margin 会"穿透"到父元素外面!父元素整体下移 30px */ </style> ``` - 解决办法 ```css /* 方法 1:给父元素加 overflow */ .parent { overflow: hidden; } /* 方法 2:给父元素加 padding 代替子元素的 margin */ .parent { padding-top: 30px; } /* 方法 3:给父元素加 border(哪怕是透明的) */ .parent { border-top: 1px solid transparent; } /* 方法 4(最推荐):用 Flexbox,自动避免合并 */ .parent { display: flex; flex-direction: column; } ``` - 为了避免 margin 合并带来的混乱,养成一个好习惯:统一只用一个方向的 margin。推荐只用 margin-bottom,不要上下混用 ```css /* ✅ 好习惯:统一用 margin-bottom */ h2 { margin-bottom: 12px; } p { margin-bottom: 16px; } /* ❌ 坏习惯:上下混用 */ h2 { margin-bottom: 12px; } p { margin-top: 20px; } /* 和 h2 的 bottom 合并了,不直观 */ ``` - display 属性 ```css /* block —— 独占一行,可以设宽高 */ /* 默认就是 block 的标签:div, p, h1~h6, ul, li, section... */ .block-el { display: block; width: 300px; /* ✅ 生效 */ height: 100px; /* ✅ 生效 */ margin: 20px; /* ✅ 四个方向都生效 */ } /* inline —— 和文字一起排列,不能设宽高 */ /* 默认就是 inline 的标签:span, a, strong, em, img... */ .inline-el { display: inline; width: 300px; /* ❌ 无效 */ height: 100px; /* ❌ 无效 */ margin-top: 20px; /* ❌ 上下 margin 无效 */ padding: 10px; /* ⚠️ 左右有效,上下视觉上有但不影响布局 */ } /* inline-block —— 既能和文字一行排列,又能设宽高 */ /* 两全其美,适合按钮、标签等小组件 */ .inline-block-el { display: inline-block; width: 120px; /* ✅ 生效 */ height: 40px; /* ✅ 生效 */ margin: 10px; /* ✅ 四个方向都生效 */ } ``` - 常用技巧:把 <a> 或 <span> 改成 inline-block,就可以给它们设置宽高和完整的 padding/margin 了。 ```css /* 经典用例:inline-block 做按钮 */ .tag { display: inline-block; padding: 4px 12px; background: #e8f4fd; color: #1a73e8; border-radius: 4px; font-size: 13px; } ``` - border 的用法 - ```css /* 基础语法:宽度 样式 颜色 */ .box { border: 1px solid #ddd; } /* 单侧边框 */ .section { border-left: 3px solid #2563eb; padding-left: 12px; } /* 圆角 */ .card { border-radius: 8px; } /* 四角相同 */ .avatar { border-radius: 50%; } /* 正圆(需要宽高相等) */ .pill { border-radius: 9999px; } /* 胶囊形状 */ /* 只圆上面两个角 */ .tab { border-radius: 8px 8px 0 0; } /* 虚线 / 点线 */ .dashed { border: 2px dashed #aaa; } .dotted { border: 1px dotted #aaa; } /* outline —— 不占空间的"边框",常用于焦点指示 */ button:focus { outline: 2px solid #2563eb; outline-offset: 2px; } ``` - 文档流与定位 - 正常文档流 -  - 正常文档流中的元素互相"知道"彼此的存在,会自动避让 - 接下来要学的定位方式,有些会脱离文档流,让元素"飘起来",不再影响其他元素的排列 - position 五种定位方式 - static 默认值 - ```css .box { position: static; /* 默认就是这个,不用写 */ /* top/left/right/bottom 无效 */ /* z-index 无效 */ } ``` - relative 相对定位 - ```css .box { position: relative; top: 20px; /* 相对原位向下移 20px */ left: 30px; /* 相对原位向右移 30px */ } ``` -  - relative 单独用的场景不多,它最大的价值是给 absolute 子元素提供定位参考点。 - absolute 绝对定位(重点) - 脱离文档流,相对于最近的有定位属性的祖先元素来定位 - ```css /* 经典搭配:父 relative + 子 absolute */ .parent { position: relative; /* 成为定位参考点 */ width: 400px; height: 300px; } .child { position: absolute; top: 0; right: 0; /* 贴在父元素的右上角 */ /* 这个子元素已经脱离文档流,不占原来的空间 */ } ``` - 如果找不到有定位属性的祖先,就相对于整个页面(<html>)定位。 - 常见用法 ```css /* 右上角的徽标 / 角标 */ .avatar-wrap { position: relative; display: inline-block; } .badge { position: absolute; top: -4px; right: -4px; width: 18px; height: 18px; background: red; color: white; font-size: 12px; text-align: center; line-height: 18px; border-radius: 50%; } /* 绝对定位实现水平垂直居中 */ .centered { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } /* 铺满父元素(做遮罩层常用) */ .overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; /* 等价于简写:inset: 0; */ background: rgba(0, 0, 0, 0.5); } ``` - fixed - 脱离文档流,相对于浏览器视口定位,滚动页面也不动: - ```css /* 固定顶部导航 */ .navbar { position: fixed; top: 0; left: 0; right: 0; /* left + right = 0 让它撑满整个宽度 */ height: 60px; background: white; z-index: 100; /* 确保在最上层 */ } /* 别忘了给 body 加上 padding,防止内容被导航遮住 */ body { padding-top: 60px; } /* 右下角的回到顶部按钮 */ .back-to-top { position: fixed; bottom: 30px; right: 30px; width: 44px; height: 44px; } ``` - sticky 粘性定位(现代常用) - 结合了 relative 和 fixed 的效果——正常情况下跟着滚动,滚到指定位置后就"粘"住 - 父元素不能设 overflow: hidden 或 overflow: auto,否则 sticky 会失效。 - ```css /* 表格的表头粘在顶部 */ thead th { position: sticky; top: 0; background: white; } /* 侧边栏跟着滚到一定位置后固定 */ .sidebar { position: sticky; top: 80px; /* 距顶部 80px 时粘住 */ } /* 分组列表的标题吸顶 */ .group-title { position: sticky; top: 0; background: #f5f5f5; padding: 8px 16px; font-weight: bold; } ``` - z-index 层叠上下文 - 当元素重叠时,z-index 控制谁在上面 - ```css /* z-index 只对有定位属性的元素生效(非 static) */ .layer-back { position: relative; z-index: 1; } .layer-middle { position: relative; z-index: 10; } .layer-front { position: relative; z-index: 100; } ``` - 分层规范 ```css /* 建议给项目定一套层级规范,避免数字乱飞 */ :root { --z-dropdown: 100; /* 下拉菜单 */ --z-sticky: 200; /* 粘性元素 */ --z-navbar: 300; /* 导航栏 */ --z-overlay: 400; /* 遮罩层 */ --z-modal: 500; /* 弹窗 */ --z-toast: 600; /* 提示消息 */ } .modal { z-index: var(--z-modal); } .overlay { z-index: var(--z-overlay); } .navbar { z-index: var(--z-navbar); } ``` - z-index 不是全局比较的。每个设置了定位 + z-index 的元素会创建一个层叠上下文,子元素的 z-index 只在父元素内部比较: - 浮动 float (了解即可) - 浮动是 CSS2 时代的布局方式,现在已经被 Flexbox 和 Grid 替代 - ```css /* 文字环绕图片 —— float 唯一还在用的场景 */ .article img { float: left; margin-right: 16px; margin-bottom: 8px; } ``` - 如果遇到浮动导致父元素高度塌陷的问题,用学过的 ::after 清除浮动或者直接改用 Flexbox 就好 - 颜色与背景 - 颜色表示法 - HEX(十六进制) ```css /* 6 位写法:#RRGGBB */ .box { color: #2563eb; } /* 3 位简写:当每组两位相同时 */ .box { color: #fff; } /* 等价于 #ffffff */ .box { color: #333; } /* 等价于 #333333 */ /* 8 位写法:带透明度 #RRGGBBAA */ .box { background: #2563eb80; } /* 50% 透明度 */ ``` - HEX 是最常见的写法,设计稿给的颜色值通常就是 HEX。缺点是不直观,看不出颜色长什么样。 - RGB/RGBA - ```css /* rgb(红, 绿, 蓝) 每个值 0~255 */ .box { color: rgb(37, 99, 235); } /* rgba 加一个透明度参数 0~1 */ .box { background: rgba(37, 99, 235, 0.5); } /* 50% 透明 */ /* 现代写法:直接在 rgb 里加透明度,用 / 分隔 */ .box { background: rgb(37 99 235 / 0.5); } ``` - HSL / HSLA(推荐学会) - ```css /* hsl(色相, 饱和度, 亮度) */ /* 色相 H:0~360 的色环角度 0/360 = 红,120 = 绿,240 = 蓝 饱和度 S:0% = 灰色,100% = 纯色 亮度 L:0% = 黑色,50% = 正常,100% = 白色 */ .box { color: hsl(220, 85%, 53%); } /* 蓝色 */ .box { color: hsl(220, 85%, 53%, 0.5); } /* 半透明蓝 */ ``` - ```css /* 同一个蓝色,只改亮度就能得到一组深浅色 */ .blue-50 { background: hsl(220, 85%, 97%); } /* 极浅蓝背景 */ .blue-100 { background: hsl(220, 85%, 90%); } /* 浅蓝 */ .blue-500 { background: hsl(220, 85%, 53%); } /* 主色 */ .blue-700 { background: hsl(220, 85%, 35%); } /* 深蓝 */ .blue-900 { background: hsl(220, 85%, 20%); } /* 极深蓝文字 */ /* 只改色相就能切换颜色 */ .red { color: hsl(0, 85%, 53%); } .green { color: hsl(120, 85%, 35%); } .purple { color: hsl(270, 85%, 53%); } ``` - 命名颜色 - ```css .box { color: red; } .box { background: transparent; } /* 透明,这个经常用 */ .box { color: currentColor; } /* 继承当前元素的 color 值 */ ``` - opacity 与 RGBA 透明的区别 ```css /* opacity:整个元素(包括文字和子元素)都变透明 */ .card { background: #2563eb; color: white; opacity: 0.5; /* 背景、文字、子元素全部变成 50% 透明 😰 */ } /* rgba / hsla:只让颜色本身透明 */ .card { background: rgba(37, 99, 235, 0.5); color: white; /* 只有背景半透明,文字完全清晰 ✅ */ } ``` - 背景属性 - background-color ```css .box { background-color: #f5f5f5; } ``` - 渐变背景(重点) ```css /* 线性渐变 */ .banner { background: linear-gradient(to right, #667eea, #764ba2); } /* 指定角度 */ .banner { background: linear-gradient(135deg, #667eea, #764ba2); } /* 多色渐变 */ .rainbow { background: linear-gradient( 90deg, #ff6b6b, #feca57, #48dbfb, #ff9ff3 ); } /* 控制颜色位置 */ .sharp { background: linear-gradient( 90deg, #2563eb 0%, #2563eb 50%, /* 蓝色占前 50% */ #f59e0b 50%, /* 黄色从 50% 开始,形成硬分界 */ #f59e0b 100% ); } /* 径向渐变(从中心向外) */ .spotlight { background: radial-gradient(circle, #fff, #e2e8f0); } /* 椭圆渐变 + 偏移中心 */ .glow { background: radial-gradient( ellipse at top left, rgba(37, 99, 235, 0.2), transparent 60% ); } ``` - 背景图片 ```css .hero { /* 基础用法 */ background-image: url('hero.jpg'); /* 不重复 */ background-repeat: no-repeat; /* 覆盖整个容器(可能裁切) */ background-size: cover; /* 完全显示(可能留白) */ background-size: contain; /* 居中显示 */ background-position: center; /* 滚动时背景固定(视差效果) */ background-attachment: fixed; /* 简写(推荐记住这个万能组合) */ background: url('hero.jpg') no-repeat center / cover; } ``` - 多重背景叠加 ```css /* 渐变叠加在图片上 —— 做图文卡片的经典手法 */ .card-with-image { background: linear-gradient( to bottom, transparent 40%, rgba(0, 0, 0, 0.7) 100% ), url('photo.jpg') no-repeat center / cover; color: white; } /* 装饰性渐变叠加 */ .fancy-bg { background: radial-gradient(ellipse at top left, rgba(37, 99, 235, 0.15), transparent 50%), radial-gradient(ellipse at bottom right, rgba(245, 158, 11, 0.15), transparent 50%), #fafafa; } ``` - 配色技巧 ```css /* 一个页面通常只需要这几种颜色 */ :root { --color-primary: hsl(220, 85%, 53%); /* 主色:按钮、链接 */ --color-primary-light: hsl(220, 85%, 95%);/* 主色浅版:背景 */ --color-primary-dark: hsl(220, 85%, 40%); /* 主色深版:悬停 */ --color-gray-50: hsl(220, 10%, 98%); /* 页面背景 */ --color-gray-200: hsl(220, 10%, 88%); /* 边框、分隔线 */ --color-gray-500: hsl(220, 10%, 50%); /* 次要文字 */ --color-gray-900: hsl(220, 10%, 12%); /* 主文字 */ --color-success: hsl(145, 65%, 42%); /* 成功 */ --color-warning: hsl(40, 95%, 55%); /* 警告 */ --color-danger: hsl(0, 75%, 55%); /* 错误 */ } /* 使用变量 */ .btn-primary { background: var(--color-primary); color: white; } .btn-primary:hover { background: var(--color-primary-dark); } ``` - 字体与文字排版 - font-family 字体栈 - ```css /* 字体栈:从左到右依次尝试,找到第一个可用的就使用 */ body { font-family: "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif; } ``` - ```css font-family: "PingFang SC", /* macOS 上的中文字体 */ "Microsoft YaHei", /* Windows 上的中文字体 */ "Helvetica Neue", /* macOS 上的英文字体 */ Arial, /* Windows 上的英文字体 */ sans-serif; /* 兜底:让系统选一个无衬线字体 */ ``` - 引入 google fonts - ```html <!-- 在 HTML 的 <head> 中引入 --> <link href="https://fonts.googleapis.cn/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet"> ``` - ```css body { font-family: "Noto Sans SC", sans-serif; } ``` -系统字体栈(零加载延迟) ```css /* 直接使用用户系统里的字体,不需要下载 */ body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans SC", sans-serif; } /* 代码用等宽字体 */ code, pre { font-family: "Fira Code", "JetBrains Mono", "Source Code Pro", Consolas, monospace; } ``` - font-size 与单位 - ```css /* px —— 绝对单位,固定大小 */ h1 { font-size: 32px; } /* em —— 相对于父元素的 font-size */ .parent { font-size: 16px; } .child { font-size: 1.5em; } /* = 16 × 1.5 = 24px */ /* em 的问题:层层嵌套时会复利式叠加 */ .child .grandchild { font-size: 1.5em; } /* = 24 × 1.5 = 36px 😰 */ /* rem —— 相对于根元素(html)的 font-size ⭐ 推荐 */ html { font-size: 16px; } /* 浏览器默认就是 16px */ h1 { font-size: 2rem; } /* = 16 × 2 = 32px */ h2 { font-size: 1.5rem; } /* = 16 × 1.5 = 24px */ p { font-size: 1rem; } /* = 16px */ ``` - ```css /* px —— 绝对单位,固定大小 */ h1 { font-size: 32px; } /* em —— 相对于父元素的 font-size */ .parent { font-size: 16px; } .child { font-size: 1.5em; } /* = 16 × 1.5 = 24px */ /* em 的问题:层层嵌套时会复利式叠加 */ .child .grandchild { font-size: 1.5em; } /* = 24 × 1.5 = 36px 😰 */ /* rem —— 相对于根元素(html)的 font-size ⭐ 推荐 */ html { font-size: 16px; } /* 浏览器默认就是 16px */ h1 { font-size: 2rem; } /* = 16 × 2 = 32px */ h2 { font-size: 1.5rem; } /* = 16 × 1.5 = 24px */ p { font-size: 1rem; } /* = 16px */ ``` - 记住一个原则:font-size 用 rem,padding/margin 等间距可以用 rem 或 px。 - rem 的好处是后面做响应式时,只需要改 html 的 font-size 就能整体缩放。 - font 相关属性大全 - ```css .text { /* 字号 */ font-size: 16px; /* 字重:100~900,或 normal(400) / bold(700) */ font-weight: 400; /* 正常 */ font-weight: 700; /* 加粗 */ /* 风格 */ font-style: normal; /* 正常 */ font-style: italic; /* 斜体 */ /* 简写(顺序固定:style weight size/line-height family) */ font: italic 700 18px/1.6 "Noto Sans SC", sans-serif; } ``` - 一般只用 400(正文)和 700(标题/强调)两种就够了,最多加一个 500(中等) - 行高与文本间距 - ```css .article { /* line-height:行高,控制行与行之间的距离 */ line-height: 1.8; /* 无单位写法(推荐)= font-size × 1.8 */ line-height: 28px; /* 固定值写法 */ /* letter-spacing:字间距 */ letter-spacing: 0.5px; /* 中文正文加一点字间距更透气 */ letter-spacing: 2px; /* 大标题可以拉大 */ letter-spacing: -0.5px; /* 紧凑排列 */ /* word-spacing:词间距(英文用) */ word-spacing: 2px; } ``` - ```css /* 正文:1.6 ~ 1.8,中文偏大更舒适 */ .body-text { line-height: 1.7; } /* 标题:1.2 ~ 1.4,紧凑一些更有力 */ h1, h2, h3 { line-height: 1.3; } /* 按钮/标签:等于 height 实现垂直居中 */ .btn { height: 40px; line-height: 40px; /* 文字垂直居中 */ } /* UI 组件:1.4 ~ 1.5 */ .card p { line-height: 1.5; } ``` - 文本对齐与装饰 - ```css /* 水平对齐 */ .left { text-align: left; } .center { text-align: center; } .right { text-align: right; } .justify { text-align: justify; } /* 两端对齐,中文排版常用 */ /* 文本装饰 */ a { text-decoration: none; } /* 去掉链接下划线 */ .del { text-decoration: line-through; } /* 删除线 */ .underline { text-decoration: underline; text-decoration-color: #2563eb; /* 下划线颜色 */ text-underline-offset: 4px; /* 下划线和文字的距离 */ text-decoration-thickness: 2px; /* 下划线粗细 */ } /* 文本转换 */ .upper { text-transform: uppercase; } /* 全大写 */ .lower { text-transform: lowercase; } /* 全小写 */ .cap { text-transform: capitalize; } /* 首字母大写 */ /* 文本缩进(中文段落首行缩进) */ .cn-paragraph { text-indent: 2em; } /* 两个字的宽度 */ ``` - 文本溢出管理 - ```css /* 单行文本溢出显示省略号 —— 三件套必须一起用 */ .ellipsis { white-space: nowrap; /* 不换行 */ overflow: hidden; /* 超出隐藏 */ text-overflow: ellipsis; /* 显示省略号 */ } /* 多行文本溢出(限制 3 行) */ .line-clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; /* 最多显示 3 行 */ overflow: hidden; } /* white-space 的几个值 */ .nowrap { white-space: nowrap; } /* 强制不换行 */ .pre { white-space: pre; } /* 保留空格和换行(像 <pre>) */ .prewrap { white-space: pre-wrap; } /* 保留空格换行,但允许自动折行 */ /* overflow-wrap:长单词/URL 强制断行 */ .break-word { overflow-wrap: break-word; /* 旧写法:word-break: break-all; 也能用但更暴力 */ } ``` 布局体系# - 布局体系 - Flex 基础与主轴 - Flex 容器与项目 - ```html <!-- 容器(父元素):加 display: flex 的那个 --> <div class="container"> <!-- 项目(子元素):容器的直接子元素自动成为 flex 项目 --> <div class="item">A</div> <div class="item">B</div> <div class="item">C</div> </div> ``` - ```css .container { display: flex; /* 这一行就开启了 Flexbox */ } ``` -  - inline-flex,和 flex 唯一的区别是容器本身变成行内元素 - ```css /* flex:容器是块级元素,独占一行 */ .container { display: flex; } /* inline-flex:容器是行内元素,可以和其他内容并排 */ .badge-group { display: inline-flex; } ``` - 主轴与交叉轴 -  - justify-content 控制主轴,align-items 控制交叉轴。主轴方向由 flex-direction 决定。 - flex-direction 主轴方向 - ```css .container { display: flex; /* 四个方向 */ flex-direction: row; /* 默认:从左到右 → */ flex-direction: row-reverse; /* 从右到左 ← */ flex-direction: column; /* 从上到下 ↓ */ flex-direction: column-reverse; /* 从下到上 ↑ */ } ``` - ```css /* 水平导航 */ .nav { display: flex; flex-direction: row; } /* 垂直堆叠的表单 */ .form { display: flex; flex-direction: column; } /* 移动端常见:图标在上文字在下 */ .tab-item { display: flex; flex-direction: column; align-items: center; } ``` - justify-content 主轴对齐(重点) - ```css .container { display: flex; justify-content: flex-start; /* 默认:靠起始端 */ justify-content: flex-end; /* 靠末端 */ justify-content: center; /* 居中 */ justify-content: space-between; /* 两端对齐,中间等分 */ justify-content: space-around; /* 每个项目两侧等间距 */ justify-content: space-evenly; /* 所有间距完全相等 */ } ``` -  - 常用场景 ```css /* 导航栏:logo 在左,链接在右 */ .navbar { display: flex; justify-content: space-between; } /* 按钮组居中 */ .button-group { display: flex; justify-content: center; } /* 底部 Tab 栏等分 */ .tab-bar { display: flex; justify-content: space-evenly; } /* 表单按钮靠右 */ .form-actions { display: flex; justify-content: flex-end; } ``` - flex-wrap - 默认情况下所有项目挤在一行,即使溢出也不换行 - ```css .container { display: flex; flex-wrap: nowrap; /* 默认:不换行,项目可能被压缩 */ flex-wrap: wrap; /* 放不下就换行 */ flex-wrap: wrap-reverse; /* 换行但方向反转(新行在上面) */ } ``` - flex-wrap: wrap 配合固定宽度的项目可以实现简单的网格效果 ```css /* 简易卡片网格 */ .card-list { display: flex; flex-wrap: wrap; gap: 16px; } .card { width: 300px; /* 固定宽度,放不下自动换行 */ } ``` - flex-flow 简写 - ```css /* 简写:flex-flow: <direction> <wrap> */ .container { flex-flow: row wrap; /* 水平排列 + 允许换行 */ flex-flow: column nowrap; /* 垂直排列 + 不换行 */ } /* 等价于分开写 */ .container { flex-direction: row; flex-wrap: wrap; } ``` - gap 间距 - gap 是控制 Flex 项目之间间距的最佳方式,比给每个项目加 margin 干净得多 - ```css .container { display: flex; gap: 16px; /* 行列间距都是 16px */ gap: 16px 24px; /* 行间距 16px,列间距 24px */ row-gap: 16px; /* 只设行间距 */ column-gap: 24px; /* 只设列间距 */ } ``` - gap 相比 margin 的优势 ```css /* ❌ 用 margin 的痛苦:最后一个元素多出右边距 */ .item { margin-right: 16px; } .item:last-child { margin-right: 0; } /* 还得手动去掉 */ /* ✅ 用 gap:自动只在项目之间加间距,边缘没有 */ .container { display: flex; gap: 16px; } ``` - Flexbox 交叉轴与项目属性 - align-items 交叉轴对齐(重点) - justify-content 管主轴,align-items 管交叉轴。当主轴是水平方向时,交叉轴就是垂直方向 - ```css .container { display: flex; height: 200px; /* 容器要有高度才能看出交叉轴效果 */ align-items: stretch; /* 默认:项目拉伸撑满容器高度 */ align-items: flex-start; /* 顶部对齐 */ align-items: flex-end; /* 底部对齐 */ align-items: center; /* 垂直居中 */ align-items: baseline; /* 按文字基线对齐 */ } ``` -  - align-self 单个项目的对齐 - align-items 是容器属性,控制所有项目。align-self 写在项目上,让单个项目有不同的对齐方式 - ```css .container { display: flex; align-items: flex-start; /* 所有项目顶部对齐 */ height: 200px; } /* 只让第三个项目底部对齐 */ .item-c { align-self: flex-end; } /* 只让第二个项目居中 */ .item-b { align-self: center; } ``` - align-content 多行对齐 - align-content 只在 flex-wrap: wrap 产生多行时才有效果,它控制的是多行整体在交叉轴上的分布 - ```css .container { display: flex; flex-wrap: wrap; height: 400px; align-content: flex-start; /* 所有行挤在顶部 */ align-content: flex-end; /* 所有行挤在底部 */ align-content: center; /* 所有行居中 */ align-content: space-between; /* 首行贴顶末行贴底,中间等分 */ align-content: space-around; /* 每行上下等间距 */ align-content: stretch; /* 默认:行高拉伸填满容器 */ } ``` -  - Flex 项目属性(核心) - 写在子元素上的三个属性,控制项目如何分配空间 - flex-basis 基础尺寸 - ```css .item { /* flex-basis 定义项目在分配多余空间之前的初始大小 */ flex-basis: auto; /* 默认:使用 width 或内容宽度 */ flex-basis: 200px; /* 固定初始宽度 200px */ flex-basis: 30%; /* 初始占 30% */ flex-basis: 0; /* 初始宽度为 0,完全由 flex-grow 决定 */ } ``` - flex-basis 和 width 的关系:当两者同时存在时,flex-basis 优先级更高(主轴为水平时) - 用了 Flexbox 后建议统一用 flex-basis 代替 width。 - flex-grow 放大比例 - 当容器有剩余空间时,flex-grow 决定每个项目分到多少: - ```css /* 默认 flex-grow: 0,不放大 */ /* 三个项目各自宽 100px,容器宽 600px,剩余 300px */ .item-a { flex-grow: 1; } /* 分到 300 × 1/3 = 100px → 总宽 200px */ .item-b { flex-grow: 1; } /* 分到 300 × 1/3 = 100px → 总宽 200px */ .item-c { flex-grow: 1; } /* 分到 300 × 1/3 = 100px → 总宽 200px */ ``` - 常见用法 ```css /* 让某个项目占满剩余空间 */ .sidebar { width: 250px; } /* 固定宽度 */ .main { flex-grow: 1; } /* 占满剩余 */ /* 搜索框:按钮固定宽,输入框自适应 */ .search-bar { display: flex; } .search-input { flex-grow: 1; } /* 输入框占满剩余空间 */ .search-btn { width: 80px; } /* 按钮固定宽 */ ``` - flex-shrink 缩小比例 - 当容器空间不足时,flex-shrink 决定每个项目被压缩多少 - ```css /* 默认 flex-shrink: 1,所有项目等比缩小 */ /* 不想被压缩(比如侧边栏固定宽度) */ .sidebar { flex-shrink: 0; /* 我不缩! */ width: 250px; } .main { flex-shrink: 1; /* 空间不够时我来缩 */ } ``` - flex 简写 ⭐ 必须掌握 - ```css /* flex: grow shrink basis */ 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,不伸不缩 */ ``` - flex: 1 vs flex: auto 的区别 - ```css /* flex: 1 → flex: 1 1 0% */ /* basis 为 0,忽略内容宽度,纯按 grow 比例分配 */ /* flex: auto → flex: 1 1 auto */ /* basis 为 auto,先考虑内容宽度,再按 grow 比例分配剩余空间 */ ``` - 大多数情况用 flex: 1 就对了,想要项目完全等宽就用它。 - 经典布局模式 - ```css /* 模式 1:水平垂直居中(最经典的 Flex 用法) */ .center-box { display: flex; justify-content: center; align-items: center; } /* 模式 2:左右两端对齐 + 垂直居中(导航栏) */ .navbar { display: flex; justify-content: space-between; align-items: center; } /* 模式 3:固定侧边栏 + 自适应主内容 */ .layout { display: flex; } .sidebar { flex: 0 0 250px; } /* 固定 250px */ .main { flex: 1; } /* 占满剩余 */ /* 模式 4:底部固定 Footer(页面不够高时 Footer 也贴底) */ .page { display: flex; flex-direction: column; min-height: 100vh; } .page-header { /* 自然高度 */ } .page-main { flex: 1; } /* 撑满中间空间 */ .page-footer { /* 自然高度 */ } /* 模式 5:左图右文(或左文右图) */ .media { display: flex; align-items: flex-start; /* 顶部对齐,不拉伸 */ gap: 16px; } .media-image { flex: 0 0 120px; } /* 图片固定宽 */ .media-body { flex: 1; } /* 文字占满 */ ``` - Grid 基础与轨道定义 - Grid 容器与基本概念 - ```css .container { display: grid; /* 开启 Grid 布局 */ } ``` -  - grid-template-columns 定义列 - ```css /* 固定宽度 */ .grid { display: grid; grid-template-columns: 200px 300px 200px; /* 三列:200 + 300 + 200 */ } /* 百分比 */ .grid { grid-template-columns: 25% 50% 25%; } /* fr 单位 —— 按比例分配剩余空间 ⭐ */ .grid { grid-template-columns: 1fr 2fr 1fr; /* 总共 4fr,第一列 1/4,第二列 2/4,第三列 1/4 */ } /* 混合使用 */ .grid { grid-template-columns: 250px 1fr 200px; /* 两侧固定,中间自适应 —— 比 Flex 更简洁! */ } ``` - `fr` 是 Grid 独有的单位,表示 fraction(份)。它会在固定尺寸分配完之后,按比例分配剩余空间 - grid-template-rows 定义行 - ```css .grid { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 80px 1fr 60px; /* 三行:头 80px + 中间自适应 + 底 60px */ } /* 行通常不需要显式定义,让内容撑开即可 */ .grid { grid-template-columns: 1fr 1fr 1fr; /* 不写 grid-template-rows,行高由内容决定 */ } ``` - repeat() 简化重复定义 - ```css /* 写 12 列等宽 */ /* ❌ 繁琐写法 */ grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; /* ✅ repeat 写法 */ grid-template-columns: repeat(12, 1fr); /* 重复模式 */ grid-template-columns: repeat(3, 1fr 2fr); /* 等价于:1fr 2fr 1fr 2fr 1fr 2fr(6 列) */ /* 和固定列混合 */ grid-template-columns: 200px repeat(3, 1fr) 200px; /* 等价于:200px 1fr 1fr 1fr 200px(5 列) */ ``` - minmax() 弹性范围 - minmax(最小值, 最大值) 让列宽在一个范围内弹性变化: - ```css .grid { grid-template-columns: minmax(200px, 300px) 1fr 1fr; /* 第一列:最小 200px,最大 300px */ /* 后两列等分剩余空间 */ } /* 行高最小 100px,内容多时自动撑高 */ .grid { grid-template-rows: minmax(100px, auto); } ``` - gap 行列间距 - 和 Flexbox 的 gap 一样,但 Grid 里可以分别控制行间距和列间距 - ```css .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; /* 行列间距都是 20px */ gap: 16px 24px; /* 行间距 16px,列间距 24px */ row-gap: 16px; /* 只设行间距 */ column-gap: 24px; /* 只设列间距 */ } ``` - 隐式网格与 grid-auto-rows - 你定义了 2 行 3 列 = 6 个格子,但如果有 9 个项目,多出来的 3 个怎么办? - Grid 会自动创建新行来容纳它们,这就是隐式网格: - ```css .grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: 150px 150px; /* 只定义了 2 行 */ /* 隐式创建的行默认高度由内容决定 */ /* 用 grid-auto-rows 控制隐式行的高度 */ grid-auto-rows: 150px; /* 隐式行也是 150px */ grid-auto-rows: minmax(100px, auto); /* 最少 100px,内容多就撑高 */ } ``` - Grid 项目放置与区域 - grid-column / grid-row 放置项目 - 用网格线编号来精确控制项目的位置和大小 - ```css .item { /* 完整写法 */ grid-column-start: 1; grid-column-end: 3; /* 从列线 1 到列线 3 → 跨 2 列 */ grid-row-start: 1; grid-row-end: 2; /* 从行线 1 到行线 2 → 占 1 行 */ /* 简写(推荐) */ grid-column: 1 / 3; /* 起始线 / 结束线 */ grid-row: 1 / 2; } ``` -  - span 关键字 - 用 span 表示跨几格 - ```css /* 以下三种写法效果相同 */ .item { grid-column: 1 / 3; } /* 从线 1 到线 3 */ .item { grid-column: 1 / span 2; } /* 从线 1 起,跨 2 列 */ .item { grid-column: span 2; } /* 从自然位置起,跨 2 列 */ ``` - span 在实际开发中更常用,因为你不需要关心具体的线编号 - ```css /* 图片画廊:让某张特色图跨 2 列 2 行 */ .photo-featured { grid-column: span 2; grid-row: span 2; } /* 跨满整行 */ .full-width { grid-column: 1 / -1; /* 从第一条线到最后一条线 */ } ``` - 实现各种跨列跨行布局 - ```css .grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: 120px; gap: 12px; } /* 横幅:跨满一整行 */ .banner { grid-column: 1 / -1; /* 第 1 条线到最后一条线 */ } /* 大卡片:跨 2 列 2 行 */ .large-card { grid-column: span 2; grid-row: span 2; } /* 侧边栏:固定在右侧,跨 3 行 */ .sidebar { grid-column: 4; /* 只写一个值 = 起始线,占 1 列 */ grid-row: 1 / span 3; /* 从第 1 行起跨 3 行 */ } /* 自由放置到任意位置 */ .special { grid-column: 2 / 4; /* 第 2~3 列 */ grid-row: 2 / 4; /* 第 2~3 行 */ } /* ┌──────────────────────────────┐ │ banner (跨 4 列) │ ├───────────┬────────┬─────────┤ │ │ C │ │ │ large ├────────┤ sidebar │ │ (2×2) │ D │ (1×3) │ ├─────┬─────┼────────┤ │ │ E │ F │ G │ │ └─────┴─────┴────────┴─────────┘ */ ``` - grid-template-areas 命名区域 ⭐ 重点 - 这是 Grid 最直观的布局方式——用 ASCII 字符画的方式"画"出你的布局: - ```css .page { display: grid; grid-template-columns: 220px 1fr; grid-template-rows: 60px 1fr 50px; grid-template-areas: "header header" "sidebar main" "footer footer"; min-height: 100vh; } /* 每个子元素用 grid-area 对号入座 */ .page-header { grid-area: header; } .page-sidebar { grid-area: sidebar; } .page-main { grid-area: main; } .page-footer { grid-area: footer; } ``` - 规则 ```css grid-template-areas: "header header header" /* 同名区域必须是矩形 */ "sidebar main main" /* 每行的区域数 = 列数 */ "sidebar footer footer"; /* 用 . 表示空白格 */ /* ❌ 非矩形会报错 */ grid-template-areas: "header sidebar" "main main" "footer sidebar"; /* sidebar 呈 L 形,不合法! */ /* ✅ 用 . 留空 */ grid-template-areas: "header header header" "sidebar main ." /* 右下角留空 */ "footer footer footer"; ``` - Grid 中的对齐 - 容器级对齐(控制所有项目) - ```css .grid { display: grid; grid-template-columns: repeat(3, 100px); /* 网格总宽 300px,可能比容器窄 */ height: 400px; /* justify-items:项目在单元格内的水平对齐 */ justify-items: stretch; /* 默认:撑满单元格宽度 */ justify-items: start; /* 靠左 */ justify-items: end; /* 靠右 */ justify-items: center; /* 水平居中 */ /* align-items:项目在单元格内的垂直对齐 */ align-items: stretch; /* 默认:撑满单元格高度 */ align-items: start; /* 靠上 */ align-items: end; /* 靠下 */ align-items: center; /* 垂直居中 */ /* place-items:简写(垂直 水平) */ place-items: center; /* 水平垂直都居中 */ place-items: start end; /* 垂直靠上,水平靠右 */ } /* start + start center + center end + end ┌────────────┐ ┌────────────┐ ┌────────────┐ │[item] │ │ │ │ │ │ │ │ [item] │ │ │ │ │ │ │ │ [item]│ └────────────┘ └────────────┘ └────────────┘ */ ``` - 整个网格在容器内的对齐 - 当网格的总尺寸小于容器时,可以控制整个网格的位置: - ```css .grid { display: grid; grid-template-columns: repeat(3, 100px); /* 总宽 300px */ width: 600px; /* 容器宽 600px,有 300px 空余 */ /* justify-content:整个网格水平方向的对齐 */ justify-content: center; /* 网格整体水平居中 */ justify-content: space-between; /* 列之间等分空间 */ /* align-content:整个网格垂直方向的对齐 */ align-content: center; /* place-content 简写 */ place-content: center; } ``` - 单个项目对齐 - ```css /* 某个项目单独设置 */ .special-item { justify-self: center; /* 这个项目在格子里水平居中 */ align-self: end; /* 这个项目在格子里垂直靠底 */ /* 简写 */ place-self: end center; } ``` - Grid 自适应与 Flex vs Grid 选型 - auto-fill 与 auto-fit - auto-fill:能塞多少列就塞多少列 - ```css .grid { display: grid; grid-template-columns: repeat(auto-fill, 200px); gap: 16px; } ``` - auto-fit:同上,但空轨道会被折叠 - ```css .grid { display: grid; grid-template-columns: repeat(auto-fit, 200px); gap: 16px; } ``` - auto-fit + minmax 万能自适应 - ```css .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; } ``` - 这一行做了什么:每列最小 280px,有多余空间时等分放大到 1fr。容器变宽就自动多放一列,变窄就自动减少,每列永远在 280px ~ 等分之间弹性变化。 - auto-fill vs auto-fit + minmax 的区别 - 当项目数量少于能放下的列数时,差别才显现 - ```css /* 容器 1200px,3 个项目 */ /* auto-fill:空轨道保留,项目不会撑到 1fr */ repeat(auto-fill, minmax(280px, 1fr)) | [280px] [280px] [280px] [空280px] | 项目大小固定在 280px,因为还有空轨道分 fr /* auto-fit:空轨道折叠,项目撑满 */ repeat(auto-fit, minmax(280px, 1fr)) | [ 400px ] [ 400px ] [ 400px ] | 空轨道折叠了,3 个项目等分 1200px ``` 响应式与进阶# - 响应式与进阶 - 响应式基础与媒体查询 - viewport meta 标签 - ```html <meta name="viewport" content="width=device-width, initial-scale=1.0"> ``` - 手机浏览器会把页面当做 980px 宽的桌面页面来渲染,然后整体缩小 - 媒体查询 @media 基础用法 - ```css /* 基础语法 */ @media (条件) { /* 条件满足时才生效的样式 */ } /* 最大宽度:屏幕 ≤ 768px 时生效 */ @media (max-width: 768px) { .sidebar { display: none; } } /* 最小宽度:屏幕 ≥ 1024px 时生效 */ @media (min-width: 1024px) { .container { max-width: 1200px; } } /* 范围:屏幕在 768px ~ 1024px 之间 */ @media (min-width: 768px) and (max-width: 1024px) { .card-grid { grid-template-columns: repeat(2, 1fr); } } ``` - 现代范围语法 ```css /* 新语法:用比较运算符,更易读 */ @media (width <= 768px) { .sidebar { display: none; } } @media (width >= 1024px) { .container { max-width: 1200px; } } @media (768px <= width <= 1024px) { .card-grid { grid-template-columns: repeat(2, 1fr); } } ``` - 移动优先 vs 桌面优先 - 桌面优先 - ```css /* 默认样式 = 桌面 */ .grid { grid-template-columns: repeat(3, 1fr); } /* 平板:≤ 1024px */ @media (max-width: 1024px) { .grid { grid-template-columns: repeat(2, 1fr); } } /* 手机:≤ 768px */ @media (max-width: 768px) { .grid { grid-template-columns: 1fr; } } ``` - 移动优先 - ```css /* 默认样式 = 手机 */ .grid { grid-template-columns: 1fr; } /* 平板:≥ 768px */ @media (min-width: 768px) { .grid { grid-template-columns: repeat(2, 1fr); } } /* 桌面:≥ 1024px */ @media (min-width: 768px) { .grid { grid-template-columns: repeat(3, 1fr); } } ``` - 断点设计 - 断点就是布局发生变化的屏幕宽度 - 需要按布局需要记 - ```css /* 推荐的断点体系 */ /* 小手机 */ /* 默认样式覆盖,不需要媒体查询 */ /* 大手机 / 小平板 */ @media (min-width: 640px) { } /* 平板 */ @media (min-width: 768px) { } /* 小桌面 / 大平板横屏 */ @media (min-width: 1024px) { } /* 桌面 */ @media (min-width: 1280px) { } /* 大桌面 */ @media (min-width: 1536px) { } ``` - ```css /* 大多数项目只需要这三个 */ /* 手机:默认 */ /* 平板:768px */ /* 桌面:1024px */ ``` - 媒体查询的组织方式 - 集中在底部 ```css /* 所有默认样式 */ .header { ... } .hero { ... } .card { ... } /* 所有平板适配 */ @media (min-width: 768px) { .header { ... } .hero { ... } .card { ... } } /* 所有桌面适配 */ @media (min-width: 1024px) { .header { ... } .hero { ... } .card { ... } } ``` - 跟在组件后面(按组件分组) ```css /* 一个组件的所有状态都在一起,好维护。这也是大多数 CSS 框架和组件库的做法。*/ /* Header */ .header { ... } @media (min-width: 768px) { .header { ... } } @media (min-width: 1024px) { .header { ... } } /* Hero */ .hero { ... } @media (min-width: 768px) { .hero { ... } } @media (min-width: 1024px) { .hero { ... } } /* Card */ .card { ... } @media (min-width: 768px) { .card { ... } } ``` - 常见响应式模式 - 导航栏折叠 ```css /* 移动端:隐藏链接,显示菜单按钮 */ .nav { display: none; } .menu-toggle { display: block; } /* 桌面端:显示链接,隐藏按钮 */ @media (min-width: 768px) { .nav { display: flex; } .menu-toggle { display: none; } } ``` - 侧边栏变底部 ```css /* 移动端:单列,侧边栏在下面 */ .layout { display: grid; grid-template-columns: 1fr; } /* 桌面端:双列 */ @media (min-width: 1024px) { .layout { grid-template-columns: 1fr 280px; } } ``` - Grid areas 重排 ```css /* 移动端布局 */ .page { display: grid; grid-template-areas: "header" "main" "sidebar" "footer"; grid-template-columns: 1fr; } /* 桌面端布局 */ @media (min-width: 1024px) { .page { grid-template-areas: "header header" "sidebar main" "footer footer"; grid-template-columns: 240px 1fr; } } ``` - 表格在移动端变卡片 ```css /* 移动端:隐藏表头,每行变成卡片 */ @media (max-width: 768px) { table thead { display: none; } table tr { display: block; margin-bottom: 16px; border: 1px solid #eee; border-radius: 8px; padding: 12px; } table td { display: flex; justify-content: space-between; padding: 6px 0; } /* 用 data 属性模拟表头 */ table td::before { content: attr(data-label); font-weight: 700; color: #333; } } ``` - 间距随屏幕缩放 ```css /* 用 CSS 变量集中管理间距 */ :root { --page-padding: 16px; --section-gap: 24px; } @media (min-width: 768px) { :root { --page-padding: 32px; --section-gap: 40px; } } @media (min-width: 1024px) { :root { --page-padding: 48px; --section-gap: 56px; } } /* 全局使用变量,不用每个组件单独写媒体查询 */ .header { padding: 0 var(--page-padding); } .hero { padding: var(--section-gap) var(--page-padding); } .content { padding: var(--section-gap) var(--page-padding); } ``` - 响应式进阶内容 - 图片是响应式布局中最容易出问题的元素——要么溢出容器,要么被拉伸变形。 - max-width 防溢出 - ```css /* 所有项目都应该全局加上这条 */ img { max-width: 100%; /* 永远不超过容器宽度 */ height: auto; /* 保持原始比例 */ display: block; /* 去掉图片底部的空隙 */ } ``` - object-fit:控制图片填充方式 - 当图片容器有固定的宽高比时,object-fit 控制图片如何适应 - ```css /* 原图 800×400,容器 300×300 */ .img-cover { width: 300px; height: 300px; object-fit: cover; /* 填满容器,多余裁掉(保持比例)✅ 最常用 */ } .img-contain { width: 300px; height: 300px; object-fit: contain; /* 完整显示,可能留白(保持比例) */ } .img-fill { width: 300px; height: 300px; object-fit: fill; /* 拉伸填满(变形)❌ 默认值 */ } .img-none { width: 300px; height: 300px; object-fit: none; /* 不缩放,只显示原图中心部分 */ } ``` - 配合 object-position 控制裁切焦点: ```css .avatar { width: 100px; height: 100px; border-radius: 50%; object-fit: cover; object-position: center top; /* 头像聚焦在上半部分 */ } /* 常用值 */ object-position: center; /* 默认:居中裁切 */ object-position: top; /* 从顶部裁切 */ object-position: left top; /* 从左上角裁切 */ object-position: 20% 30%; /* 自定义焦点 */ ``` - aspect-ratio:固定宽高比 ```css /* 不用 padding hack 了,直接声明宽高比 */ .card-cover { width: 100%; aspect-ratio: 16 / 9; /* 16:9 视频比例 */ object-fit: cover; } .avatar { width: 80px; aspect-ratio: 1; /* 正方形 = 1/1 */ border-radius: 50%; object-fit: cover; } .photo-portrait { aspect-ratio: 3 / 4; /* 竖版照片 */ } /* 配合 Grid 让所有卡片封面等高 */ .article-grid .card-cover { aspect-ratio: 16 / 10; object-fit: cover; } ``` - 响应式字体 - vw 单位:随视口缩放 - ```css /* vw = viewport width 的百分比 */ /* 1vw = 视口宽度的 1% */ h1 { font-size: 5vw; /* 手机 375px → 18.75px */ /* 平板 768px → 38.4px */ /* 桌面 1440px → 72px */ } ``` - 问题很明显:手机上太小,桌面上太大,没有上下限。 - clamp() 有上下限的响应式值 - clamp(最小值, 首选值, 最大值) 完美解决了这个问题 - ```css h1 { font-size: clamp(1.75rem, 4vw, 3rem); /* 最小 1.75rem (28px) 首选 4vw(随视口变化) 最大 3rem (48px) 三者取中间值 */ } ``` - 常用 clamp 配方 ```css /* 标题 */ h1 { font-size: clamp(1.75rem, 4vw, 3rem); } h2 { font-size: clamp(1.25rem, 3vw, 2rem); } h3 { font-size: clamp(1.1rem, 2.5vw, 1.5rem); } /* 正文 */ body { font-size: clamp(0.875rem, 1.5vw, 1.125rem); } /* 间距也可以用 clamp */ .section { padding: clamp(24px, 5vw, 64px) clamp(16px, 4vw, 48px); } /* 容器最大宽度 */ .container { width: clamp(320px, 90vw, 1200px); margin: 0 auto; } ``` - 用了 clamp 后,很多字号和间距就不需要写媒体查询了——它自动在范围内平滑缩放。 - 其他响应式单位 - ```css /* vh = viewport height */ .hero { min-height: 100vh; } /* 占满整个视口高度 */ /* svh / dvh = 小/动态视口高度(解决手机地址栏问题) */ .hero { min-height: 100dvh; } /* 推荐用 dvh */ /* vmin / vmax = 视口较小/较大边 */ .square { width: 50vmin; height: 50vmin; } /* 始终是正方形 */ ``` - 手机浏览器的地址栏会收起/展开,导致 100vh 的高度有时候会偏大。dvh(dynamic viewport height)会自动适应地址栏的变化 - ```css .full-screen { min-height: 100vh; /* 兜底 */ min-height: 100dvh; /* 现代浏览器用这个 */ } ``` - 容器查询 - 媒体查询根据视口宽度变化,但一个组件可能在不同位置出现 - 主内容区很宽,侧边栏很窄 - 容器查询根据父容器的宽度变化,让组件真正做到"放在哪里就适配哪里" - ```css /* 第一步:声明容器 */ .card-wrapper { container-type: inline-size; /* 启用容器查询 */ container-name: card; /* 可选:给容器命名 */ } /* 第二步:根据容器宽度写样式 */ @container card (min-width: 400px) { .card { display: flex; /* 容器宽时横排 */ gap: 16px; } .card-cover { flex: 0 0 200px; } } @container card (max-width: 399px) { .card { display: block; /* 容器窄时竖排 */ } .card-cover { width: 100%; aspect-ratio: 16 / 9; } } ``` - 容器查询单位 ```css .card-wrapper { container-type: inline-size; } .card-title { font-size: clamp(14px, 3cqi, 20px); /* cqi = 容器 inline-size 的 1% */ /* 类似于 vw,但基于容器宽度而不是视口 */ } ``` - 常用容器单位:cqi(容器宽度%)、cqb(容器高度%)、cqmin(较小边%)。 - 不需要媒体查询的响应式技巧 - 很多响应式效果可以用纯 CSS 实现,不写一行 @media - ```css /* 技巧 1:Grid auto-fit + minmax(Day 13 学过) */ .grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); /* 自动调整列数,完全不需要媒体查询 */ } /* 技巧 2:clamp 响应式字号 */ h1 { font-size: clamp(1.5rem, 4vw, 3rem); } /* 技巧 3:clamp 响应式间距 */ .section { padding: clamp(24px, 5vw, 64px); } /* 技巧 4:flex-wrap 自动换行 */ .button-group { display: flex; flex-wrap: wrap; gap: 12px; } /* 技巧 5:min() / max() 控制宽度 */ .container { width: min(90vw, 1200px); /* 取小值:窄屏时 90vw,宽屏时 1200px */ margin: 0 auto; } .card { width: max(280px, 30%); /* 取大值:至少 280px */ } /* 技巧 6:auto-fit 让不够时换行 */ .stats { display: flex; flex-wrap: wrap; gap: 16px; } .stat-item { flex: 1 1 150px; /* 至少 150px,不够就换行 */ } ``` - translation 过渡动画 - translation 基础语法 - ```css /* 没有 transition:悬停时背景色瞬间变化 */ .btn { background: #2563eb; } .btn:hover { background: #1d4ed8; } /* 加上 transition:背景色用 0.3 秒平滑过渡 */ .btn { background: #2563eb; transition: background 0.3s; } .btn:hover { background: #1d4ed8; } ``` - 完整语法 ```css transition: 属性 时长 缓动函数 延迟; /* 示例 */ transition: background 0.3s ease 0s; transition: transform 0.2s ease-in-out; transition: opacity 0.5s linear 0.1s; ``` - 分开写的写法 ```css .box { transition-property: background; /* 哪个属性要过渡 */ transition-duration: 0.3s; /* 过渡多长时间 */ transition-timing-function: ease; /* 缓动曲线 */ transition-delay: 0s; /* 延迟多久开始 */ } ``` - transition-property 过渡哪些属性 - ```css /* 指定单个属性 */ transition: background 0.3s; /* 指定多个属性 */ transition: background 0.3s, color 0.3s, transform 0.2s; /* 所有可过渡属性一起 */ transition: all 0.3s; ``` - all 方便但有两个问题 ```css /* ❌ all 的隐患 */ .card { transition: all 0.3s; /* 问题 1:可能过渡你不想过渡的属性(比如 width、height 导致布局抖动)*/ /* 问题 2:性能比指定属性差 */ } /* ✅ 推荐:明确列出要过渡的属性 */ .card { transition: transform 0.3s, box-shadow 0.3s; } ``` - 哪些属性可以过渡 ```css /* 能过渡的属性必须有中间值(浏览器知道怎么从 A 插值到 B)*/ /* ✅ 可以过渡 */ color, background-color /* 颜色有中间值 */ opacity /* 0 到 1 之间连续 */ transform /* 位移、缩放、旋转都有中间值 */ width, height, padding, margin /* 数值类都可以 */ border-color, border-radius box-shadow font-size, letter-spacing, line-height /* ❌ 不能过渡 */ display /* none → block 没有中间状态 */ font-family /* 字体之间没法渐变 */ background-image /* 图片之间不能插值 */ position /* static → absolute 没有中间值 */ ``` - display: none 不能过渡是一个经典痛点。要做显隐动画,用 opacity + visibility 代替 ```css /* 用 opacity + visibility 替代 display 的显隐过渡 */ .dropdown { opacity: 0; visibility: hidden; transform: translateY(-8px); transition: opacity 0.2s, visibility 0.2s, transform 0.2s; } .trigger:hover .dropdown { opacity: 1; visibility: visible; transform: translateY(0); } ``` - transition-duration 时长 - ```css transition-duration: 0.3s; /* 300 毫秒 */ transition-duration: 300ms; /* 同上,用 ms 写法 */ transition-duration: 0.15s; /* 150 毫秒,很快 */ transition-duration: 0.5s; /* 500 毫秒,中速 */ transition-duration: 1s; /* 1 秒,比较慢 */ ``` - 不同场景的推荐时长 ```css /* 微交互(按钮、链接、图标)→ 快速 */ .btn { transition: background 0.15s, transform 0.15s; } /* 悬停效果(卡片、图片)→ 中速 */ .card { transition: transform 0.3s, box-shadow 0.3s; } /* 展开/收起(下拉菜单、折叠面板)→ 稍慢 */ .panel { transition: max-height 0.35s, opacity 0.35s; } /* 页面级动画(模态框、页面切换)→ 慢 */ .modal { transition: opacity 0.4s, transform 0.4s; } ``` - 越小的元素过渡越快,越大的元素过渡可以稍慢。超过 0.5s 用户会觉得卡顿。 - transition-timing-function 缓动函数(重点) - 缓动函数决定过渡的"节奏感"——匀速、先快后慢、先慢后快 - ```css /* 预设曲线 */ transition-timing-function: linear; /* 匀速 */ transition-timing-function: ease; /* 默认:慢→快→慢,最常用 */ transition-timing-function: ease-in; /* 慢→快(加速) */ transition-timing-function: ease-out; /* 快→慢(减速)*/ transition-timing-function: ease-in-out; /* 慢→快→慢,比 ease 对称 */ ``` - 适合的场景 ```css /* ease-out(减速)→ 元素进入画面 */ .card-enter { transition: transform 0.3s ease-out; } /* ease-in(加速)→ 元素离开画面 */ .card-leave { transition: transform 0.3s ease-in; } /* ease-in-out → 来回移动、展开收起 */ .accordion { transition: max-height 0.35s ease-in-out; } /* linear → 颜色渐变、进度条 */ .progress-bar { transition: width 0.5s linear; } ``` - cubic-bezier 自定义曲线 - 所有预设曲线都是 cubic-bezier 的简写 ```css /* 预设值的真身 */ ease: cubic-bezier(0.25, 0.1, 0.25, 1.0) ease-in: cubic-bezier(0.42, 0, 1.0, 1.0) ease-out: cubic-bezier(0, 0, 0.58, 1.0) ease-in-out: cubic-bezier(0.42, 0, 0.58, 1.0) /* 自定义:弹性回弹效果 */ .bounce { transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); /* y 值超过 1 就会产生"过冲"回弹效果 */ } ``` - 推荐网站 cubic-bezier.com 可以预览和对比各种曲线 - transition-delay 延迟 - ```css /* 鼠标悬停 0.1s 后才开始变化 */ .tooltip { transition: opacity 0.3s ease 0.1s; } /* 多个属性不同延迟 → 顺序动画 */ .card:hover .card-title { transform: translateY(0); transition: transform 0.3s ease 0s; /* 标题先动 */ } .card:hover .card-desc { transform: translateY(0); transition: transform 0.3s ease 0.1s; /* 描述延迟 0.1s */ } .card:hover .card-btn { transform: translateY(0); transition: transform 0.3s ease 0.2s; /* 按钮延迟 0.2s */ } ``` - transform 变化 - 2D 变化基础 - transform 不会影响文档流——元素在页面中的原始位置仍然保留,只是视觉上发生了变化 - translate 位移 - ```css /* 水平和垂直移动 */ transform: translateX(50px); /* 右移 50px */ transform: translateY(-20px); /* 上移 20px */ transform: translate(50px, -20px); /* 合写:右移 50 + 上移 20 */ /* 百分比相对于元素自身尺寸 */ transform: translateX(100%); /* 右移自身宽度的距离 */ transform: translateY(-50%); /* 上移自身高度的一半 */ ``` - translate 最经典的用途——绝对定位居中 ```css .centered { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); /* top/left 把左上角移到容器中心 */ /* translate 再把元素自身偏移回去 */ } ``` - scale 缩放 - ```css transform: scale(1.5); /* 等比放大 1.5 倍 */ transform: scale(0.8); /* 等比缩小到 80% */ transform: scale(1); /* 原始大小 */ transform: scale(0); /* 缩小到消失 */ transform: scaleX(1.5); /* 只水平拉宽 */ transform: scaleY(0.5); /* 只垂直压扁 */ transform: scale(1.5, 0.8); /* 水平 1.5 倍,垂直 0.8 倍 */ ``` - 常见用法 ```css /* 按钮点击缩小反馈 */ .btn:active { transform: scale(0.96); } /* 图片悬停放大 */ .img-wrapper { overflow: hidden; } .img-wrapper img { transition: transform 0.4s ease; } .img-wrapper:hover img { transform: scale(1.1); } /* 从 0 到 1 的弹出效果 */ .popup { transform: scale(0); transition: transform 0.3s ease; } .popup.active { transform: scale(1); } ``` - rotate 旋转 - ```css transform: rotate(45deg); /* 顺时针旋转 45 度 */ transform: rotate(-90deg); /* 逆时针旋转 90 度 */ transform: rotate(180deg); /* 翻转 */ transform: rotate(360deg); /* 转一圈(用于 loading 动画) */ transform: rotate(0.5turn); /* 半圈 = 180deg */ ``` - ```css transform: rotate(45deg); /* 顺时针旋转 45 度 */ transform: rotate(-90deg); /* 逆时针旋转 90 度 */ transform: rotate(180deg); /* 翻转 */ transform: rotate(360deg); /* 转一圈(用于 loading 动画) */ transform: rotate(0.5turn); /* 半圈 = 180deg */ ``` - ```css transform: rotate(45deg); /* 顺时针旋转 45 度 */ transform: rotate(-90deg); /* 逆时针旋转 90 度 */ transform: rotate(180deg); /* 翻转 */ transform: rotate(360deg); /* 转一圈(用于 loading 动画) */ transform: rotate(0.5turn); /* 半圈 = 180deg */ ``` - ```css /* 箭头图标旋转表示展开/收起 */ .arrow { display: inline-block; transition: transform 0.3s ease; } .expanded .arrow { transform: rotate(180deg); } /* 悬停微旋转 */ .icon:hover { transform: rotate(15deg); } ``` - skew 倾斜 - ```css transform: skewX(10deg); /* 水平方向倾斜 */ transform: skewY(5deg); /* 垂直方向倾斜 */ transform: skew(10deg, 5deg); /* 合写 */ ``` - ```css /* 平行四边形按钮 */ .skew-btn { transform: skewX(-10deg); } /* 文字反向 skew 保持正常 */ .skew-btn span { display: inline-block; transform: skewX(10deg); } /* 斜切背景装饰 */ .section-divider { height: 80px; background: hsl(220, 85%, 55%); transform: skewY(-3deg); } ``` - 组合变化 ```css /* 先旋转 45 度,再右移 100px */ transform: translateX(100px) rotate(45deg); /* 实际执行顺序:先 rotate → 再 translate */ /* 注意:旋转后坐标轴也跟着转了,所以 translate 方向会变 */ /* 先移动再旋转 vs 先旋转再移动,效果完全不同! */ ``` - ```css transform: translateX(100px) rotate(45deg); 原始 → 先旋转 45° → 再沿旋转后的 X 轴移动 100px 结果:元素在右上方 transform: rotate(45deg) translateX(100px); 原始 → 先沿原始 X 轴移动 100px → 再旋转 45° 结果:元素在正右方但旋转了 ``` - 先写 translate,再写其他变化 ```css /* ✅ 推荐顺序:translate → scale → rotate */ .card:hover { transform: translateY(-8px) scale(1.02) rotate(1deg); } ``` - translate-origin 变换原点 - 默认情况下所有变换都以元素中心为原点。transform-origin 可以改变这个原点: - ```css /* 默认:中心 */ transform-origin: center; /* 50% 50% */ /* 关键字 */ transform-origin: top left; /* 左上角 */ transform-origin: bottom right; /* 右下角 */ transform-origin: center top; /* 顶部中间 */ /* 百分比 */ transform-origin: 0% 0%; /* 左上角 */ transform-origin: 100% 100%; /* 右下角 */ /* 像素值 */ transform-origin: 20px 30px; ``` - 改变原点最直观的效果在旋转和缩放上: - ```css /* 旋转:绕中心转 vs 绕左上角转 */ .spin-center { transform-origin: center; transform: rotate(45deg); } .spin-corner { transform-origin: top left; transform: rotate(45deg); } /* spin-center: spin-corner: ╱╲ ┌╲ ╱ ╲ │ ╲ ╱ ◉ ╲ ←原点在中心 │ ╲ ← 原点在左上角 ╲ ╱ │ ╱ ╲╱ │╱ */ ``` - 实用场景 ```css /* 下拉菜单从顶部展开 */ .dropdown { transform-origin: top center; transform: scaleY(0); /* 从顶部收起 */ transition: transform 0.2s ease; } .trigger:hover .dropdown { transform: scaleY(1); /* 从顶部展开 */ } /* 导航下划线从左侧展开 */ .nav-link::after { transform-origin: left; transform: scaleX(0); transition: transform 0.3s ease; } .nav-link:hover::after { transform: scaleX(1); } /* 图片从左上角放大 */ .zoom-corner { transform-origin: top left; } .zoom-corner:hover { transform: scale(1.5); } ``` - 3D 变化 - ```css /* 给父元素设透视 */ .scene { perspective: 800px; /* 观察者距离屏幕 800px */ /* 值越小 → 透视感越强(像凑近看)*/ /* 值越大 → 透视感越弱(像远处看)*/ } ``` - @keyframes 关键帧动画 - 基础语法 - 动画分两步:先用 @keyframes 定义动画,再用 animation 应用到元素上 - ```css /* 第一步:定义关键帧 */ @keyframes fadeIn { from { opacity: 0; /* 起始状态 */ } to { opacity: 1; /* 结束状态 */ } } /* 第二步:应用动画 */ .element { animation: fadeIn 0.5s ease; } ``` - 用百分比定义关键帧 - ```css @keyframes bounce { 0% { transform: translateY(0); } 30% { transform: translateY(-30px); } 50% { transform: translateY(0); } 70% { transform: translateY(-15px); } 100% { transform: translateY(0); } } .ball { animation: bounce 1s ease; } ``` - animation 属性详解 - animation 的完整简写 - ```css animation: 名称 时长 缓动 延迟 次数 方向 填充 播放状态; /* 示例 */ animation: fadeIn 0.5s ease 0s 1 normal forwards running; ``` - animation-name 动画名称 - ```css animation-name: fadeIn; /* 对应 @keyframes fadeIn */ animation-name: none; /* 不应用任何动画 */ ``` - animation-duration 持续时长 - ```css animation-duration: 0.5s; animation-duration: 300ms; animation-duration: 2s; ``` - animation-timing-function 缓动 - ```css animation-timing-function: ease; animation-timing-function: linear; animation-timing-function: ease-in-out; animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); /* steps() —— 逐帧动画,不做插值 */ animation-timing-function: steps(4); /* 分 4 步跳变 */ animation-timing-function: steps(1, end); /* 打字机效果 */ ``` - ```css /* 精灵图逐帧动画 */ .sprite { width: 64px; height: 64px; background: url('sprite.png'); animation: walk 0.6s steps(8) infinite; } @keyframes walk { to { background-position: -512px 0; } /* 8 帧 × 64px */ } /* 打字机效果 */ .typing { width: 0; overflow: hidden; white-space: nowrap; border-right: 2px solid; animation: typing 2s steps(15) forwards, /* 15 个字逐个显示 */ blink 0.8s step-end infinite; /* 光标闪烁 */ } @keyframes typing { to { width: 15ch; } /* ch = 一个字符的宽度 */ } @keyframes blink { 50% { border-color: transparent; } } ``` - animation-delay 延迟 - ```css animation-delay: 0.3s; /* 等 0.3 秒后开始 */ animation-delay: -0.5s; /* 负值:动画从 0.5s 处开始播放(跳过开头) */ ``` - ```css /* loading 动画:3 个点交错起步 */ .dot:nth-child(1) { animation-delay: 0s; } .dot:nth-child(2) { animation-delay: 0.15s; } .dot:nth-child(3) { animation-delay: 0.3s; } ``` - animation-iteration-count 播放次数 - ```css animation-iteration-count: 1; /* 默认:播放 1 次 */ animation-iteration-count: 3; /* 播放 3 次 */ animation-iteration-count: infinite; /* 无限循环 */ ``` - animation-direction 方向 - ```css animation-direction: normal; /* 默认:0% → 100% */ animation-direction: reverse; /* 反向:100% → 0% */ animation-direction: alternate; /* 交替:去 → 回 → 去 → 回 */ animation-direction: alternate-reverse; /* 反向交替:回 → 去 → 回 → 去 */ ``` - animation-fill-mode 填充模式 (重要) - ```css animation-fill-mode: none; /* 默认:动画结束后回到初始状态 */ animation-fill-mode: forwards; /* 保持在最后一帧 */ animation-fill-mode: backwards; /* 延迟期间就应用第一帧 */ animation-fill-mode: both; /* forwards + backwards */ ``` - ```css @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } none: 延迟中透明度=1 → 动画0→1 → 结束后回到1(跳了一下再回来) forwards: 延迟中透明度=1 → 动画0→1 → 结束后保持1 ✅ backwards: 延迟中透明度=0 → 动画0→1 → 结束后回到1 both: 延迟中透明度=0 → 动画0→1 → 结束后保持1 ✅✅ 最完整 ``` - animation-play-state 播放状态 - ```css animation-play-state: running; /* 默认:播放中 */ animation-play-state: paused; /* 暂停 */ /* 悬停暂停动画 */ .marquee:hover { animation-play-state: paused; } ``` - animation 简写 - 记住简写的顺序规则:第一个时间值是 duration,第二个是 delay - ```css /* animation: name duration timing delay count direction fill state */ animation: fadeIn 0.5s ease 0s 1 normal forwards running; /* 常用的简写 */ animation: fadeIn 0.5s ease forwards; /* 渐入并保持 */ animation: spin 1s linear infinite; /* 无限匀速旋转 */ animation: bounce 0.6s ease-in-out 3; /* 弹跳 3 次 */ animation: float 3s ease-in-out infinite alternate; /* 无限来回浮动 */ ``` - 多动画同时应用 - ```css .element { animation: fadeIn 0.5s ease forwards, slideUp 0.5s ease forwards; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes slideUp { from { transform: translateY(30px); } to { transform: translateY(0); } } ``` 工程化与实战# - 标题