细腻手绘齿轮与机械时钟摆动:SVG 矢量齿形数学建模与 CSS 连续啮合旋转
在复古手账与蒸汽朋克风格的插画设计中机械齿轮与单摆时钟一直是最具象征意义的美学符号。发条拧紧时的金属微颤、大小齿轮齿面之间严丝合缝的连续啮合咬合、老式钟摆在两极点之间迟疑而笃定的回摆……这些机械运动所传达的不仅是纯粹的工科几何理性更是一种对“时间流转、岁月刻度”的温情敬畏。很多前端开发者在尝试用 SVG 绘制齿轮动画时常常随手在圆形外侧贴上一圈均匀分布的矩形或梯形。这种粗糙的做法在静态展示时或许还能蒙混过关但一旦让两个不同尺寸的齿轮相对旋转就会立刻暴露出齿顶打架、穿模干涉或者齿隙悬空脱节的严重破绽毫无机械运动的咬合真实感。在本文中我们将深入机械工程学中的渐开线齿轮Involute Gear几何原理利用三角函数在纯前端动态推导生成严谨精确的 SVG 闭合齿廓矢量路径并配合现代 CSS 硬件加速与角速度反比传动比计算打造一套丝滑运转、绝不穿模的复古时钟摆动动画。机械原理两个齿轮为什么能完美啮合要让两个齿轮在相对旋转时既不互相撞击、又不相互脱离必须满足经典机械传动准则模数相同原则Equal Module模数 $m$ 是决定单个轮齿高矮胖瘦的基础参数。两个能够互相啮合的齿轮其模数必须完全相等分度圆与中心距设大齿轮齿数为 $z_1$小齿轮齿数为 $z_2$。分度圆直径 $d_1 m \cdot z_1$$d_2 m \cdot z_2$两齿轮的安装中心距必须严格等于两分度圆半径之和$L \frac{d_1 d_2}{2} \frac{m(z_1 z_2)}{2}$角速度反比与反向旋转主动轮与从动轮旋转方向相反。若大轮齿数为 18 齿、小轮齿数为 12 齿则传动比为 $18 / 12 1.5$。大齿轮顺时针旋转一周需要 6 秒小齿轮必须严格在 4 秒内逆时针旋转整整一周初始相位偏置Phase Offset小齿轮在起始时刻必须绕自身轴心预先偏转半个齿距角$\Delta \theta \frac{360^\circ}{2 \cdot z_2}$确保大轮的齿凸恰好嵌入小轮的齿凹之中。[主动大齿轮 z118] [从动小齿轮 z212] 分度圆 R1 分度圆 R2 ( O1 ) --- 中心距 L --- ( O2 ) | | 顺时针旋转周期 T1 6s 逆时针旋转周期 T2 4s (严格反向相切啮合无缝咬合绝无穿模)核心实现渐开线齿廓 SVG 路径生成器在 Web 端我们无需引入重量级的 CAD 库只需一段精巧的 TypeScript 数学算法就能通过正弦余弦极坐标变换在毫秒级内输出标准的 SVGpath d...字符串// utils/svg-gear-generator.ts export interface GearConfig { teeth: number; // 齿数 z module: number; // 模数 m pressureAngleDeg?: number; // 压力角 (标准通常为 20 度) holeRadius?: number; // 轴心内孔半径 } export class SvgGearGenerator { /** * 生成标准机械齿轮的闭合 SVG 路径数据 */ public static generateGearPath(config: GearConfig): string { const { teeth: z, module: m, pressureAngleDeg 20, holeRadius 14 } config; // 机械关键圆半径计算 const pitchRadius (m * z) / 2; // 分度圆半径 const addendum m * 1.0; // 齿顶高 const dedendum m * 1.25; // 齿根高 const outerRadius pitchRadius addendum; // 齿顶圆半径 const rootRadius Math.max(holeRadius 2, pitchRadius - dedendum); // 齿根圆半径 const anglePerTooth (2 * Math.PI) / z; const points: [number, number][] []; for (let i 0; i z; i) { const baseAngle i * anglePerTooth; // 单个齿形分为 4 个关键角度区段齿根左 - 齿顶左 - 齿顶右 - 齿根右 const a0 baseAngle; const a1 baseAngle anglePerTooth * 0.25; const a2 baseAngle anglePerTooth * 0.55; const a3 baseAngle anglePerTooth * 0.80; // 1. 齿根谷底左端 points.push([rootRadius * Math.cos(a0), rootRadius * Math.sin(a0)]); // 2. 渐开线上升段至齿顶 points.push([outerRadius * Math.cos(a1), outerRadius * Math.sin(a1)]); // 3. 齿顶平坦圆弧顶 points.push([outerRadius * Math.cos(a2), outerRadius * Math.sin(a2)]); // 4. 渐开线下降段至齿根 points.push([rootRadius * Math.cos(a3), rootRadius * Math.sin(a3)]); } // 组装外齿多边形 let d M ${points[0][0].toFixed(2)} ${points[0][1].toFixed(2)} ; for (let i 1; i points.length; i) { d L ${points[i][0].toFixed(2)} ${points[i][1].toFixed(2)} ; } d Z ; // 轴心内孔挖空使用偶数奇数填充规则镂空轴心 d M ${holeRadius} 0 A ${holeRadius} ${holeRadius} 0 1 0 ${-holeRadius} 0 A ${holeRadius} ${holeRadius} 0 1 0 ${holeRadius} 0 Z; return d; } }机械钟表与单摆联动组件实现在 Vue 3 组件中我们放置两个齿数比为 18:12 的齿轮计算其分度圆中心距进行精确定位并在底部悬挂一个优雅摆动的单摆!-- components/MechanicalClockwork.vue -- template div classclockwork-container svg classclockwork-svg viewBox0 0 500 400 xmlnshttp://www.w3.org/2000/svg defs !-- 复古做旧黄铜金属渐变 -- linearGradient idbrassGrad x10% y10% x2100% y2100% stop offset0% stop-color#e8c07d / stop offset40% stop-color#b8863b / stop offset70% stop-color#8a5c22 / stop offset100% stop-color#d4a359 / /linearGradient filter idgearShadow x-15% y-15% width130% height130% feDropShadow dx1 dy3 stdDeviation3 flood-color#2a1805 flood-opacity0.35 / /filter /defs !-- 机械摆锤挂载点 -- g classpendulum-group transformtranslate(250, 120) !-- 摆杆 -- line x10 y10 x20 y2190 stroke#7a4f1a stroke-width4 stroke-linecapround / !-- 摆锤重块 -- circle cx0 cy190 r24 fillurl(#brassGrad) filterurl(#gearShadow) / circle cx0 cy190 r14 fill#54330d / /g !-- 主动大齿轮 (18 齿, 坐标 200, 120) -- g classgear gear-driver transformtranslate(200, 120) path :ddriverGearPath fillurl(#brassGrad) filterurl(#gearShadow) fill-ruleevenodd / !-- 辐条装饰 -- circle cx0 cy0 r28 fillnone stroke#54330d stroke-width3 stroke-dasharray8 6 / /g !-- 从动小齿轮 (12 齿, 啮合中心距精确计算) -- g classgear gear-driven :styledrivenGearStyle path :ddrivenGearPath fillurl(#brassGrad) filterurl(#gearShadow) fill-ruleevenodd / circle cx0 cy0 r18 fillnone stroke#54330d stroke-width2.5 / /g /svg /div /template script setup langts import { computed } from vue; import { SvgGearGenerator } from ../utils/svg-gear-generator; const m 6; // 模数 const z1 18; // 大齿轮齿数 const z2 12; // 小齿轮齿数 const driverGearPath SvgGearGenerator.generateGearPath({ teeth: z1, module: m, holeRadius: 18 }); const drivenGearPath SvgGearGenerator.generateGearPath({ teeth: z2, module: m, holeRadius: 12 }); // 计算两圆心距离 L m * (z1 z2) / 2 6 * (18 12) / 2 90px const centerDist (m * (z1 z2)) / 2; const driverX 200; const driverY 120; const drivenX driverX centerDist; // 290 const drivenY driverY; // 初始相位角对齐微调 15 度齿顶嵌入 const drivenGearStyle computed(() ({ transform: translate(${drivenX}px, ${drivenY}px) rotate(15deg), transformOrigin: 0 0 })); /script style scoped .clockwork-container { display: flex; justify-content: center; align-items: center; padding: 24px; } .clockwork-svg { width: 500px; height: 400px; overflow: visible; } /* 主动大齿轮顺时针 6 秒/周 */ .gear-driver { animation: rotateClockwise 6s linear infinite; transform-origin: 200px 120px; will-change: transform; } /* 从动小齿轮逆时针 4 秒/周 (严格保持 1.5 倍速反向) */ .gear-driven { animation: rotateCounterClockwise 4s linear infinite; transform-origin: 290px 120px; will-change: transform; } /* 钟摆单摆振动周期 2 秒平滑加减速简谐运动 */ .pendulum-group { animation: pendulumSwing 2s cubic-bezier(0.4, 0, 0.6, 1) infinite alternate; transform-origin: 250px 120px; will-change: transform; } keyframes rotateClockwise { from { transform: translate(200px, 120px) rotate(0deg); } to { transform: translate(200px, 120px) rotate(360deg); } } keyframes rotateCounterClockwise { from { transform: translate(290px, 120px) rotate(15deg); } to { transform: translate(290px, 120px) rotate(-345deg); } } keyframes pendulumSwing { 0% { transform: translate(250px, 120px) rotate(-16deg); } 100% { transform: translate(250px, 120px) rotate(16deg); } } /style避坑指南机械啮合动画的核心细节动画时间函数的严格线性linear两个连续咬合的齿轮必须绝对使用linear匀速转动。如果误用了ease或ease-in-out两轮在不同周期阶段的瞬时角速度会发生错位导致肉眼可见的相对打滑与撞击中心距与 SVG 视口单位严格匹配在设置 CSS 旋转中心transform-origin时必须精准对齐分度圆几何圆心坐标哪怕偏离 1px旋转时都会产生明显的偏心摆动偏磨感摆锤周期与齿轮转速的声画共振单摆的来回往复周期2秒设计为大齿轮转动周期6秒的整倍数。这种数学上的共振节奏会让用户在凝视手账画面时感受到一种深邃悠长的秩序感与平静感。听汐的机械手记齿轮缝隙里的时间沉思在所有的人类发明中机械时钟大概是最浪漫的一种装置。它不需要任何网络连接不需要电池仅仅依靠几枚相互咬合的金属小齿轮与一根左右摆动的游丝发条就能日夜不息地把浩瀚无垠的宇宙时间切分成一声声清脆温润的“滴答”。在手账页面的角落里放上这样一对咬合无声的小齿轮看着它们不紧不慢地旋转心底的躁动总会悄悄平复。生活也是这样不需要急功近利地狂奔只要选对方向让每一分、每一秒都在平稳的齿轨上笃定运转时间自会为你结出最甘甜的秋实。

