第四篇_强化视觉_字体_动画_变换

第四篇 强化视觉:字体、动画、变换

目标:在掌握盒模型和布局之后,进一步提升页面的"质感"。看完这一篇,你应该能:

  • 为不同场景选择合适的字体、字号、行高和字重;
  • 使用过渡和动画让交互变得自然、不突兀;
  • 用 2D/3D 变换给元素增加层次感,而不是简单粗暴地"放大缩小"。

第9章 字体:让文字拥有自己的个性

这一章解决的核心问题:

  • 中文 Web 项目里应该选哪些字体?怎么写回退字体?
  • 字号、行高、字重如何搭配才舒服?
  • @font-face 怎么加载自定义字体?加载不出来时会发生什么?

用一句话概括:让页面上的文字既好看又好读。

9.1 字体族与字体文件

9.1.1 字体族(font-family)基础

CSS 中的 font-family 就是用来指定字体的。常见写法:

css 复制代码
body {
  /* font-family: 设置字体族,浏览器会按顺序查找可用字体 */
  font-family: "PingFang SC",      /* 苹果系统的苹方字体 */
               "Microsoft YaHei",   /* Windows系统的微软雅黑 */
               "Noto Sans SC",      /* Google的思源黑体 */
               Arial,               /* 西文备选字体 */
               sans-serif;          /* 无衬线字体族(最终兜底) */
}

这里包含几类字体:

  • 具体字体:"PingFang SC"(苹方)、"Microsoft YaHei"(微软雅黑)、"Noto Sans SC"
  • 西文字体:Arial
  • 泛型字体族(generic family):sans-serif(无衬线)、serif(衬线)、monospace(等宽)

浏览器的选择流程大致是:

  1. 按顺序找你写的字体
  2. 找到第一个当前系统有的
  3. 如果都没有,就退回到泛型字体族

可以用一个小图来理解:

txt 复制代码
"PingFang SC", "Microsoft YaHei", "Noto Sans SC", Arial, sans-serif
   ↓           ↓              ↓             ↓            ↓
 先尝试       再尝试         再尝试        再尝试      最后至少给我一个无衬线

实战建议:

  • 中文环境下,推荐写一个「中文 + 西文 + 泛型」的链条
  • 不要只写 Microsoft YaHeiSimSun 之类单个字体,这样在 macOS / Linux 上效果会很差
9.1.2 常用中文 Web 字体组合

一个通用、比较稳妥的组合:

css 复制代码
body {
  font-family: 
    /* 系统UI字体(优先使用操作系统的界面字体) */
    -apple-system, BlinkMacSystemFont,     /* macOS/iOS系统字体 */
    "Segoe UI", "Roboto", "Helvetica Neue", /* Windows/Android/老版macOS */
    
    /* 中文字体族 */
    "PingFang SC",        /* macOS/iOS中文字体 */
    "Hiragino Sans GB",   /* macOS日文字体(支持中文) */
    "Microsoft YaHei",    /* Windows中文字体 */
    "Noto Sans CJK SC",   /* Google思源黑体简体 */
    "Source Han Sans SC", /* Adobe思源黑体简体 */
    
    /* 西文兜底 */
    Arial, sans-serif;     /* 最终备选方案 */
}

含义大致是:

  • 优先使用系统 UI 字体(iOS/macOS 上的系统字体)
  • 再退到常见的中文无衬线字体
  • 最后至少还有一个通用的 sans-serif

你可以根据项目受众(Windows 为主还是移动端为主)去微调这份列表,但整体思路类似:先系统,后常见中文,再兜底。

9.1.3 字体文件格式与兼容性

在 Web 上常见的字体文件格式:

  • ttf / otf:传统桌面字体格式
  • woff:为 Web 优化的字体格式,压缩率更高
  • woff2:更现代的压缩,更省流量(现代浏览器支持好)

在实际项目里,通常建议:

  • 优先提供 woff2 + woff
  • 必要时提供 ttf 作为兜底

注意:字体文件很大,乱用自定义字体会严重拖慢首屏加载,这一点在后面性能部分会再强调。

9.2 @font-face 的加载

@font-face 允许你引入自定义字体(如品牌专用字体或图标字体)。

9.2.1 基本用法
css 复制代码
/* @font-face: 自定义字体声明规则 */
@font-face {
  font-family: "MyBrandFont";  /* 给自定义字体起个名字 */
  
  /* src: 字体文件的来源,按顺序尝试加载 */
  src: url("/fonts/MyBrandFont.woff2") format("woff2"),  /* 优先:最新压缩格式 */
       url("/fonts/MyBrandFont.woff") format("woff");     /* 备选:旧版压缩格式 */
  
  font-weight: 400;    /* 声明这是正常粗细(normal)的字形 */
  font-style: normal;  /* 声明这是正体(非斜体)字形 */
  
  /* font-display: 控制字体加载期间的文字显示策略 */
  font-display: swap;  /* swap=先显示备用字体,加载完成后切换 */
}

.brand-title {
  /* 使用自定义字体,失败时退回到系统字体 */
  font-family: "MyBrandFont",      /* 优先使用自定义字体 */
               system-ui,           /* 退回系统UI字体 */
               -apple-system,       /* Apple系统字体 */
               BlinkMacSystemFont,  /* Chrome on Mac字体 */
               "Segoe UI",          /* Windows字体 */
               sans-serif;          /* 最终兜底方案 */
}

关键点解释:

  • font-family:给这套字体起一个名字(CSS 内部用)
  • src:列出字体文件及格式
  • font-weight / font-style:声明这是哪个粗细、哪种风格的字形
  • font-display:控制字体加载时的行为(非常重要)
9.2.2 font-display 的几种策略

常见取值:

  • auto:浏览器自己决定
  • swap推荐,先用系统字体显示,字体加载完再"无缝切换"成目标字体
  • fallback:短暂空白后,如果加载超时就不再换成目标字体
  • block:在一小段时间内会「空白」,等字体下来再全部渲染

可以用一个时间轴来感受:

txt 复制代码
加载过程:

[ 请求字体中...... ]

font-display: swap

0ms ──────> 文本先用系统字体显示
          字体加载完成后,悄悄换成 MyBrandFont

font-display: block

0ms ──────> 文本区域可能是空白(FOIT:Flash of Invisible Text)
          等字体加载完成才一起显示

大多数内容型网站,建议使用 swap,这样用户不会看到空白的文字区域,最多是"字体风格稍后变一下"。

9.2.3 字体加载失败时会怎样?

如果网络很差或字体路径错误:

  • 浏览器会按 font-family 列表继续向后找
  • 找不到时会退到泛型族(比如 sans-serif

这也是为什么即使用 @font-face 也要在 font-family 中写好"备用字体链"。

9.3 字体渲染、行高、字距

这一节更多是「排版手感」,不是单纯记忆属性,而是建立一个舒服的默认排版模板。

9.3.1 字号与层级(Typographic Scale)

一个常见且好用的标题层级(以根字号 16px 为例):

css 复制代码
/* font-size: 设置字体大小 */
/* rem单位: 相对于根元素(html)的字体大小,默认1rem=16px */

h1 { 
  font-size: 2.25rem;    /* 2.25 × 16 = 36px 主标题 */
}

h2 { 
  font-size: 1.875rem;   /* 1.875 × 16 = 30px 次级标题 */
}

h3 { 
  font-size: 1.5rem;     /* 1.5 × 16 = 24px 三级标题 */
}

h4 { 
  font-size: 1.25rem;    /* 1.25 × 16 = 20px 四级标题 */
}

body { 
  font-size: 1rem;       /* 1 × 16 = 16px 正文大小 */
}

small { 
  font-size: 0.875rem;   /* 0.875 × 16 = 14px 小字/注释 */
}

把它画成「标题梯度」:

txt 复制代码
H1  ██████████████████   36px
H2  ███████████████      30px
H3  ███████████          24px
H4  █████████            20px
正文 ███████             16px
注释 █████              14px

关键是:

  • 标题层级之间要有明显差别,但不要夸张到"像海报"
  • 正文字号在中文环境下,16px 是一个比较舒服的起点
9.3.2 行高(line-height)

常见写法:

  • 不带单位的倍数:line-height: 1.5;1.6;1.75;
  • 不推荐为正文用 px 行高(容易在不同字号组合时出现割裂感)

一个常见的正文排版设置:

css 复制代码
.article {
  font-size: 16px;      /* 设置基础字体大小 */
  
  /* line-height: 设置行高(行间距) */
  /* 无单位数值 = 字体大小的倍数 */
  line-height: 1.75;    /* 实际行高 = 16px × 1.75 = 28px */
                        /* 1.75是中文阅读的舒适行高 */
}

直观感觉可以想成:

txt 复制代码
文本行(字号16px)

┌────────────────────┐
│ 文本文本文本文本文  │  ← 行高包裹的"行盒子"
└────────────────────┘

行高太小:文字挤在一起难读;

行高太大:行与行之间像断开一样,也不好阅读。

经验值:

  • 正文段落:1.6 ~ 1.8
  • 标题:可以略小,如 1.2 ~ 1.4
9.3.3 字距与段落间距

几个相关属性:

  • letter-spacing:字母/字距
  • word-spacing:单词间距(对中文不常用)
  • text-indent:首行缩进
  • margin-top / margin-bottom:控制段落之间的距离

中文常用设置示例:

css 复制代码
.article p {
  /* margin: 设置外边距 (上下 左右) */
  margin: 0.75rem 0;      /* 段落上下间距各12px (0.75×16) */
  
  /* text-indent: 设置首行缩进 */
  /* em单位: 相对于当前元素的字体大小 */
  text-indent: 2em;       /* 缩进2个字符宽度(中文习惯) */
}

.article p.no-indent {
  text-indent: 0;         /* 取消首行缩进(如章节开头) */
}
9.3.4 字体渲染差异(粗细与抗锯齿)

不同系统和浏览器对同一个字体的渲染可能不一样,例如:

  • macOS 上文字更圆润、抗锯齿更明显
  • Windows 上有时会显得更锐利甚至偏"糊"

你可以在 DevTools 中对同一段文本:

  • 切换不同 font-weight(400 / 500 / 600 / 700)
  • 观察不同系统下的效果,挑选自己觉得舒服的档位

在设计系统中,常见的做法是只用少数几个字重:

  • 正文:400
  • 强调:500600
  • 标题:600700

9.4 中文字体的选择与性能

中文字体的一个现实问题是:文件更大

9.4.1 尽量优先使用系统字体

对于大多数内容型网站,推荐策略是:

  • 优先使用系统自带字体(体积为 0,不需要下载)
  • 只在品牌要求很强时,才引入额外的 Web 字体

示例策略:

css 复制代码
body {
  font-family: 
    /* system-ui: 使用操作系统的默认UI字体(最优先) */
    system-ui,              /* 通用系统字体关键字 */
    -apple-system,          /* Safari/macOS专用 */
    BlinkMacSystemFont,     /* Chrome/macOS专用 */
    "Segoe UI",            /* Windows系统字体 */
    "PingFang SC",          /* macOS中文字体 */
    "Microsoft YaHei",     /* Windows中文字体 */
    sans-serif;             /* 无衬线字体兜底 */
}

system-ui 是一个较新的泛型族,代表系统默认 UI 字体,现代浏览器支持较好。

9.4.2 如果必须用 Web 中文字体

要考虑几个问题:

  • 子集化(Subsetting)
    • 一套完整中文字体可能有几万个字形,文件轻松上 MB
    • 若只需部分字符(如 Logo 或标题语),可以做子集字体,只包含必要字符
  • 分权重加载
    • 不要一次性加载 300/400/500/600/700 等很多个字重
    • 优先加载正文用的 400 和标题用的 600 即可
  • 懒加载
    • 某些不重要的装饰字体,可以在首屏之后再加载
9.4.3 使用字体服务

市面上有多种字体服务(如 Google Fonts、一些国内商用字体平台),它们会帮助你:

  • 处理多格式(woff2/woff/ttf)
  • 做 CDN 加速
  • 一定程度的子集化

但要注意:

  • 商用项目必须确认字体的授权
  • 一些境外字体 CDN 可能在国内网络环境下速度不稳定

第9章到这里,你应该已经可以为页面做一套看起来「舒服」的文字系统:合理的字体族、字号梯度、行高、段落间距,并且知道自定义字体加载会带来什么影响。接下来第10章,我们会从静态视觉转向「动态」------用过渡和动画让页面动起来。

第10章 过渡与动画:让页面动起来

这一章解决的核心问题:

  • hover 时元素"突然跳变"很生硬,如何让变化变得柔和?
  • transition 要写哪些参数?顺序有什么讲究?
  • transform 缩放、旋转、位移这些变换如何组合?
  • animation + keyframestransition 有什么区别,各自适合什么场景?

10.1 transition 的四要素

transition 用来在「状态 A」和「状态 B」之间加一条"缓冲曲线"。典型场景:按钮 hover、折叠面板展开等。

10.1.1 四个核心参数

语法可以拆解为四个部分:

css 复制代码
/* transition: 过渡效果的简写属性 */
transition: property duration timing-function delay;
/*          要过渡的属性 持续时间 缓动函数 延迟时间 */

各参数详解:

  • property(过渡属性):指定哪个CSS属性需要过渡效果

    • all - 所有属性都过渡
    • opacity - 透明度
    • transform - 变换(缩放/旋转/位移等)
    • background-color - 背景颜色
    • width/height - 宽高(性能较差,慎用)
  • duration(持续时间):动画执行的时长

    • 0.2s - 200毫秒
    • 300ms - 300毫秒
    • 建议范围:150ms-350ms(太快看不清,太慢显得卡顿)
  • timing-function(缓动函数):控制动画的速度变化曲线

    • ease - 默认值,先快后慢
    • linear - 匀速
    • ease-in - 慢速开始,加速结束
    • ease-out - 快速开始,减速结束
    • ease-in-out - 慢速开始和结束
    • cubic-bezier() - 自定义贝塞尔曲线
  • delay(延迟时间):动画开始前的等待时间

    • 0s - 立即开始
    • 0.1s - 延迟100毫秒开始

最常见的写法:

css 复制代码
.btn {
  /* transition: 可以同时过渡多个属性,用逗号分隔 */
  transition: 
    background-color 0.2s ease,  /* 背景色过渡:0.2秒,缓入缓出 */
    transform 0.2s ease;         /* 变换过渡:0.2秒,缓入缓出 */
}

小建议:尽量只对 少数关键属性 做过渡,比如 opacitytransform,它们通常性能较好。

10.1.2 从"生硬跳变"到"柔和过渡"

没有 transition 时:

css 复制代码
/* 没有过渡效果的卡片(生硬) */
.card {
  box-shadow: none;  /* 初始状态:无阴影 */
}
.card:hover {
  /* hover状态:突然出现阴影(视觉跳变) */
  box-shadow: 0 10px 30px rgba(0,0,0,.15);
  /*          X偏移 Y偏移 模糊 颜色 */
}

/* 添加过渡效果后(柔和) */
.card {
  box-shadow: none;
  
  /* transition: 让阴影渐变出现,而不是突然跳变 */
  transition: box-shadow 0.25s ease;
  /*          过渡属性 持续0.25秒 缓动曲线 */
}

大脑中的感受大概是:

txt 复制代码
状态 A(无阴影) ────(0.25s 内平滑变化)────> 状态 B(有阴影)
10.1.3 常见 timing-function 曲线

几个常用曲线的感觉:

  • linear:匀速,机械感强,多用于进度条
  • ease:先快后慢,比线性自然(默认值)
  • ease-in:慢慢启动,适合"出现"
  • ease-out:慢慢停下,适合"消失"
  • ease-in-out:两头慢,中间快,更流畅

你可以在 DevTools 的动画面板里直观看到这些曲线,也可以用 cubic-bezier() 自定义,例如:

css 复制代码
.btn {
  /* cubic-bezier: 自定义贝塞尔曲线,精确控制动画节奏 */
  transition: transform 200ms cubic-bezier(0.22, 0.61, 0.36, 1);
  /*                            四个值分别是:x1, y1, x2, y2 */
  /*                            这个曲线比ease更有"弹性"感觉 */
}

这类自定义曲线经常被称为"手感更像 App 的动效曲线"。

10.2 transform:缩放、旋转、位移、倾斜

transform 决定元素在 2D/3D 空间中的几何变换。常见的有:

  • translateX/Y:位移
  • scale:缩放
  • rotate:旋转
  • skew:倾斜
10.2.1 位移:translate
css 复制代码
/* transform: 元素的2D/3D变换,不影响文档流 */

.move-right {
  /* translateX: 水平位移(正值向右,负值向左) */
  transform: translateX(20px);  /* 向右移动20像素 */
}

.move-down {
  /* translateY: 垂直位移(正值向下,负值向上) */
  transform: translateY(10px);  /* 向下移动10像素 */
}

/* 也可以同时位移 */
.move-both {
  /* translate: 同时设置X和Y位移 */
  transform: translate(20px, 10px);  /* 右移20px,下移10px */
}

left/top 的区别:

  • left/top 会影响布局(通常需要配合 position
  • transform: translate 不改变文档流占位,更适合做动效
    left/top 属于定位属性,需要配合 position(如 relative、absolute 等)使用,会改变元素在文档流中的位置,可能影响其他元素的布局。
    transform: translate 只是视觉上移动元素,不会改变元素在文档流中的原始占位,其他元素的布局不会受到影响。
10.2.2 缩放:scale
css 复制代码
/* scale: 缩放变换 */

.zoom-in {
  /* scale: 等比例缩放(1=原始大小) */
  transform: scale(1.05);     /* 放大到105%(1.05倍) */
}

.zoom-x {
  /* scaleX: 仅横向缩放 */
  transform: scaleX(1.2);     /* 宽度变为120%,高度不变 */
}

.zoom-y {
  /* scaleY: 仅纵向缩放 */
  transform: scaleY(0.8);     /* 高度变为80%,宽度不变 */
}

/* 非等比缩放 */
.zoom-custom {
  transform: scale(1.2, 0.8); /* X轴120%,Y轴80% */
}

配合 transition,可以做卡片 hover 的轻微放大:

css 复制代码
.card {
  transition: transform 0.2s ease;
}

.card:hover {
  /* transform: 可以组合多个变换函数 */
  transform: 
    translateY(-4px)    /* 向上浮起4像素 */
    scale(1.02);        /* 轻微放大2% */
  
  /* ⚠️ 变换函数的执行顺序:从左到右书写,但矩阵运算是从右到左 */
  /* 对于这个例子,视觉上差异不大,但复杂变换时顺序很重要 */
  /* 例:rotate(45deg) translateX(100px) 和 translateX(100px) rotate(45deg) 结果完全不同 */
}
10.2.3 旋转与倾斜:rotate、skew
css 复制代码
/* rotate: 旋转变换 */
.rotate {
  /* rotate: 顺时针旋转(负值为逆时针) */
  transform: rotate(5deg);      /* 顺时针旋转5度 */
}

.rotate-reverse {
  transform: rotate(-45deg);    /* 逆时针旋转45度 */
}

/* skew: 倾斜变换 */
.skew {
  /* skewX: 水平倾斜 */
  transform: skewX(10deg);      /* X轴倾斜10度 */
}

.skew-y {
  /* skewY: 垂直倾斜 */
  transform: skewY(5deg);       /* Y轴倾斜5度 */
}

旋转常用在图标按钮(比如刷新、关闭)上;倾斜适合作为装饰效果,慎用在正文内容上,以免影响可读性。

10.2.4 transform 原点:transform-origin

默认情况下,变换的原点是元素的中心:

css 复制代码
.scale-from-top {
  /* transform-origin: 设置变换的基准点(默认是center center) */
  transform-origin: top center;  /* 基准点设为顶部中心 */
  /*                上方位 水平位 */
  
  transform: scaleY(0);          /* 从顶部向下收缩为0 */
}

/* 其他常用基准点 */
.origin-examples {
  transform-origin: center center; /* 默认:中心点 */
  transform-origin: top left;      /* 左上角 */
  transform-origin: bottom right;  /* 右下角 */
  transform-origin: 50% 50%;       /* 百分比定位 */
  transform-origin: 10px 20px;     /* 具体像素位置 */
}

示意:

txt 复制代码
默认原点:中心

   ↑
┌───────┐
│   •   │  ← transform-origin 在中间
└───────┘

改变原点:top center

   •
┌───────┐
│       │
└───────┘

这在做折叠菜单(从上往下展开)时非常常见:

css 复制代码
/* 折叠菜单效果:从上往下展开 */
.collapse {
  transform-origin: top;         /* 变换基准点在顶部 */
  transform: scaleY(0);          /* 初始状态:垂直方向缩为0(隐藏) */
  overflow: hidden;              /* 隐藏溢出内容 */
}

.collapse.open {
  transform: scaleY(1);          /* 展开状态:恢复原始高度 */
  transition: transform 0.2s ease; /* 平滑过渡效果 */
}

10.3 animation 与 keyframes

transition 是在两个状态之间自动补间,而 animation + @keyframes 可以定义多帧动画,更适合:

  • 循环动画(如加载动画)
  • 独立于交互事件的装饰性动效
10.3.1 基本结构
css 复制代码
/* @keyframes: 定义动画的关键帧序列 */
@keyframes pulse {
  0%   { transform: scale(1); }      /* 开始帧:原始大小 */
  50%  { transform: scale(1.05); }   /* 中间帧:放大5% */
  100% { transform: scale(1); }      /* 结束帧:回到原始大小 */
}

.btn-pulse {
  /* animation: 动画属性的简写 */
  animation: pulse 1.2s ease-in-out infinite;
  /*         动画名 时长 缓动函数 循环次数 */
}

animation 简写参数顺序一般为:

css 复制代码
/* animation 完整语法:8个参数 */
animation: name duration timing-function delay iteration-count direction fill-mode play-state;
/*         动画名 时长 缓动函数 延迟 循环次数 播放方向 填充模式 播放状态 */
/* ⚠️ 重要:duration 必须写在 delay 之前,因为两者都是时间值,浏览器靠位置区分 */

常用参数详解:

  • name(动画名称):引用@keyframes定义的动画

    • 例:pulsefade-inslide-up
  • duration(持续时间):动画播放一次的时长

    • 例:1s200ms2.5s
  • timing-function(缓动函数):与transition相同

    • easelinearease-in-out
  • delay(延迟时间):动画开始前的等待时间

    • 例:0s0.5s
  • iteration-count(循环次数):动画重复播放次数

    • 1 - 播放一次
    • 3 - 播放三次
    • infinite - 无限循环
  • direction(播放方向):动画的播放顺序

    • normal - 正常播放(默认)
    • reverse - 反向播放
    • alternate - 交替播放(正反正反...)
    • alternate-reverse - 反向交替
  • fill-mode(填充模式):动画开始前/结束后的状态

    • none - 不保持(默认)
    • forwards - 保持最后一帧状态
    • backwards - 立即应用第一帧状态
    • both - 同时应用forwards和backwards
  • play-state(播放状态):控制动画暂停/播放

    • running - 播放中(默认)
    • paused - 暂停
10.3.2 from / to 写法

单纯"从 A 到 B"的动画,可以用 from / to

css 复制代码
/* from/to语法:简单的两帧动画 */
@keyframes fade-in {
  from { opacity: 0; }  /* from = 0% 开始状态:完全透明 */
  to   { opacity: 1; }  /* to = 100% 结束状态:完全不透明 */
}

.fade-in {
  /* 淡入效果:0.3秒内从透明变为不透明 */
  animation: fade-in 0.3s ease-out;
  /*         动画名 0.3秒 缓出曲线 */
}
10.3.3 一次性入场动画

如果你只希望元素在第一次进入时播放一个动画,而不是一直循环,可以:

  • 在初始状态触发类名(如 JS 加上 .enter
  • animation-fill-mode: forwards 保持结束状态
css 复制代码
/* 上滑淡入动画 */
@keyframes slide-up {
  from { 
    transform: translateY(12px);  /* 从下方12px处开始 */
    opacity: 0;                   /* 初始透明 */
  }
  to { 
    transform: translateY(0);     /* 滑到原位 */
    opacity: 1;                   /* 变为不透明 */
  }
}

.card-enter {
  animation: slide-up 0.3s ease-out forwards;
  /*                                forwards=保持最终状态 */
}

一般来说:交互驱动的小动效 优先考虑 transition
时间驱动/循环/多步骤动画 再考虑 animation

10.4 实战:呼吸按钮、加载动画

这一节用两个小例子把上面的概念串起来。

10.4.1 呼吸按钮(pulse button)

目标效果:按钮轻微放大缩小,像在"呼吸",吸引用户注意但不过分抢眼。

HTML:

html 复制代码
<button class="btn btn-primary btn-pulse">立即开始</button>

CSS(核心部分):

css 复制代码
/* 呼吸按钮基本样式 */
.btn-primary {
  background: #2563eb;
  color: #fff;
  border-radius: 999px;  /* 超大圆角 = 胶囊按钮 */
  padding: 0.6em 1.6em;  /* em单位:随字体大小缩放 */
  box-shadow: 0 10px 30px rgba(37,99,235,.25);
  
  /* 同时过渡两个属性 */
  transition: transform 0.15s ease, 
              box-shadow 0.15s ease;
}

/* hover状态:轻微上浮和放大 */
.btn-primary:hover {
  transform: translateY(-1px) scale(1.02);  /* 上移1px + 放大2% */
  box-shadow: 0 14px 40px rgba(37,99,235,.35); /* 阴影加强 */
}

/* 呼吸动画关键帧 */
@keyframes btn-pulse {
  0%   { 
    transform: scale(1);      /* 原始大小 */
    box-shadow: 0 10px 30px rgba(37,99,235,.25); /* 较小阴影 */
  }
  50%  { 
    transform: scale(1.04);   /* 放大4% */
    box-shadow: 0 14px 40px rgba(37,99,235,.35); /* 较大阴影 */
  }
  100% { 
    transform: scale(1);      /* 回到原始 */
    box-shadow: 0 10px 30px rgba(37,99,235,.25);
  }
}

/* 应用呼吸动画 */
.btn-pulse {
  animation: btn-pulse 1.5s ease-in-out infinite;
  /*         动画名 每次循环1.5秒 缓入缓出 无限循环 */
}

按钮的动线大致可以想象为:

txt 复制代码
scale: 1 → 1.04 → 1
shadow: 较小 → 稍大 → 较小

节奏:  呼───吸───呼───吸───(循环)
10.4.2 加载动画(loading spinner)

目标效果:一个简单的圆环一直旋转,表示"正在加载"。

HTML:

html 复制代码
<div class="spinner" aria-label="Loading"></div>

CSS:

css 复制代码
/* 加载动画:旋转圆环 */
.spinner {
  width: 24px;
  height: 24px;
  border-radius: 50%;                           /* 变成圆形 */
  
  /* 制作圆环:四边框,但只有顶边是亮色 */
  border: 3px solid rgba(148, 163, 184, 0.3);   /* 整体淡灰色边框 */
  border-top-color: #2563eb;                    /* 顶部覆盖为蓝色 */
  
  /* 旋转动画 */
  animation: spin 0.8s linear infinite;
  /*         动画名 0.8秒 匀速 无限循环 */
}

@keyframes spin {
  /* 只需要结束帧:旋转360度(一圈) */
  to { transform: rotate(360deg); }
}

可以用一行文字来记它的本质:

txt 复制代码
圆圈 + 部分高亮 + 持续 rotate(360deg) = 经典 loading-spinner
10.4.3 动效的"度"与性能

最后,再强调两个常被忽略的点:

  • 动效的"度"

    • 动画太快:眨眼就过,看不清变化
    • 动画太慢:让人着急,影响操作效率
    • 一般 UI 交互动效时长常见在 150ms ~ 300ms 之间
  • 性能与属性选择

    • 优先对 opacitytransform 做过渡/动画
    • 避免频繁动画 width/height/top/left 等会触发布局重排的属性

动效的目标,不是"炫技",而是让用户感觉:界面懂我在做什么,并且在温柔地回应我。


第10章带你从过渡、变换到关键帧动画,完成了 CSS 动效的基础拼图。接下来第11章,我们会在此基础上继续探索滤镜与特效,比如毛玻璃、混合模式,让你的页面在细节上更接近成熟产品的视觉质感。

第11章 滤镜与特效

这一章解决的核心问题:

  • 如何用 filter 做出模糊、灰度、亮度调整等效果?
  • mix-blend-mode 是什么?怎么让文字/图层和背景"混色"?
  • backdrop-filter 如何实现毛玻璃(玻璃拟态)效果?
  • 在不牺牲可读性和性能的前提下,用特效提升页面质感。

11.1 filter 滤镜

filter 可以理解为给元素贴上一个"后期滤镜",对其像素进行模糊、变亮、变暗、灰度等处理。

常见的滤镜函数:

  • blur(px):高斯模糊
  • brightness(%):亮度
  • contrast(%):对比度
  • grayscale(%):灰度
  • saturate(%):饱和度
  • sepia(%):褐色(复古效果)
  • drop-shadow():投影(类似 box-shadow,但跟随图形轮廓)
11.1.1 基础示例
css 复制代码
/* filter: CSS滤镜属性,用于图像特效 */

.img-blur {
  /* blur: 高斯模糊效果 */
  filter: blur(4px);        /* 模糊半径4像素 */
}

.img-gray {
  /* grayscale: 灰度滤镜 */
  filter: grayscale(100%);  /* 100% = 完全灰度(黑白照片) */
                            /* 0% = 原始彩色 */
}

.img-dim {
  /* 多个滤镜可以叠加使用 */
  filter: brightness(0.8)   /* 亮度降至80% */
          contrast(1.1);     /* 对比度提升到110% */
}

你也可以链式叠加多个滤镜,执行顺序从左到右:

css 复制代码
.hero-bg {
  /* 链式叠加多个滤镜,按顺序执行 */
  filter: grayscale(30%)     /* 30%灰度(略微脱色) */
          brightness(0.9)     /* 90%亮度(稍暗) */
          saturate(1.1);      /* 110%饱和度(颜色稍浓) */
}
11.1.2 滤镜 vs 直接改颜色

有些效果既可以通过调整颜色属性(如 background-color)实现,也可以通过滤镜实现。

  • filter 的优势:可以对图片或视频整体做后期调整,而不改动源文件
  • 代价:在一些设备上滤镜可能比简单颜色变更更耗性能

常见用法:

  • 给背景图加一点 brightness() 压暗,突出前景文字
  • 给缩略图加 grayscale(100%),hover 时还原彩色
css 复制代码
/* 缩略图悬停彩色效果 */
.thumb {
  filter: grayscale(100%);      /* 默认:完全灰度 */
  transition: filter 0.2s ease;  /* 滤镜过渡效果 */
}

.thumb:hover {
  filter: grayscale(0%);         /* hover:恢复彩色 */
}

11.2 mix-blend-mode 混合模式

mix-blend-mode 用于控制一个元素和其"背后内容"怎样混合颜色,类似于 Photoshop 里的图层混合模式。

常见混合模式:

  • multiply:正片叠底,通常让颜色变暗
  • screen:滤色,让颜色变亮
  • overlay:叠加,对比增强
  • difference:差值,产生反相效果(更偏实验性)
11.2.1 基础用法
css 复制代码
.title-overlay {
  /* mix-blend-mode: 混合模式,控制元素与背景的颜色混合 */
  mix-blend-mode: overlay;  /* overlay = 叠加模式 */
                            /* 效果:增强对比度 */
}

HTML:

html 复制代码
<div class="hero">
  <img src="hero-bg.jpg" class="hero-bg" alt="背景图">
  <h1 class="hero-title title-overlay">混合模式标题</h1>
</div>

简单示意:

txt 复制代码
背景:蓝色渐变
前景:白色文字 + overlay 混合

结果:文字会带一点背景的颜色调子,比纯白更"贴在画面上"。

注意:mix-blend-mode 的最终效果高度依赖背景内容,难以精确控制,一般用于图形/装饰性排版,不适合作为关键 UI 的唯一对比手段。

11.2.2 常见小用法
  • 让 icon/装饰图案跟背景混色,形成更丰富的质感
  • 做横幅(banner)时,让白色大标题与背景渐变融合得更自然

示例:

css 复制代码
.badge-fancy {
  background: #f97316;         /* 橙色背景 */
  
  /* multiply: 正片叠底模式 */
  mix-blend-mode: multiply;    /* 颜色会变暗,更融入背景 */
                               /* 类似在纸上叠加颜料 */
}

在深色背景上,会显得颜色更"沉",而不是刺眼的纯橙色。

11.3 backdrop-filter 毛玻璃效果

backdrop-filter 是实现「玻璃拟态(Glassmorphism)」的关键属性,它不是对元素自身,而是对元素背后的内容应用滤镜。

典型用法:

  • 半透明卡片,背景被模糊
  • 固定在顶部的导航栏,略带模糊背景
11.3.1 基础示例
css 复制代码
/* 玻璃拟态卡片 */
.glass-card {
  /* 半透明背景 */
  background: rgba(255, 255, 255, 0.12);  /* 白色,12%不透明度 */
  
  border-radius: 16px;                    /* 圆角 */
  
  /* 半透明边框增加层次感 */
  border: 1px solid rgba(255, 255, 255, 0.3);
  
  /* backdrop-filter: 对元素背后内容应用滤镜 */
  backdrop-filter: blur(18px);            /* 背景模糊18px */
                                          /* 这是玻璃效果的核心 */
}

视觉结构可以想象为:

txt 复制代码
背景图 / 彩色渐变
   ↓(通过 glass-card 看到时,会被 blur)
半透明玻璃卡片(带 blur 的 backdrop-filter)
   ↓
卡片内部文字和图标(保持清晰)
11.3.2 浏览器支持与兼容性

backdrop-filter 在现代浏览器中支持已经不错,但:

  • 某些老版本浏览器不支持
  • 移动端不同厂商实现可能略有差异

实战建议:

  • .glass-card 提供一个「退化样式」,例如简单的半透明背景,不依赖 blur
  • 在需要时,可以用 @supports (backdrop-filter: blur(10px)) { ... } 做能力检测
css 复制代码
/* 渐进增强:先提供基础样式,再根据浏览器能力增强 */
.glass-card {
  /* 退化样式:普通半透明卡片(不支持backdrop-filter时) */
  background: rgba(15, 23, 42, 0.7);  /* 70%不透明的深色 */
}

/* @supports: 检测浏览器是否支持某个CSS特性 */
@supports (backdrop-filter: blur(16px)) {
  /* 如果支持backdrop-filter */
  .glass-card {
    background: rgba(15, 23, 42, 0.4);    /* 降低不透明度到40% */
    backdrop-filter: blur(16px);          /* 启用玻璃模糊效果 */
    -webkit-backdrop-filter: blur(16px);  /* Safari 17及更早版本兼容(Safari 18+已无需前缀) */
  }
}

11.4 实战:制作玻璃拟态卡片

综合用上一节的知识,做一个常见的玻璃拟态卡片样式。

11.4.1 HTML 结构
html 复制代码
<section class="hero-glass">
  <div class="glass-card">
    <h2 class="glass-title">Pro 版订阅</h2>
    <p class="glass-subtitle">解锁全部 CSS 实战案例与模板</p>
    <button class="btn btn-primary">立即升级</button>
  </div>
</section>
11.4.2 CSS 样式(核心部分)
css 复制代码
.hero-glass {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(circle at 0% 0%, rgba(96, 165, 250, .35), transparent 55%),
    radial-gradient(circle at 100% 100%, rgba(244, 114, 182, .35), transparent 55%),
    #020617; /* 深色背景 */
}

.glass-card {
  max-width: 360px;
  padding: 24px 28px;
  border-radius: 20px;
  border: 1px solid rgba(148, 163, 184, 0.5);
  background: rgba(15, 23, 42, 0.55);
  box-shadow:
    0 18px 40px rgba(15, 23, 42, 0.9),
    0 0 0 1px rgba(148, 163, 184, 0.35);
  backdrop-filter: blur(18px);
}

.glass-title {
  color: #e5e7eb;
  font-size: 1.5rem;
  margin-bottom: 0.25rem;
}

.glass-subtitle {
  color: #cbd5f5;
  font-size: 0.95rem;
  margin-bottom: 1.25rem;
}

结构示意:

txt 复制代码
背景层:彩色渐变 + 深色底
   ↓
玻璃层:半透明 + blur + 边框 + 投影(glass-card)
   ↓
内容层:清晰文字 + 按钮
11.4.3 字体与对比度的注意事项

在做这种特效时,容易只顾好看,忘了"可读性"与"可访问性":

  • 保证文字与背景的对比度足够(尤其是小字号)
  • 对重要信息,避免依赖颜色与模糊效果作为唯一区分
  • 在深浅两套主题中分别检查玻璃拟态卡片的可读性

第11章中,我们用 filtermix-blend-modebackdrop-filter 做了几种常见视觉特效,并完成了一个玻璃拟态卡片。到这里,本篇「强化视觉」从字体、动效到特效已经成型。后面的篇章中,我们会更多地把这些能力带入组件化与实际页面,去构建真正可维护、可复用的 CSS 设计系统。

100vh;

display: flex;

align-items: center;

justify-content: center;

background:

radial-gradient(circle at 0% 0%, rgba(96, 165, 250, .35), transparent 55%),

radial-gradient(circle at 100% 100%, rgba(244, 114, 182, .35), transparent 55%),

#020617; /* 深色背景 */

}

.glass-card {

max-width: 360px;

padding: 24px 28px;

border-radius: 20px;

border: 1px solid rgba(148, 163, 184, 0.5);

background: rgba(15, 23, 42, 0.55);

box-shadow:

0 18px 40px rgba(15, 23, 42, 0.9),

0 0 0 1px rgba(148, 163, 184, 0.35);

backdrop-filter: blur(18px);

}

.glass-title {

color: #e5e7eb;

font-size: 1.5rem;

margin-bottom: 0.25rem;

}

.glass-subtitle {

color: #cbd5f5;

font-size: 0.95rem;

margin-bottom: 1.25rem;

}

复制代码
结构示意:

```txt
背景层:彩色渐变 + 深色底
   ↓
玻璃层:半透明 + blur + 边框 + 投影(glass-card)
   ↓
内容层:清晰文字 + 按钮
11.4.3 字体与对比度的注意事项

在做这种特效时,容易只顾好看,忘了"可读性"与"可访问性":

  • 保证文字与背景的对比度足够(尤其是小字号)
  • 对重要信息,避免依赖颜色与模糊效果作为唯一区分
  • 在深浅两套主题中分别检查玻璃拟态卡片的可读性

第11章中,我们用 filtermix-blend-modebackdrop-filter 做了几种常见视觉特效,并完成了一个玻璃拟态卡片。到这里,本篇「强化视觉」从字体、动效到特效已经成型。后面的篇章中,我们会更多地把这些能力带入组件化与实际页面,去构建真正可维护、可复用的 CSS 设计系统。

相关推荐
开开心心_Every6 小时前
图片转PDF合并工具,支持扫描仪输入
运维·前端·人工智能·随机森林·edge·pdf·逻辑回归
垦利不6 小时前
TS基础篇
开发语言·前端·typescript
cd_949217216 小时前
2026年朝阳永续AI小二专业研投能力解析
前端·人工智能·easyui
FlyWIHTSKY6 小时前
`nth-child()`的 基础用法
前端·html
Ww.xh6 小时前
Figma设计稿转React代码:ClaudeCode+MCP实战教程
前端·react.js·figma
不老刘7 小时前
破局 EMR 痛点:如何化解“护理记录跨页”与“A4物理打印”的架构冲突
前端·架构
m0_738120727 小时前
后渗透维权提权基础——CTF模拟红队进行权限维持(一)
服务器·前端·python·安全·web安全·php
朝阳397 小时前
react【实战】自定义下拉框、单选、多选、输入框
前端·javascript·react.js
ZC跨境爬虫7 小时前
跟着 MDN 学 HTML day_5:(原生table表格语义化搭建+CSS轻量化交互美化全实战)
前端·css·ui·html