☰
用 CSS 实现沿圆形轨道平滑环绕动画:You-need-to-know-css 的 circular smooth 技巧详解
2026/10/13 2:54:43 网站建设 项目流程
  • 前端
  • 文档
  • 教程

【免费下载链接】You-need-to-know-css

💄CSS tricks for web developers~

项目地址:https://gitcode.com/gh_mirrors/yo/You-need-to-know-css
点击查看免费下载

本技巧出自 You-need-to-know-css 仓库 "Transition & Animations" 分类下的 circular smooth 演示(英文文档 circular-smooth.md,中文文档 zh-cn/circular-smooth.md)。它不依赖 SVG、Canvas 或 JavaScript,仅用animation+transform的链式rotate()/translate()组合,就让多个头像元素绕固定圆心公转,同时保持自身朝向始终不变;再借助负值animation-delay让多个元素等间距错峰,配合animation-play-state悬停暂停与气泡提示完成交互。读完本文你将掌握:用translate()替代transform-origin实现"绕任意点旋转"的底层原理、环形轨道动画的关键帧写法,以及多元素等分轨道的数学计算与参数调优方法。

效果与适用场景

该演示的最终效果是:在一个 300px × 300px 的圆形区域内,Vue.js 的 Logo 静止居中,周围 5 张开发者头像(Evan You、Damian Dulisz、defcc、Edd Yerburgh、Sarah Drasner)以 7.5 秒为周期沿圆周匀速环绕,且任意时刻 5 个头像都均匀分布在圆环上(彼此间隔 72°);鼠标悬停到圆形区域时,环绕动画立即暂停,每个头像上方弹出对应的姓名气泡,移出后动画继续。

这类效果非常适合以下场景:

  • 用户/成员头像墙:团队成员或粉丝头像绕主视觉旋转,形成"众星拱月"的动感氛围;
  • 环形菜单 / 仪表盘:选项沿圆弧均匀排布并缓慢旋转,悬停时定格查看;
  • 加载指示器 / 动效装饰:多元素沿轨道平滑运动,比单一旋转的 spinner 更有层次感;
  • 任何"元素绕固定圆心公转且不自转"的需求:例如行星轨道演示、卫星环绕动效。

前置知识:四条 CSS 基础能力

在进入代码之前,先明确该技巧依赖的四项 CSS 能力(原文档在开头列出了它们作为背景知识):

能力作用在本示例中的具体用法
animation定义动画的名称、时长、循环次数与缓动函数animation: circular-smooth-spin 7.5s infinite linear
transform对元素做旋转、平移、缩放等几何变换链式rotate() translateY() rotate()组合
transition在属性值变化时平滑过渡气泡的opacity与transform: scale()渐变
animation-delay延迟动画开始时间,支持负值-1.5s / -3s / -4.5s / -6s实现等间距错峰

这四条全部是 CSS 的长期稳定特性,也是仓库中 index.html 的 keywords 元数据所声明的技术栈;具体浏览器兼容情况见本文末尾的"浏览器支持"小节。

核心原理:用三层 transform 组合代替 transform-origin

原文档引用了 James Anderson 的一句关键论断:

"Transform-origin is just a syntactic sugar. In fact, you can always use translate() instead."(transform-origin 只是一个语法糖,事实上你总是可以用 translate() 来代替它。)

这句话正是整个技巧的理论起点:transform-origin只是告诉浏览器"绕元素上的哪个点做变换",而任何"绕任意点旋转"的变换,都可以用"先平移到该点 → 旋转 → 平移回去"三步等价表达。本示例反其道而行之——不设置任何transform-origin(保持默认的 50% 50%),而是把旋转和平移写进一条transform链里。

关键帧拆解:公转与自转的抵消

环形轨道的全部秘密都在这段@keyframes中(来自 circular-smooth.md):

@keyframes circular-smooth-spin { from { transform: rotate(0turn) translateY(-124px) rotate(1turn); } to { opacity: 1; transform: rotate(1turn) translateY(-124px) rotate(0turn); } }

需要特别注意:rotate(0turn)与rotate(1turn)在几何上都是恒等变换(360° 旋转回到原位),所以from和to两个端点的最终视觉效果完全相同,都是translateY(-124px)。但浏览器在插值动画时,会按位置逐个插值三个函数:

  • 第一层rotate(0turn) → rotate(1turn):角度从 0° 连续增长到 360°,驱动元素完成一整圈公转;
  • 中间translateY(-124px):恒定不变,定义公转轨道的半径(124px);
  • 第三层rotate(1turn) → rotate(0turn):角度从 360° 反向减到 0°,恰好抵消第一层旋转带来的"自转",使头像在环绕过程中始终保持竖直朝上,不会跟着公转一起翻转。

写成数学形式,任意时刻 t 的变换矩阵近似为R(360°·t) · T(0, -124px) · R(-360°·t):先沿本地 Y 轴负方向平移 124px,再让整个坐标系绕元素自身的变换原点旋转 θ,最后反向旋转 -θ 抵消朝向。于是元素的中心点以"自身原始位置"为圆心、124px 为半径做圆周运动,而元素的自身朝向始终不变。

这就是为什么原文档强调"transform-origin 只是语法糖"——把"绕远处一点旋转"翻译成"平移 + 旋转 + 反向旋转"的链式写法后,连transform-origin都不需要设置。

圆心与半径如何确定

轨道参数由元素定位和translateY共同决定:

  • .path容器为 300px × 300px 的圆(border-radius: 50%),内部用display: flex+margin: auto让.logo(52px)精确居中,Logo 中心即轨道圆心;
  • .avatar尺寸 50px,绝对定位在top: 124px; left: 124px——容器中心坐标为 (150, 150),头像中心坐标为 (149, 149),两者几乎重合(124 + 25 = 149,与 150 只差 1px,可视为居中);
  • translateY(-124px)使头像沿本地 Y 轴向上平移 124px——这就是公转半径。头像最外缘距圆心约 124 + 25 = 149px,恰好贴合 150px 半径的容器内壁,视觉上正好沿圆形边框内侧环绕。

完整示例:五头像绕 Logo 平滑环绕

把原文档 vuep 演示中的<style>部分完整提取出来,就是这份可直接复制的核心 CSS:

main { width: 100%; height: 529px; display: flex; flex-direction: column; align-items: center; } .path { width: 300px; height: 300px; border-radius: 50%; margin: 100px auto; position: relative; display: flex; border: 1px solid #b4a078; } .logo { width: 52px; margin: auto; background: #FFF; } .avatar { width: 50px; position: absolute; top: 124px; left: 124px; animation: circular-smooth-spin 7.5s infinite linear; animation-play-state: running; } .avatar > span { font-weight: 500; position: absolute; white-space: nowrap; top: -50%; left: 50%; padding: 3px 12px; opacity: 0; transform: scale(0); transition: opacity, transform .8s; transform-origin: 0 bottom; } .avatar > img { width: inherit; border-radius: 50%; overflow: hidden; } .path:hover .avatar { animation-play-state: paused; } .path:hover .avatar > span { opacity: 1; transform: scale(1); transition: opacity, transform .8s; } .avatar:nth-of-type(2) { animation-delay: -1.5s; } .avatar:nth-of-type(3) { animation-delay: -3s; } .avatar:nth-of-type(4) { animation-delay: -4.5s; } .avatar:nth-of-type(5) { animation-delay: -6s; } @keyframes circular-smooth-spin { from { transform: rotate(0turn) translateY(-124px) rotate(1turn); } to { opacity: 1; transform: rotate(1turn) translateY(-124px) rotate(0turn); } }

对应的 HTML 结构(头像图片来自仓库 static 目录下的真实资源,如 static/vue-logo.png、static/Evan.jpeg、static/Dulisz.jpeg、static/defcc.png、static/Yerburgh.png、static/Drasner.jpeg):

<main class="main"> <div class="path"> <img class="logo" src="./static/vue-logo.png" /> <div class="avatar"> <span>Evan You</span> <img src="./static/Evan.jpeg"/> </div> <div class="avatar"> <span>Damian Dulisz</span> <img src="./static/Dulisz.jpeg"/> </div> <div class="avatar"> <span>defcc</span> <img src="./static/defcc.png"/> </div> <div class="avatar"> <span>Edd Yerburgh</span> <img src="./static/Yerburgh.png"/> </div> <div class="avatar"> <span>Sarah Drasner</span> <img src="./static/Drasner.jpeg"/> </div> </div> </main>

几个值得注意的实现细节:

  • .avatar > img { width: inherit; }让头像图片继承父级.avatar的 50px 宽度,配合border-radius: 50%+overflow: hidden切成圆形;
  • 动画简写circular-smooth-spin 7.5s infinite linear的含义依次为:动画名、时长 7.5s、无限循环、匀速linear。linear缓动是后面负值错峰能均匀排布的前提——只有匀速运动,负延迟对应的相位才严格按比例分布;
  • 关键帧to里的opacity: 1并不参与头像自身的透明度动画(头像一直是不透明状态),属于演示中的冗余声明,不影响轨道效果。

在本仓库中,该演示是通过 docsify + vuep 交互式组件呈现的:文档内先写<vuep template="#circular-smooth_tlp"></vuep>,再用<script v-pre type="text/x-template" id="circular-smooth_tlp">包裹 CSS、HTML 模板与(空的)Vue script,index.html 加载 libs/vuep.min.js 与 libs/vue.min.js 后即可实时渲染。仓库的 docsify 配置见 libs/index.js,侧边栏索引见 _sidebar.md(该技巧被收录在 "Transition & Animations" 分组下)。如需本地查看全部演示,可参照 README.md 的 Quick start 用 docsify 启动后打开本地 3000 端口预览。

错峰排布:负值 animation-delay 的妙用

5 个头像想要在圆环上等间距分布,最直接的想法是给每个头像不同的延迟让它们"先后出发"。但如果使用正值延迟,页面加载后的前几秒会先看到头像一个接一个地启动,随后才趋于均匀。原文档采用的是更优雅的负值延迟:

.avatar:nth-of-type(2) { animation-delay: -1.5s; } .avatar:nth-of-type(3) { animation-delay: -3s; } .avatar:nth-of-type(4) { animation-delay: -4.5s; } .avatar:nth-of-type(5) { animation-delay: -6s; }

负值animation-delay的语义是"动画仿佛已经运行了这么长时间":浏览器会让元素直接从时间轴上的对应位置开始播放,而不是真的等待。这带来两个好处:

  1. 页面加载瞬间就呈现完整的均匀排布,无需等待动画"跑起来";
  2. 所有头像从第一帧起就保持固定相位差,视觉上永远是一个稳定的五边形环绕队形。

相位间隔的计算非常简单:总周期 7.5s ÷ 5 个头像 = 1.5s,即每个头像错开 1.5s,正好对应 360° ÷ 5 = 72°。加上未设延迟的第 1 个头像(0°),5 个头像的相位依次为 0°、72°、144°、216°、288°,均匀铺满整圈。若你的场景有 n 个元素,周期 T,则延迟步长取T / n即可;若希望元素沿轨道反向排布或改变起始角度,可以改用负的translateY方向或调整第一层rotate的初值。

交互增强:悬停暂停与姓名气泡

环形动画通常需要交互配合,原文档用两条规则实现了"悬停定格 + 气泡提示":

.path:hover .avatar { animation-play-state: paused; } .path:hover .avatar > span { opacity: 1; transform: scale(1); transition: opacity, transform .8s; }

暂停轨道:animation-play-state: paused让悬停瞬间冻结所有头像的当前相位。注意选择器是.path:hover .avatar——只要鼠标进入圆形区域,整组头像同时定格,而不是只暂停被悬停的那一个,这样所有气泡都能稳定展示。.avatar上显式声明的animation-play-state: running与默认值一致,属于可省略的保险写法。

气泡提示:每个.avatar > span默认opacity: 0; transform: scale(0),悬停时变为opacity: 1; transform: scale(1),配合transition: opacity, transform .8s在 0.8s 内完成淡入与放大。气泡的transform-origin: 0 bottom表示缩放的原点位于气泡自身的左下角,因此它从"贴在头像上方"的位置向上展开,视觉更自然。

这里有一个值得注意的简写细节:transition: opacity, transform .8s;按 CSStransition简写的解析规则,会被拆成两条过渡——opacity(未指定时长,默认 0s)与transform .8s。也就是说这种写法下透明度是瞬时切换、只有缩放有 0.8s 渐变。如果希望淡入和放大同步进行,应写成transition: opacity .8s, transform .8s;。原文档示例保留了前者,实际使用时可以根据预期效果选择。

关键参数速查表

参数 / 属性取值作用与调优建议
animationcircular-smooth-spin 7.5s infinite linear动画名 + 时长 + 无限循环 + 匀速。时长决定环绕快慢,周期越长越从容;务必保持linear,否则负值延迟的相位不再等距
animation-delay-1.5s / -3s / -4.5s / -6s负值错峰,步长 = 周期 ÷ 元素数(本例 7.5 ÷ 5 = 1.5s),对应 72° 相位差
animation-play-staterunning / paused悬停时暂停轨道动画,让气泡可读
transformrotate(0/1turn) translateY(-124px) rotate(1/0turn)三层链式:公转角度 + 轨道半径 + 自转抵消;translateY的 124px 即轨道半径
.avatar定位top: 124px; left: 124px让头像中心与容器圆心重合,是"圆心位置"的来源
气泡transitionopacity, transform .8s注意简写语法:opacity 默认 0s、transform 0.8s;需要同步渐变时写成opacity .8s, transform .8s
气泡transform-origin0 bottom气泡从自身左下角为原点展开,贴合"从头像上方弹出"的语义

常见调优方向:想扩大/缩小轨道,改translateY数值(并同步调整头像初始定位使圆心重合);想改变环绕速度,改animation时长;想增减元素数量,按周期 ÷ 数量重算延迟步长;想实现"公转 + 自转"并存(如行星自转效果),只需去掉第三层反向rotate或将其改为非全额角度。

浏览器支持

该技巧依赖三项能力:CSS Animations(animation与@keyframes)、2D Transforms(rotate/translate)、CSS Transitions(气泡过渡)。原文档在"Browser Support"小节通过三个 caniuse 内嵌面板(分别对应css-animation、transforms2d、css-transitions)给出了参考:这三者属于现代浏览器的长期稳定特性,覆盖 Chrome、Firefox、Safari、Edge 的近期版本。由于本文示例还用到了nth-of-type、flex等布局选择器与属性,落地到具体项目时,建议结合目标用户群体的浏览器版本范围做一次 caniuse 核查,并视需要为老版本浏览器补充前缀或降级方案(例如动画暂停可退化为"隐藏气泡")。

小结

沿圆形轨道平滑环绕动画的核心可以浓缩为三点:

  1. 轨道即变换链:rotate(θ) translateY(r) rotate(-θ)三层组合,用纯transform实现"绕远处圆心公转 + 自身朝向不变",完全绕开transform-origin——这正是 James Anderson 所说"transform-origin 只是语法糖"的实践注脚;
  2. 负值延迟实现错峰:animation-delay: -T/n让多个元素在页面加载瞬间即呈现等间距分布,配合linear缓动保证相位严格均匀;
  3. hover 暂停 + 气泡:animation-play-state: paused定格轨道,transition+transform-origin: 0 bottom让气泡从头像上方优雅展开。

该技巧与仓库中同属 "Transition & Animations" 分组的其他动效互补使用效果更佳:无缝滚动背景见 smooth.md,弹性过渡见 elastic.md,自由落体弹跳见 bounce.md。掌握"变换链 + 负延迟"这一组合拳后,你可以自由扩展出卫星环绕、多轨行星系统、环形菜单等更丰富的 CSS 动效。

  • 前端
  • 文档
  • 教程

【免费下载链接】You-need-to-know-css

💄CSS tricks for web developers~

项目地址:https://gitcode.com/gh_mirrors/yo/You-need-to-know-css
点击查看免费下载
上一篇:如何快速下载B站高清视频:BilibiliDown跨平台下载器完全指南
下一篇:OPC UA客户端终极指南:5分钟实现工业自动化数据可视化

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询