扇形菜单核心是用transform: rotate() + translateX()让li沿圆周等距分布,需设transform-origin: 0 0,并反向旋转矫正文字方向;动画靠CSS过渡控制opacity和transform;可用label+checkbox纯CSS实现交互;响应式需用vw/clamp()替代固定像素值。

用 transform: rotate() + translateX() 把菜单项“甩”到圆周上
扇形菜单不是靠图片拼的,核心是让每个 li 沿圆周等距分布。做法是:先绕原点旋转角度,再沿 X 轴平移半径距离。比如共 6 个菜单项,每项间隔 60deg,第 i 项(从 0 开始)就写 transform: rotate(60deg * i) translateX(120px)。
必须统一设置 transform-origin: 0 0,否则旋转轴会随元素默认位置偏移,导致所有项叠在圆心或错位。常见错误是漏写这句,或者写成 50% 50% —— 那样旋转中心在自身中心,不是容器原点,后续 translateX() 就不朝向圆周了。
文字会跟着歪斜,所以得在 translateX() 后补一句反向旋转:rotate(-60deg * i)。否则用户看到的是斜着的按钮文字,可读性差。
初始状态必须缩在圆心,靠 opacity 和 transform 控制展开动画
展开动画不是 JS 插入 DOM,而是 CSS 过渡切换状态。所有 li 初始样式应为:opacity: 0; transform: rotate(...) translateX(0)(即只转不移,停在圆心)。
点击后统一加类,改 translateX(120px) 并设 opacity: 1。过渡必须明确写:transition: opacity 0.3s, transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)。
不能写 transition: all,否则可能触发 layout(重排),尤其在 Safari 上卡顿明显。另外收起时若直接移除类,动画常被跳过——得先强制 reflow,比如读一次 offsetHeight,再移类。
用 label + input[type="checkbox"] 实现纯 CSS 交互,避开 JS 状态管理
如果不需要动态焦点控制或键盘导航,label 关联 input[type="checkbox"] 是最轻量的开关方案。关键点有三个:
-
input必须有id="menubtn",对应label的for="menubtn",否则点击无响应 -
input的type必须是checkbox,radio或hidden都不行——只有 checkbox 支持 :checked 伪类联动 - 菜单容器(如
.menu-list)要用:checked ~ .menu-list或:checked + .menu-list触发显示,不能依赖子选择器,因为input通常不在菜单 DOM 内部
这种写法免去了 JS 绑定、状态维护和可访问性属性(如 aria-expanded)的手动同步,但牺牲了键盘焦点流转和屏幕阅读器语义支持。
响应式降级时,translateX() 的像素值必须用 vw 或 calc() 动态适配
写死 translateX(120px) 在小屏上会撑破视口,大屏又显得太紧凑。更稳妥的是把半径换成相对单位:
- 用
translateX(calc(50vw - 50px))(假设容器宽 100px,想让它沿半径为视口一半的圆排列) - 或结合
clamp():translateX(clamp(80px, 20vw, 160px)),兼顾最小/最大安全距离 - 若用 JS 计算,注意 resize 时要防抖,且需监听
orientationchange(iOS Safari 不触发 resize)
另一个易忽略点:扇形菜单的圆心角总和未必是 360°,比如只展开半圆(180°),那每个项的旋转步长就是 180deg / (n-1),不是 360deg / n。算错会导致最后一项悬空或重叠。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