相关新闻

软考 系统架构设计师历年真题集萃(56)

软考 系统架构设计师历年真题集萃(56)

接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(55) 第91题 商业智能关注如何从业务数据中提取有用的信息,然后采用这些信息指导企业的业务开展。商业智能系统主要包括数据预处理、建立()、数据分析和数据展现4个主要阶段。其中数据预处理是整合企业原始数据的第…

2026/10/15 1:59:12 阅读更多 →
【Unity开发】StreamingAssets、Assets Bundle、Resources区别对比

【Unity开发】StreamingAssets、Assets Bundle、Resources区别对比

🐾 个人主页 🐾 阿松爱睡觉,横竖醒不来🏅你可以不屠龙,但不能不磨剑🗡文章一、前言二、StreamingAssets文件夹1. 用途2. 访问方式3. 优点4. 缺点三、AssetBundle1. 用途2. 访问方式3. 优点4. 缺点四、Resou…

2026/10/15 1:59:12 阅读更多 →
ECharts地图JS选型与下钻渲染避坑全指南

ECharts地图JS选型与下钻渲染避坑全指南

简介:一套覆盖全国、地市、区县三级地图的js资源共343个文件,专为ECharts使用设计,压缩包约25.79MB。所有文件按“行政区划代码_省份_城市”命名,涵盖黑龙江、吉林、新疆等多省市,便于按区域定位并引入对应地图。开发者…

