ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

纯CSS3齿轮咬合滚动动画:scroll()时间轴与齿形绘制实战

纯CSS3齿轮咬合滚动动画:scroll()时间轴与齿形绘制实战 简介纯CSS3实现的齿轮咬合滚动动画源码包是一份轻量级前端学习示例主要面向对CSS3动画感兴趣的开发者尤其适合想尝试摆脱JavaScript、仅用样式表完成复杂交互的初学者。压缩包内仅含1个html文件整体大小约1KB没有图片和多余依赖下载后可直接在浏览器中运行也能直接打开源码分析实现过程。动画核心围绕keyframes、transform:rotate与transition展开并结合CSS3选择器、相对单位、定位、3D变换、伪元素以及媒体查询等特性展示了齿轮精准咬合与连续滚动的完整思路。通过查看源码可以梳理齿轮咬合的位置计算与旋转同步逻辑理解如何用纯CSS模拟机械传动效果对提升前端动效设计能力有直接帮助。目前已有79人学习下载对于想要快速上手CSS3动画的开发者来说是一个短小精悍的参考案例。1. 纯 CSS3 实现的齿轮咬合滚动动画效果先盯住咬合再谈旋转纯 CSS3 实现的齿轮咬合滚动动画效果经常出现在机械风官网、后台大屏和产品演示页里页面往下滚一组齿轮跟着转大轮慢、小轮快方向相反。看起来像 JavaScript 驱动实际只需要给每个齿轮配一条 scroll 时间轴就能把滚动位置直接映射成旋转角度。比较新的实现是 CSS Scroll-Driven Animations严格说不算 CSS3 本身而是 CSS Animations Level 2 里扩展出来的能力但日常沟通里大家还是会用“纯 CSS3”这个叫法。这类动画的难点不在“让齿轮转起来”而在“让齿轮看着像咬合”。两个齿数不同的齿轮转速比必须符合齿数反比方向必须相反初始角度还得错开半个齿距否则滚动起来就是两个圆在各自空转。你从标题里拿到的 zip 包本质就是由 HTML、CSS、SVG 组成的最小工程不需要框架也不需要滚动事件的监听。这篇文章围绕齿轮咬合理念展开先讲animation-timeline: scroll()和转速比怎么配合再给 SVG 与 CSS mask 两种齿形画法最后把animation-range、降级和 zip 源码包整理方式串起来。新手可以照着目录搭一个能跑的 demo老手也能看到相位、齿数配比和兼容性上的边界条件。2. scroll() 时间轴、齿数配比与相位齿轮咬合的先决条件2.1 scroll() 把滚动位置变成动画播放位置最小可运行代码CSS 里新建一条滚动时间轴的写法很简单在普通keyframes后面加animation-timeline: scroll()。下面是最小可运行代码keyframes gear-spin { to { transform: rotate(360deg); } } .gear { width: 8rem; aspect-ratio: 1; border-radius: 50%; background: #3f5b72; animation: gear-spin 1s linear; animation-timeline: scroll(block root); }这段代码的含义是页面从顶部滚到底部.gear恰好转一圈。scroll(block root)里第一个参数block表示取块级滚动方向也就是纵向滚动第二个参数root表示用根滚动容器。如果你的齿轮在一个overflow: auto的容器里应改成scroll(block nearest)或者干脆给容器设置scroll-timeline后通过animation-timeline: --timeline-name引用。这里的animation-duration: 1s只是兜底时长。浏览器支持scroll()时滚动进度担任动画进度1s不参与计算不支持时它退化成普通时间动画。理解这一点后你会明白为什么 scroll-driven animations 比 JS 监听滚动更容易保持帧率旋转只发生在transform上不触发布局和绘制浏览器可以放进合成器线程处理。2.2 齿数与转速比用 --ratio 统一不同尺寸齿轮齿轮咬合时两个齿轮在接触点的线速度必须相等否则就是打滑。线速度等于角速度乘半径而齿轮半径与齿数成正比于是得到关系从动轮角速度 / 驱动轮角速度 驱动轮齿数 / 从动轮齿数。驱动轮齿数 Z1从动轮齿数 Z2从动轮相对驱动轮的圈数CSS 建议值12121--ratio: 124122--ratio: 212240.5--ratio: 0.515101.5--ratio: 1.5写成 CSS 变量后一组齿轮可以共用一个keyframes.gear { --ratio: 1; /* 从动轮圈数 / 驱动轮圈数 */ --direction: 1; /* 1 同向-1 反向 */ --phase: 0deg; /* 滚动起点时的齿相位 */ animation: mesh-gear linear; animation-timeline: scroll(block root); } .gear--main { --ratio: 1; --direction: 1; --phase: 0deg; } .gear--driven24 { --ratio: 2; --direction: -1; --phase: 15deg; } keyframes mesh-gear { from { transform: rotate(var(--phase)); } to { transform: rotate(calc(var(--phase) 360deg * var(--ratio) * var(--direction))); } }主动轮从0deg转到360deg从动轮按照--ratio: 2和--direction: -1转到-720deg。所有齿轮共用同一条scroll()时间轴所以无论尺寸差别多大它们在任何滚动位置的相对转角都是一致的。需要留意的是--ratio必须用预先算好的数字CSS 里直接写calc(Z1 / Z2)依赖较新的算术能力兼容性不如写死数字稳。2.3 相位不是动画延迟把 --phase 写进 keyframes有人会试图用animation-delay给不同齿轮错开角度这在 scroll-driven animations 里不是好做法。animation-delay影响的是时间轴上动画何时开始而滚动时间轴本就和时间没有强绑定设完 delay 后滚动位置和角度的对应关系会变得很难调。正确的做法是把“初始咬合相位”写进 keyframes 的from和to里。上面的--phase就是干这个的滚动到顶部时齿轮停在rotate(var(--phase))滚动到底部时停在rotate(var(--phase) target)。这样相位是一个恒定偏置不会随滚动进度变化两个齿轮的相对齿位始终不变。还有一个常见误区是给每个齿轮单独写一套spin-1x、spin-2xkeyframes。这样做齿轮一多样式表会迅速膨胀。把变化量收敛到--ratio和--direction两个 CSS 变量里后面加第三第四个齿轮时只需要覆盖变量不需要碰 keyframes。3. 齿形绘制与齿轮布局SVG 内联和 CSS mask 两种方案3.1 内联 SVG 齿轮齿数改起来最快的方案齿轮最好别用 png 或 jpg 做图因为在不同 DPI 屏幕上会发虚缩放时锯齿明显。常见做法是把齿轮画成内联 SVG动画只旋转 SVG 本身。一个 8 齿的齿轮骨架可以写成这样svg classgear-svg viewBox-100 -100 200 200 aria-hiddentrue defs g idtooth rect x-6 y-102 width12 height26 rx3 fillcurrentColor / /g /defs g classgear-svg__teeth use href#tooth transformrotate(0) / use href#tooth transformrotate(45) / use href#tooth transformrotate(90) / use href#tooth transformrotate(135) / use href#tooth transformrotate(180) / use href#tooth transformrotate(225) / use href#tooth transformrotate(270) / use href#tooth transformrotate(315) / /g circle r78 fillcurrentColor / circle r16 fill#fff / /svg对应的 CSS.gear-svg { width: 160px; height: 160px; overflow: visible; color: #3f5b72; transform-box: fill-box; transform-origin: center; animation: mesh-gear linear; animation-timeline: scroll(block root); }transform-box: fill-box是关键。SVG 元素默认的 transform-origin 参照的是 viewBox 坐标系不设置的话齿轮很容易绕着一个奇怪的顶点旋转。设置fill-box后transform-origin: center才真正落到图形中心。use href#tooth里的href指向同文档中的g idtooth所以这个 SVG 必须内联进 HTML不能直接改成img srcgear.svg否则浏览器无法跨文件引用内部 id。3.2 CSS mask 齿轮一个圆盘换出十二个齿不想写一大段 SVG 时可以用repeating-conic-gradient配合mask直接在 div 上切出齿形。下面的 CSS 把一个圆形 div 变成 12 齿齿轮.gear--mask { --teeth: 12; --half: calc(360deg / var(--teeth) / 2); width: 14rem; aspect-ratio: 1; border-radius: 50%; background: #3f5b72; -webkit-mask: radial-gradient(circle, #000 0 30%, transparent 32% 40%, #000 42% 100%), repeating-conic-gradient(#000 0 var(--half), transparent var(--half) calc(var(--half) * 2)); mask: radial-gradient(circle, #000 0 30%, transparent 32% 40%, #000 42% 100%), repeating-conic-gradient(#000 0 var(--half), transparent var(--half) calc(var(--half) * 2)); -webkit-mask-composite: source-in; mask-composite: intersect; animation: mesh-gear linear; animation-timeline: scroll(block root); }--half是半个齿距12 齿时是 15deg。repeating-conic-gradient的黑色扇区是齿透明扇区是齿槽radial-gradient负责保留中心轮盘和边缘轮缘。两个 mask 层用source-in或intersect取交集最后得到“中心盘 外圈齿”的形状。这个方案的优点是代码短改--teeth就能换齿数缺点是齿形是直扇形不是机械渐开线近距离看会偏扁平风。如果你要的是工业机械感优先用 SVG。两者对比可以简单记成SVG 更适合精确啮合mask 更适合轻量装饰。实际项目中我一般把 SVG 当作“正式方案”mask 用在那些只有 40 像素大小、用户不会凑近看的装饰齿轮上。3.3 中心距用布局算不靠眼睛调flex 双齿轮示例齿轮咬合的前提是中心距等于两轮半径之和。用 flex 布局可以让这个条件自动成立不需要手动调 margin.gearbox { --r1: 80px; --r2: 56px; display: flex; align-items: center; } .gear-box--a { width: calc(2 * var(--r1)); height: calc(2 * var(--r1)); } .gear-box--b { width: calc(2 * var(--r2)); height: calc(2 * var(--r2)); }在 flex 容器里第一个元素左侧从 0 开始第二个元素左侧正好在第一个元素宽度处。第一个元素中心在r1第二个元素中心在2r1 r2两者距离是r1 r2和半径之和对上了。这里不要给.gearbox设置gap任何额外间距都会让齿分离。旋转后如果看着齿间有重叠优先检查--phase而不是去挪 margin。4. 调参顺序、滚动区间与 zip 源码包整理4.1 三个必调参数--ratio、--direction、--phase参数推荐值作用--ratioZ1 / Z2写成小数决定从动轮相对驱动轮转多快--direction外啮合-1内啮合1决定旋转方向--phase0 ~ 360deg决定滚动起点时的齿位偏置调参顺序我一般固定为先定--ratio再定--direction最后才调--phase。前两个不对第三个怎么调都是白费。--phase的常见取值是半个齿距也就是360deg / (齿数 * 2)。两个相同齿数的齿轮需要错开半个齿距这样齿尖才不会顶在一起“错开”就是驱动轮--phase: 0deg从动轮--phase: 15deg。4.2 animation-range让不同齿轮在各自段落开始/停止默认scroll(root)会让动画贴合整页滚动进度。如果页面里有两个齿轮分别位于首屏和页脚直接共用一条 timeline 会导致页脚齿轮在首屏就已经转完一圈。用animation-range可以把每只齿轮的活动区间截出来.gear--header { animation: mesh-gear linear; animation-timeline: scroll(block root); animation-range: 0 40vh; } .gear--footer { animation: mesh-gear linear; animation-timeline: scroll(block root); animation-range: 60vh 90vh; animation-fill-mode: both; }animation-range的取值可以是长度、百分比或entry、exit、cover这类命名区间。滚动回到区间之前动画不开始越过区间终点后动画停在100%状态。animation-fill-mode: both保证 40vh 之前保持起点状态、90vh 之后保持终点状态。这里有个容易踩的坑如果你希望用户拉回顶部时齿轮回到初始角度就别给后面那段加forwards否则它会一直停在滚动结束时的位置视觉上像“卡死”。由于scroll()时间轴是双向可逆的用户向上滚动时动画会自然逆向播放不需要再写animation-direction: reverse。滚动到哪一帧动画就停在哪一帧这种体验是 JS 监听 scroll 时要额外做映射才能达到的。4.3 supports 降级老浏览器也有慢速旋转兜底scroll()的兼容性目前不统一不能把滚动驱动动画当成唯一实现。推荐的写法是先写普通时间动画再用supports覆盖.gear { animation: gear-spin-slow 4s linear infinite; } supports (animation-timeline: scroll()) { .gear { animation: mesh-gear linear; animation-duration: 1s; animation-iteration-count: 1; animation-timeline: scroll(block root); } }不支持scroll()的浏览器走第一段齿轮每 4 秒转一圈支持的浏览器把animation-timeline切到滚动时间轴mesh-gear会根据--ratio、--direction和--phase去跑。这样即使老浏览器没有滚动动效页面也不会是一堆静止的圆盘。4.4 最小 demo 目录与 zip 打包这类标题里带“源码.zip”的 demo通常不需要构建步骤。解压后直接双击index.html就能看效果目录结构可以是gear-scroll-demo/ ├── index.html ├── css/ │ └── style.css └── assets/ ├── gear-a.svg └── gear-b.svgSVG 内联进 HTML 后assets 目录里的源文件仅作二次修改用。打包命令可以写zip -r gear-scroll-demo.zip gear-scroll-demo -x *.DS_Store.DS_Store是 macOS 下自动生成的索引文件放进 zip 只会增加噪音。如果你不用命令行直接右键文件夹压缩也没问题。注意路径全部使用相对路径这样压缩包传到别的机器上预览时不会出现图片丢失。5. 进阶用 animation-range 分段驱动齿轮并在 DevTools 里验证咬合当页面很长时我建议按视觉段拆分齿轮组而不是让所有齿轮共用一个全局滚动区间。比如首屏的齿轮组在第一屏内转完中间内容区的齿轮组在 40vh 到 80vh 之间转页脚的齿轮组只在最后 10vh 里转动。这样做的好处是滚动节奏更明确用户能感觉到“这一段内容对应这一组动效”。验证咬合是否正确的核心方法是检查相对转角。Chrome DevTools 的 Elements 面板里选择齿轮元素Styles 面板会显示animation-timeline和animation-range的实际值。点击animation旁边的曲线图标可以查看当前滚动位置对应的动画进度。不过这个视图更适合查单只齿轮查多齿轮咬合还是要靠肉眼对齿尖。我通常会做一张检查清单中心距是否等于两轮半径之和用 flex 自动排布就不会错。--ratio是否等于Z1 / Z2不相等的转速比会造成齿尖互相切入。外啮合--direction是否一个正一个负。--phase是否让齿槽对准齿尖。animation-range是否让每只齿轮只在预期段落内活动。最后一招是临时把--phase改成不同值逐档调试。从0deg开始每次加2deg观察咬合处是否从顶齿变成贴合。如果两个齿尖始终穿模先回头检查--ratio和中心距不要在--phase上硬凑。本文还有配套的精品资源点击获取
返回列表