环形菜单坐标计算必须用 math.sin 和 math.cos,因为其本质是极坐标转直角坐标:cos 得 x 偏移,sin 得 y 偏移(css 中需取负以匹配逆时针视觉);js 三角函数只接受弧度,角度须转为 math.pi/180 倍数;圆心基于父容器 center 定位,子项用 absolute + left/top 计算;起始角如 12 点方向为 -math.pi/2;纯 css 无法运算,需 js 计算后通过 css 自定义属性传参并配合 calc 使用;transform rotate+translate 易错位,推荐绝对定位;响应式需动态适配半径与圆心,确保触摸区域≥44px。

环形菜单坐标计算为什么必须用 Math.sin 和 Math.cos
因为环形布局本质是极坐标转直角坐标:每个菜单项在圆周上的位置由角度 θ 决定,而 Math.cos(θ) 给出 x 偏移,Math.sin(θ) 给出 y 偏移(注意:CSS y 轴向下为正,所以实际要用 -Math.sin(θ) 才符合视觉上“逆时针展开”的直觉)。
常见错误是直接用角度值当像素偏移,或混淆弧度/角度单位——JavaScript 三角函数只认弧度,30 度得写成 Math.PI / 6,不是 30。
- 圆心设为父容器
transform-origin中心点(如50% 50%),所有子项用position: absolute定位 - 每个子项的
left=centerX + radius * Math.cos(angleRad),top=centerY - radius * Math.sin(angleRad) - 角度等分要从起始偏移考虑:比如想第一个菜单在正上方(12点钟方向),对应角度是
-Math.PI / 2,不是0
用 CSS 自定义属性配合 JS 动态生成坐标的实操方式
纯 CSS 无法运行三角函数,必须用 JS 计算坐标后注入 style 或 CSS 变量。推荐用 CSS 自定义属性传参,让动画和布局分离:
- 给菜单容器加
style="--item-count: 5; --radius: 120px; --start-angle: -90;" - JS 读取这些变量:
const count = parseInt(getComputedStyle(el).getPropertyValue('--item-count')) - 循环计算每个项的角度(弧度):
const rad = (startDeg + i * 360 / count) * Math.PI / 180 - 用
el.style.setProperty('--x', (radius * Math.cos(rad)).toFixed(2) + 'px')等设置变量,CSS 里用left: calc(50% + var(--x))
这样动画可完全用 CSS 过渡 left/top,JS 只负责初始化坐标,不参与每一帧渲染。
transform: rotate() 和绝对定位坐标的区别与选型
有人尝试用 transform: rotate() + translate() 实现环形,但要注意:旋转中心默认是元素自身中心,叠加多次 transform 容易错位;而绝对定位坐标是直接锚定到父容器,更可控、调试更直观。
- 如果菜单项尺寸统一且需旋转文字朝向切线方向,才考虑
rotate()配合translate—— 此时需额外计算文字旋转角度:angleRad + Math.PI / 2 - 若只是展开收起动效,用
opacity和scale配合已算好的left/top更稳定 - 避免在
transition中同时过渡transform和left/top,会触发 layout,优先全用transform(即改用translateX/translateY)
响应式环形菜单容易被忽略的三个坑
环形菜单在 resize 或移动端常崩,核心问题不在三角函数,而在坐标基准没随视口变化:
- 半径
--radius不能写死像素值,建议用vmin单位或 JS 监听resize重算(尤其 iPad 分屏时) - 圆心坐标依赖父容器宽高,若父容器用
flex或grid布局,确保其height不为auto,否则50%计算失效 - 手机端触摸区域小,每个菜单项至少保证
44px × 44px可点击区,可用padding扩展 hit area,但不要影响transform-origin位置
真正麻烦的从来不是 sin/cos 公式本身,而是把数学结果稳稳落到不同尺寸、不同缩放、不同设备的像素网格上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











