☰
CSS角度单位全解析:deg、grad、rad、turn的换算与实战避坑
2026/10/9 21:57:46 网站建设 项目流程

做前端这几年,我踩过最多的坑不是布局,不是兼容性,而是各种不起眼的“值”。比如旋转动画里 rotate(0.5turn) 被写成 rotate(0.5deg),转了半天纹丝不动;再比如渐变里 linear-gradient(200grad, ...) 被当成 200deg 用,出来的颜色方向怎么看怎么别扭。CSS 角度单位看起来就四个词——deg、grad、rad、turn,但换算关系、适用场景、浏览器细节,真掰开揉碎讲,内容比想象中多。这篇就从实际项目里的旋转、渐变、交互动效出发,把四种单位讲清楚,顺便把那些容易踩的坑也一并排掉。

这篇文章适合刚入门前端、正在补 CSS 基础的同学,也适合写了好几年样式但从没用过 turn 和 rad 的朋友。不管你是用<link>引入外部样式,还是直接在<style>里写,角度单位的写法和行为都是一样的,不用在引入方式上纠结,真正需要花心思的是把“值”写对。

1. CSS角度单位到底解决什么问题

1.1 这些单位都会出现在哪

CSS 里的角度单位不是孤立概念,它散落在各种属性里,只是很多时候你没意识到。

最常见的当然是旋转:transform: rotate(45deg),这个不用多说。再往下就是渐变,linear-gradient(90deg, red, blue)里的 90deg 决定渐变线的方向,conic-gradient(from 0.25turn at center, ...)里的 turn 决定起始色相位置。还有滤镜里的hue-rotate(90deg),色相旋转用的也是角度。倾斜用的skewX(10deg)同样是角度。这几年 CSS 还加入了三角函数,sin()、cos()、tan()都接受角度单位,在做圆形排列、波浪布局时会直接派上用场。

也就是说,只要你碰过旋转、渐变、滤镜、形变、动画,就不可能绕开角度单位。问题在于,浏览器并不在乎你用哪个单位,它只认“这个角度对应多少度”,最终渲染结果取决于角度数值本身,而不是单位名称。所以搞清楚四者的换算,不是学院派要求,而是实战刚需。

1.2 四种单位的定义与本质

先给四个单位下个准确定义。

deg 是 degree 的缩写,中文叫“度”,一个圆分成 360 份,每份是 1deg。这是我们最熟悉的单位,量角器、UI 设计稿、日常口语里的“旋转 90 度”,说的都是它。

grad 是 gradian 的缩写,中文常译作“百分度”或“梯度”,一个圆分成 400 份,每份是 1grad。这个单位在测绘、建筑和一些欧洲国家的工程领域用得比较多,但在 CSS 里极其少见,你大概率不会主动用它。它出现在 CSS 规范里,更多是为了保证单位体系的完整性。

rad 是 radian 的缩写,中文叫“弧度”,它的定义是:弧长等于半径时对应的圆心角为 1rad。一个圆的周长是半径的 2π 倍,所以一圈是 2π rad,约等于 6.28318rad。这是数学和编程领域最常用的角度单位,JavaScript 里的Math.sin、Math.cos、canvas绘图全部用弧度。如果你写过 H5 动画、Canvas 特效或者可视化图表,一定对它不陌生。

turn 是“圈”的意思,一个圆就是 1turn。它最符合直觉:转一圈、转半圈、转四分之一圈,说人话就该用 turn。

从本质上看,这四种单位描述的是同一个东西——角度大小。区别只是把“整圆”这块蛋糕切成了不同份数:deg 切成 360 份,grad 切成 400 份,rad 用弧长比例来表达,turn 直接以整圆为 1。生活化一点理解:同样是“半圈”,你可以说 180deg、200grad、约 3.14159rad,也可以直接说 0.5turn。数值差异很大,但物理含义一模一样。

1.3 一圈的换算关系

先给出最核心的等式,建议直接截图保存:

单位一整圈的数值等价换算典型场景
deg3601deg = 1/360 圈UI 设计稿、日常开发
grad4001grad = 0.9deg工程制图、测绘习惯
rad2π(约 6.28318)1rad ≈ 57.2958degJS 数学计算、Canvas
turn11turn = 360deg动画圈数、旋转特效

所以最常用的一组等式就是:

1turn = 360deg = 400grad = 2π rad

这四个写法在 CSS 里完全等价。也就是说,rotate(360deg)、rotate(400grad)、rotate(6.28318rad)、rotate(1turn)渲染出来的效果一模一样。这一点很多人刚开始是不信的,但浏览器确实会先把角度统一换算,再输出旋转结果。

2. 换算推导与项目里的单位选择