2026/10/15 1:59:12 阅读更多 →

最新新闻

Rust 泛型关联类型(GAT)深度解析:从 RFC 1598 看关联类型构造器如何开启高阶多态

Rust 泛型关联类型(GAT)深度解析:从 RFC 1598 看关联类型构造器如何开启高阶多态

文档 【免费下载链接】rfcs RFCs for changes to Rust 项目地址: https://gitcode.com/gh_mirrors/rf/rfcs 点击查看 免费下载 泛型关联类型(Generic Associated Types,简称 GAT)是 Rust 类型系统中一项承前启后的核心特性&#…

2026/10/15 2:35:23 阅读更多 →
BilibiliSponsorBlock 发版全流程实战:从版本号 bump、GitHub Release 到 Chrome/Firefox/Edge 商店提审

BilibiliSponsorBlock 发版全流程实战:从版本号 bump、GitHub Release 到 Chrome/Firefox/Edge 商店提审

前端 【免费下载链接】BilibiliSponsorBlock 一款跳过小电视视频中恰饭片段的浏览器插件,移植自 SponsorBlock。A browser extension to skip sponsored segments in videos, ported from the SponsorBlock 项目地址: https://gitcode.com/gh_mirrors/bi…

2026/10/15 2:35:23 阅读更多 →
N_m3u8DL-RE 下载 m3u8 实战:四条命令,从链接到成品 mp4

