30-seconds-of-css 实战:用纯 CSS 实现汉堡菜单到叉号按钮的悬停过渡动画

原创2026-10-03 10:46:471,453 阅读
文章标签:前端

30-seconds-of-css 实战:用纯 CSS 实现汉堡菜单到叉号按钮的悬停过渡动画

导读

本文基于 30-seconds-of-css 仓库中的 hamburger-button.md 片段,系统讲解如何仅用 HTML 与 CSS(零 JavaScript)实现经典的"汉堡菜单(Hamburger Menu)"在悬停时平滑过渡为"叉号(Cross)"的交互效果。你将掌握 flex 弹性布局下的三横条结构搭建、transform: rotate() 与 transform-origin 的旋转轴心控制,以及 transition 过渡动画的完整用法,并能在此基础上自由定制尺寸、颜色与动画时长,直接应用到导航栏、移动端菜单按钮等真实场景。

效果概述:从三横线到叉号

汉堡按钮是移动端界面中用于唤起菜单的通用视觉符号——三条水平短杠。本片段实现的是:鼠标悬停(:hover)时,上下两条横杠分别旋转 45° 与 -45° 形成叉号的两条斜边,中间的横杠淡出,从而呈现从"菜单图标"到"关闭按钮"的形态切换。

初始状态        hover 状态
━━━              ╲
━━━      →        ·
━━━              ╱

整个过程完全由 CSS 完成,无需 JavaScript 事件监听,代码量少、无依赖、可直接内联到任意页面,契合 30-seconds-of-css 项目"Short CSS code snippets for all your development needs"的定位。

HTML 结构:容器 + 三条横杠

片段给出的标记非常简洁,一个 .hamburger-menu 容器内包裹三个子元素,分别代表顶部、中部与底部横杠:

<div class="hamburger-menu">
  <div class="bar top"></div>
  <div class="bar middle"></div>
  <div class="bar bottom"></div>
</div>

结构要点:

  • 容器 div 负责整体布局与尺寸,同时作为 :hover 的触发区域(鼠标进入容器任意位置即触发动画);
  • 三个 .bar 子元素分别通过 top、middle、bottom 三个附加类进行差异化控制——悬停时只有 top 和 bottom 发生旋转,middle 负责淡出;
  • 该片段不依赖 JavaScript,也无需表单语义;如果需要更高的无障碍水平,可自行在外层包裹 <button> 或添加 aria-label,但原片段本身刻意保持最小化。

容器布局:用 flex 让三根横杠自动均匀分布

容器部分的核心在于 flex 布局的轴对齐能力:

.hamburger-menu {
  display: flex;
  flex-flow: column wrap;
  justify-content: space-between;
  height: 2.5rem;
  width: 2.5rem;
  cursor: pointer;
}

逐个属性拆解:

属性 作用
display: flex 将容器变为弹性盒子,子元素沿主轴排列
flex-flow: column wrap 主轴方向设为纵向(column),允许换行(wrap),是 flex-direction 与 flex-wrap 的简写
justify-content: space-between 在纵轴上把三条横杠均匀分布,首尾两端贴齐容器上下边缘,中间横杠居中——这是保证三条横杠"自动有间距"的关键,无需手写 margin
height / width: 2.5rem 固定容器为正方形(40px),决定按钮的点击热区与整体尺寸
cursor: pointer 悬停时显示手型光标,提示用户可点击

之所以使用 justify-content: space-between 而非 flex-direction: column 加固定 gap,是因为它能在不显式计算间距的情况下,让三条横杠在容器高度内自动等距分布,是这类"三段式"图标最省事的排布方案。

横杠样式:圆角胶囊 + 左端旋转轴心

每条横杠的公共样式如下:

.hamburger-menu .bar {
  height: 5px;
  background: black;
  border-radius: 5px;
  margin: 3px 0px;
  transform-origin: left;
  transition: all 0.5s;
}

关键点:

  • height: 5px:横杠厚度,容器 40px 高度除以三根杠加间距,视觉上比例适中;宽度未显式设置,由 flex 布局自动拉伸填满容器;
  • border-radius: 5px:圆角取与高度相同的值,形成两端浑圆的胶囊形横杠,更接近现代 UI 风格;
  • margin: 3px 0px:为横杠提供微小的纵向呼吸空间,避免与容器边缘贴死;
  • transform-origin: left:将变换原点设到元素左端。旋转默认绕元素几何中心进行,而本片段需要上下两条横杠围绕左端旋转,才能形成叉号两条边交汇于容器中心偏左处的视觉效果——这是整个动画观感正确的核心属性;
  • transition: all 0.5s:对元素的所有可动画属性(旋转、透明度等)施加 0.5 秒的过渡,all 是简写,实际生效属性主要为 transform 与 opacity。

悬停动画:上下旋转 45°,中间淡出

动画逻辑全部集中在容器处于 :hover 状态时的三条规则中:

.hamburger-menu:hover .top {
  transform: rotate(45deg);
}

.hamburger-menu:hover .middle {
  opacity: 0;
}

.hamburger-menu:hover .bottom {
  transform: rotate(-45deg);
}

机制说明:

  • .top 旋转 45°:围绕左端顺时针旋转 45°,原本水平的横杠变为向右下倾斜的斜线;
  • .bottom 旋转 -45°:围绕左端逆时针旋转 45°,变为向右上倾斜的斜线;两条斜线与容器左边缘的交点重合,共同构成"×"的左半边;
  • .middle 透明度归零:中间横杠通过 opacity: 0 淡出。之所以用 opacity 而不是 display: none,是因为 display 不可参与过渡动画,而 opacity 可以由 transition: all 0.5s 平滑驱动;
  • 由于 .bar 上已经声明了 transition: all 0.5s,悬停时 transform 与 opacity 的变化都会以 0.5 秒的时长平滑过渡,无需在 hover 规则中重复声明。

与仓库中其他依赖 transform-origin 的片段相互印证:例如 hover-underline-animation.md 同样使用 transform: scaleX(0) 加 transform-origin 实现从左侧展开的下划线动画,可见"控制变换原点 + 过渡变换属性"是 30-seconds-of-css 中制作悬停微交互的通用范式。

完整可运行代码

将以下代码合并保存为一个 HTML 文件,用浏览器打开即可看到效果(无需构建工具,无需 JavaScript):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hamburger Button 演示</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      margin: 0;
    }

    .hamburger-menu {
      display: flex;
      flex-flow: column wrap;
      justify-content: space-between;
      height: 2.5rem;
      width: 2.5rem;
      cursor: pointer;
    }

    .hamburger-menu .bar {
      height: 5px;
      background: black;
      border-radius: 5px;
      margin: 3px 0px;
      transform-origin: left;
      transition: all 0.5s;
    }

    .hamburger-menu:hover .top {
      transform: rotate(45deg);
    }

    .hamburger-menu:hover .middle {
      opacity: 0;
    }

    .hamburger-menu:hover .bottom {
      transform: rotate(-45deg);
    }
  </style>
</head>
<body>
  <div class="hamburger-menu">
    <div class="bar top"></div>
    <div class="bar middle"></div>
    <div class="bar bottom"></div>
  </div>
</body>
</html>

外层 body 的 flex 居中只是为了演示方便,可替换为任意实际布局。

定制与进阶

调整尺寸与配色

  • 按钮整体大小:修改容器的 width 与 height(如改为 3rem),横杠会随 flex 布局自动拉伸;
  • 横杠粗细:调整 .bar 的 height(如 6px),同时将 border-radius 改为与高度一致的值以保持胶囊造型;
  • 颜色:替换 .bar 的 background(如 #0087ca),注意与页面背景保持足够对比度。

调整动画节奏

  • 时长:修改 .bar 的 transition: all 0.5s 中的 0.5s,更短(如 0.3s)显得干脆,更长(如 0.8s)显得柔和;
  • 缓动函数:可将 all 0.5s 扩展为 all 0.5s ease-in-out 等缓动曲线,改善动画手感;
  • 旋转角度:理论上可用其他角度组合,但 45° 与 -45° 是构成标准叉号的最优选择,角度偏差会破坏两条斜边的对称性。

改造为点击切换

原片段基于 :hover 设计,若希望按钮"点击一次变为叉号、再点恢复",需要引入少量 JavaScript 切换类名(例如给容器添加 .active 类,并将 :hover 规则改写为 .active 规则);仅用纯 CSS 时,:hover 是达成该效果最简洁的触发器。

与其他片段的组合

  • 结合 triangle.md 中"利用 border 与旋转构造纯 CSS 图形"的思路,可将横杠替换为其他形状,探索更多图标变体;
  • 参考 button-border-animation.md 的 ::before/::after 技巧,也可以把三条横杠改造成两个伪元素实现的极简版本(上下两条由伪元素绘制、中间一条隐藏),从而进一步减少 DOM 节点。

仓库定位与本片段的适用说明

本片段来自 30-seconds-of-css 仓库的 snippets 目录,每个片段均为遵循 snippet-template.md 格式的独立 Markdown 文档,由统一的 frontmatter(title、type: snippet、tags、dateModified)描述元信息。本片段标记为 tags: <a href="https://link.gitcode.com/i/5b9aa62d4550ac196f5c499047a9dc40" target="_blank">interactivity],即归入"交互性"类别;仓库的 [CONTRIBUTING.md 明确规定片段需解决真实世界问题且足够抽象、可直接套用到不同场景,这正是本片段"三段式图标 + 悬停过渡"能够被广泛复用的原因。

需要说明的适用前提:

  • 效果依赖 hover 能力,在触屏设备上不存在鼠标悬停状态,需配合点击/触摸方案使用(如上述 .active 类改造);
  • transform、opacity、transition 均属于现代浏览器广泛支持的 CSS 特性,无需前缀即可工作于当前主流版本;对旧浏览器(如 IE)如需支持,可考虑添加 -webkit- 前缀的过渡与变换声明;
  • 片段本身以最小实现为原则,未包含无障碍与键盘交互处理,用于生产环境时应结合 button 语义与 aria-expanded 等属性做增强。

小结

本文完整还原了 30-seconds-of-css 中 hamburger-button.md 的核心实现:用 display: flex 与 justify-content: space-between 自动排布三条横杠,用 transform-origin: left 控制旋转轴心,用 transform: rotate(±45deg) 与 opacity: 0 在 :hover 时把汉堡图标平滑过渡为叉号,全程零 JavaScript。掌握这一组合后,你不仅能照搬该按钮,更能将"变换原点 + 过渡动画"的思路迁移到更多悬停微交互设计中。

登录后查看全文
30-seconds-of-css