2.1 换算推导

上一节的等式可以直接背,但我建议你还是理解一下推导,因为实战里经常要做反向换算,光背容易记反。

既然一圈等于 360deg,也等于 400grad,那么:

1deg = 400 / 360 grad = 10 / 9 grad ≈ 1.1111grad 1grad = 360 / 400 deg = 0.9deg

注意方向别搞混。很多人只知道“grad 和 deg 差不多”,结果把 200grad 当成 200deg 用,实际 200grad = 180deg,角度直接偏差 10%。

再看 rad。一圈是 2π rad,反推可得:

1rad = 360 / (2π) deg = 180 / π deg ≈ 57.2958deg 1deg = π / 180 rad ≈ 0.017453rad

如果你在 JS 里要做转换,可以用这两行工具函数:

const degToRad = (deg) => deg * Math.PI / 180; const radToDeg = (rad) => rad * 180 / Math.PI;

这一点在 Canvas 和 CSS 混用的项目里尤其重要。Canvas 的arc、rotate方法只认弧度,CSS 的 transform 却习惯写度数,两套体系来回切的时候,漏掉转换就会让图形旋转方向完全乱掉。

2.2 项目里怎么选单位

四种单位功能等价,但可读性和团队协作成本差别很大。我的习惯是这样:

第一,常规 UI 开发默认用 deg。设计稿上标的是度数,团队成员脑子里想的也是度数,代码里写rotate(45deg)谁都能看懂。换 grad 反而要解释半天。

第二,动画涉及“圈数”时优先用 turn。比如一个无限旋转的 loading 图标,@keyframes里写from { transform: rotate(0turn); } to { transform: rotate(1turn); },一眼就知道转一圈。写rotate(360deg)也不是不行,但如果是转两圈半,rotate(2.5turn)明显比rotate(900deg)直观得多。

第三,和 JS 计算打通时用 rad。如果你从Math.atan2拿到一个弧度值,直接拼到 CSS 变量里是最省事的,没必要转成度数再塞回去。CSS 变量是字符串,rotate(${rad}rad)完全合法。

第四,grad 在 Web 前端项目里基本不用。CSS 规范保留它纯粹是历史原因。如果你在代码里看到 grad,第一反应应该是“有人写错了或者有特殊行业背景”,而不是默默接受。

2.3 CSS变量与calc中的角度单位

现代 CSS 里角度单位可以放进变量,也能参与运算。比如:

:root { --base-angle: 45deg; --spin: 0.5turn; } .box { transform: rotate(var(--base-angle)); } .box-spin { animation: spin 2s linear infinite; } @keyframes spin { to { transform: rotate(var(--spin)); } }

更妙的是,CSS 的calc()允许角度类型内部混合运算。因为 deg、grad、rad、turn 都属于<angle>类型,浏览器会统一换算,所以下面这种写法是合法的:

.box { transform: rotate(calc(45deg + 0.25turn)); }

最终结果是45deg + 90deg = 135deg。这在做“基础角度 + 增量角度”的动效时很好用。不过要提醒一句:老浏览器对calc()与不同类型角度混合的兼容性一般,保守项目里还是尽量统一单位,不要为了炫技给自己挖坑。

3. 实操场景:渐变、旋转、动画与交互

3.1 渐变角度:linear-gradient 与 conic-gradient 的坑

渐变是角度单位最容易翻车的地方。linear-gradient的角度表示渐变线的方向,它的 0deg 指向正上方,角度顺时针增加。简单记就是:

0deg = to top 90deg = to right 180deg = to bottom 270deg = to left

默认值是 180deg,也就是从上到下的渐变。很多人会误以为linear-gradient(45deg)是从右下角到左上角,其实它指的是渐变线方向朝向右上,颜色沿垂直渐变线的方向过渡。刚开始不熟悉,可以先写to top right这种关键词,再换成角度。

conic-gradient的用法更直接,它从某个起始角开始绕圆心铺颜色:

.conic { background: conic-gradient(from 0.25turn at 50% 50%, red, yellow, lime, blue); }

这里0.25turn就代表从 90 度方向开始,顺时针铺一圈。用 turn 写的好处是能直接看出起点在圆的“四分之一处”,比写 90deg 更有画面感。

另外说一个非常常见的需求:字体渐变。代码如下:

.text-gradient { background: linear-gradient(120deg, #f00, #00f); -webkit-background-clip: text; background-clip: text; color: transparent; }

这套写法里,120deg 直接决定文字颜色从哪个方向开始过渡。想要文字高光从左上到右下,可以试试145deg;想要从上到下,就用180deg。角度不同,视觉效果差很多,建议多调几个值对比感受一下。

3.2 transform旋转与不同单位的等效写法

旋转用transform: rotate()时,四种单位可以任意切换。下面四个类名效果完全相同:

.rotate-deg { transform: rotate(360deg); } .rotate-grad { transform: rotate(400grad); } .rotate-rad { transform: rotate(6.28318rad); } .rotate-turn { transform: rotate(1turn); }

注意,CSS 里旋转的正方向是顺时针,负值是逆时针。rotate(-90deg)等于逆时针转 90 度,也等于rotate(0.75turn)。这在做菜单展开、箭头方向切换时非常常见。

还有一个容易忽略的点:rotate()是围绕transform-origin旋转的,默认原点是元素中心。如果你改变了原点,比如transform-origin: left top,那么同样rotate(0.25turn)的结果是围绕左上角转,视觉差异非常明显。排查旋转类 bug 时,先确认原点,再看角度单位,顺序不能反。

3.3 animation里turn的优势

写动画时 turn 的优势会被放大。看一个加载动画:

.spinner { width: 40px; height: 40px; border: 4px solid #ddd; border-top-color: #333; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { from { transform: rotate(0turn); } to { transform: rotate(1turn); } }

这里如果用rotate(360deg),表达力其实也够。但如果你想要一个转两圈半再停下来的动效,rotate(2.5turn)和rotate(900deg)的可读性差距就出来了。在关键帧里写圈数,能让任何接手代码的人秒懂动画逻辑。

配合alternate交替模式,turn 还能做摆动效果:

@keyframes swing { from { transform: rotate(-0.125turn); } to { transform: rotate(0.125turn); } } .element { animation: swing 1s ease-in-out infinite alternate; }

0.125turn就是 45 度,用来做钟摆、摇晃、指针摆动都合适,比写-45deg、45deg更有节奏感。

3.4 鼠标移入小案例:旋转加涟漪光圈扩散

把角度单位和交互结合,最典型的是“鼠标移入后图标旋转”的效果。CSS 里处理鼠标移入用:hover伪类就能实现,不需要写 JavaScript 事件。下面这个按钮,鼠标移入时图标转四分之一圈,同时背景冒出一圈涟漪光圈扩散效果,正好把transform、角度单位和伪元素过渡串起来。

<button class="action-btn"> <span class="btn-icon">↻</span> <span class="btn-text">刷新</span> </button>
.action-btn { position: relative; overflow: hidden; display: inline-flex; align-items: center; gap: 8px; padding: 12px 24px; border: none; border-radius: 8px; background: #2d6cdf; color: #fff; cursor: pointer; } .btn-icon { display: inline-block; transition: transform 0.4s ease; } .action-btn:hover .btn-icon { transform: rotate(0.25turn); } .action-btn::after { content: ""; position: absolute; left: 50%; top: 50%; width: 120px; height: 120px; border-radius: 50%; background: radial-gradient(circle, rgba(255, 255, 255, 0.6), transparent 70%); opacity: 0; transform: translate(-50%, -50%) scale(0); transition: transform 0.6s ease, opacity 0.6s ease; } .action-btn:hover::after { opacity: 1; transform: translate(-50%, -50%) scale(2.5); }

这里0.25turn就是 90 度,但比90deg更能表达“轻轻转一下”的动态幅度。顺便说一句,按钮里的图标和文字要在一行对齐,用display: inline-flex加align-items: center是最省事的方案,比手动调vertical-align和line-height稳得多。很多人给图片和文字排一行时拼命调 margin,其实问题出在父容器的对齐方式上。

3.5 滤镜与三角函数中的角度单位

角度单位不只在形变里出现。filter: hue-rotate(90deg)会让元素整体色相旋转 90 度,hue-rotate(0.5turn)则旋转半圈,效果相当于把颜色往互补方向拉。做主题切换、状态提示时,这个属性配合 turn 写起来很直观:

.success-box { filter: hue-rotate(0turn); } .warning-box { filter: hue-rotate(0.25turn); }

另外,CSS 原生三角函数也接受角度单位。比如想做一个宽度随角度变化的正方形:

.dynamic-width { --angle: 30deg; width: calc(100px * cos(var(--angle))); }

cos(30deg)返回约 0.866,所以宽度约 86.6px。这个能力在实现圆形菜单、波浪布局、齿轮齿条效果时非常有用。不过要注意兼容性,现阶段更稳妥的做法是先用 JavaScript 算好数值,再通过 CSS 变量传入,避免在关键业务页里直接依赖新特性。

4. 常见问题与排查技巧实录

4.1 deg 与 grad 混用:角度直接偏差10%

这是我在 review 代码时见过最多的问题。有人从某个老项目里复制了一段linear-gradient(100grad, ...),把它当成 100 度直接用,结果渐变方向和预期偏差 10 度,肉眼看不大,但沿着边缘切分的时候就很明显。

排查思路很简单:凡是看到 grad,先把数值乘以 0.9 换成 deg 再判断。100grad = 90deg,200grad = 180deg,400grad = 360deg。

避免混用,靠的是约定。团队内部可以规定:业务代码只管用 deg 和 turn,除非有特殊需求,禁止出现 grad。这个约定成本几乎为零,能省掉大量排查时间。

4.2 rad 数值“看着太小”导致误判

rad 的数值在视觉上很小,一圈才 6.28,半圈 3.14,90 度大约是 1.57。这就导致很多人看到rotate(1rad)时觉得“转了 1 个单位,应该差不多 90 度”,实际它只有约 57.3 度。想转 90 度,对应的弧度值是Math.PI / 2,约 1.5708rad,而不是 1.5rad。

如果你在 CSS 里直接写rotate(1.5rad),实际角度只有约 85.94 度,转不足 90 度。这种误差在视觉上很隐蔽,但在连续拼接的路径动画里会越积越大。

建议做法是:CSS 侧尽量不直接手写 rad 数值,要么用 deg,要么通过 JS 计算后把弧度值拼进样式:

const angleRad = Math.atan2(dy, dx); element.style.transform = `rotate(${angleRad}rad)`;

4.3 turn 与 transform-origin 的配合

turn虽然直观,但容易让人忽略“旋转是围绕哪个点进行的”。默认原点是元素中心,rotate(0.25turn)是绕中心顺时针转 90 度;如果你需要绕右下角旋转,必须显式声明:

.box { transform-origin: right bottom; transform: rotate(0.25turn); }

现实项目里,仪表盘指针、雷达扫描、时钟指针都依赖自定义原点。雷达扫描这类效果还会结合圆锥渐变和 mask,角度单位在整个链路里都起着关键作用。

另外还有一个隐藏坑:多个 transform 组合时,旋转会改变后续位移的坐标系。translateX(20px) rotate(0.25turn)和rotate(0.25turn) translateX(20px)结果完全不同。前者先平移再旋转,后者先旋转再沿旋转后的坐标轴平移。排查时如果发现元素位置诡异,除了看角度数值,还要看 transform 的书写顺序。

4.4 动态角度:JS与CSS变量协作

动态角度需求很常见:拖拽旋转、鼠标跟随、进度环、游戏摇杆。我的建议是尽量通过 CSS 变量把 JS 计算出的角度传给样式,而不是直接操作el.style.transform里的字符串拼接,这样动画层和逻辑层更干净。

const handleDrag = (event) => { const rect = element.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const angle = Math.atan2(event.clientY - centerY, event.clientX - centerX); element.style.setProperty('--drag-angle', `${angle}rad`); };
.element { transform: rotate(var(--drag-angle)); }

这里Math.atan2返回弧度值,CSS 变量直接接收rad字符串,中间不需要任何度数转换,从源头避免单位错乱。

4.5 兼容性速查与降级方案

四种角度单位在现代浏览器里都能正常使用,但部署到真实项目时还是要留心老环境。turn在非常老的浏览器里支持度一般,IE 基本不认识;grad虽然历史更早,但渲染引擎对它的精度处理普遍不上心,容易出现微小偏差;deg是最稳的选择。

如果项目还要兼容旧浏览器,建议按这个策略降级:

.box { transform: rotate(90deg); /* 降级路径,所有浏览器认识 */ transform: rotate(0.25turn); /* 现代浏览器覆盖 */ }

第二行会覆盖第一行,但前提是浏览器认识0.25turn。不认识时它会忽略第二行,保留第一行的 90deg。这套写法的好处是渐进增强,不用等所有用户升级浏览器就能平稳过渡。

CSS 变量同理,老浏览器不认变量时,可以先给属性写一个默认值再补变量版本:

.box { transform: rotate(45deg); transform: rotate(var(--angle, 45deg)); }

最后想聊一点个人体会。入门前端那会儿,我也觉得角度单位这种“小事”没必要专门研究,反正设计稿给度数就写度数。后来做可视化项目,需要在 Canvas、SVG、CSS 三个体系之间同步旋转角度,才真正体会到底层单位不统一有多痛苦。现在我的默认选择只有两个:给 UI 和动画用 deg 加 turn,给 JS 计算用 rad。grad 基本不碰。这种“少而明确”的单位策略,帮我省掉了大量调试时间。如果你正在做类似的项目,建议也在团队里约定一套角度单位的用法,并且把它写进代码规范里,这比遇到问题再临时换算靠谱得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询