N_m3u8DL-RE 下载 m3u8 实战:四条命令,从链接到成品 mp4

N_m3u8DL-RE 下载 m3u8 实战:四条命令,从链接到成品 mp4 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/nm3/N…

2026/10/15 2:35:23 阅读更多 →
JASONETTE-iOS 集成指南:SDWebImage 3.8 远程图片异步加载与缓存实战

JASONETTE-iOS 集成指南:SDWebImage 3.8 远程图片异步加载与缓存实战

移动开发前端 【免费下载链接】JASONETTE-iOS 📡 Native App over HTTP, on iOS 项目地址: https://gitcode.com/gh_mirrors/ja/JASONETTE-iOS 点击查看 免费下载 导读 本文以 JASONETTE-iOS 仓库中随包引入的 SDWebImage(版本 3.8.1&#…

2026/10/15 2:35:23 阅读更多 →
ai-agent-book学习记录t1

ai-agent-book学习记录t1

Chapter 2 学习笔记:上下文工程与 KV Cache本章核心是"上下文决定 Agent 能力上限"。我通过 kv-cache 离线对比实验,直观看到上下文管理模式对性能的巨大影响。实验对比了六种模式。correct(稳定系统提示、固定工具顺序&#xff09…

2026/10/15 2:35:23 阅读更多 →
Amazon Connect 实例的 CDK 自动化部署:Python 基础设施即代码实战指南

Amazon Connect 实例的 CDK 自动化部署:Python 基础设施即代码实战指南

【免费下载链接】aws-cdk-examples Example projects using the AWS CDK 项目地址: https://gitcode.com/gh_mirrors/aw/aws-cdk-examples 点击查看 免费下载 导读 本文基于 aws-cdk-examples 仓库中的 python/amazon-connect 示例,系统讲解如何使用 A…

2026/10/15 2:34:23 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →