ARTICLE DETAIL

资讯详情

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

morphicons核心算法揭秘:2D Procrustes闭式解让图标旋转角度自动涌现,告别手动声明旋转组

morphicons核心算法揭秘:2D Procrustes闭式解让图标旋转角度自动涌现,告别手动声明旋转组 morphicons核心算法揭秘2D Procrustes闭式解让图标旋转角度自动涌现告别手动声明旋转组【免费下载链接】morphiconsAny icon morphs into any other — universal morphing for stroke-based icons with spring physics. Zero dependencies, ~7 KB gzip.项目地址: https://gitcode.com/gh_mirrors/mo/morphiconsmorphicons是一个图标变形icon morphing开源库任意一个描边图标都能平滑地变形为另一个图标带弹簧物理质感零运行时依赖核心仅 ~7 KBgzip。它的最大亮点是——旋转角度不需要你手动声明。箭头从右指变成下指库会自动算出这是一次 90° 旋转然后让它优雅地转过去而不是中间塌缩、剪剪切地蠕动过去。这篇文章带你走一遍 src/core/ 里的核心算法看看数学是怎么涌现出动画的。痛点为什么旋转组声明让人头疼 做图标变形的常见做法是直接对坐标插值。但两个形状如果只是旋转关系逐点直线插值会让形状在飞行途中缩小、剪切——一个 90° 旋转的箭头中间帧会变成一条被压扁的斜杠。老派方案的补丁是给每对图标手工声明旋转组告诉引擎A 的哪部分绕哪个中心转多少度。问题来了每个图标对都要单独配置图标一多就是维护噩梦配置错了动画就不对劲但很难一眼看出来morphicons 的思路反过来不做配置做计算。它把两个形状之间的最优相似变换当成一个有闭式解的数学问题来解。全景一条纯函数流水线在动手解数学之前morphicons 先把图标驯化成统一的数字表示流程定义在 README.md 的 Architecture 一节icon A ─┐ ├→ normalize → resample → match → align → PLAN → interpolate(t) → d icon B ─┘ ↑ 可缓存、可序列化两个关键前置步骤1. 归一化src/core/normalize.ts所有 SVG 图元直线、圆弧、圆、rect、polyline…统一降级为三次贝塞尔段。2. 弧长重采样src/core/resample.ts每条子路径按弧长均匀取 N 64 个点。这一步直接消灭了两条曲线阶数不同、点怎么对应的结构性难题——对应关系就是按下标对。更妙的是拐角锚定切线突变超过 22.5° 的点被固定为精确采样点所以静止时形状是 100% 精确的而飞行中源形状的尖角会自然变圆、目标形状的尖角会逐渐变锐观感非常自然。主角登场2D Procrustes 闭式解到了align阶段对每一对对应的点云morphicons 要回答找到一个旋转 θ、缩放 σ、平移的组合使形状 A 尽量贴合形状 B。这正是 2D Procrustes 问题。一般教科书会动用 SVD但 2D 情形下它有闭式解——只需要一个atan2实现在 src/core/plan.ts 的procrustes函数里S_xx Σ aₓbₓ S_xy Σ aₓbᵧ S_yx Σ aᵧbₓ S_yy Σ aᵧbᵧ 两云均以各自质心为中心 θ* atan2(S_xy − S_yx, S_xx S_yy) σ* [cosθ*·(S_xxS_yy) sinθ*·(S_xy−S_yx)] / Σ‖aᵢ‖²一次遍历累加四个标量一个三角函数——这就是旋转角度自动涌现的全部秘密θ* 不是配置出来的是解出来的。残差判断这到底是不是旋转的标尺对齐之后还能剩多少误差morphicons 计算归一化 RMS 残差res √(Σ|σRa−b|² / Σ‖b‖²)它就是形状距离res ≈ 0A 和 B 就是同一个形状只是转了/缩了——纯旋转res较大形状确实不同——老老实实做坐标变形这样旋转组从一个需要人工声明的概念变成了一个数学的涌现性质arrow-right → arrow-down 的残差趋近 0、θ 90°系统自己就选择了纯旋转。两个工程上的精修细节最小旋转平局裁决src/core/plan.ts像直线这种反转后重合的对称形状两个遍历方向的残差都是 0但推出的旋转角却不同。打分用score res 0.05·|θ|/π——先看变形、再取短路径避免图标莫名其妙多转 180°。全局混合src/core/plan.ts先逐子路径 Procrustes再对整幅图标做一次全局 Procrustes。若全局残差 0.005说明整个图标是刚体关系所有子路径共享同一个 (θ, σ)一起转——比如箭头的杆和箭头同向旋转而不会各转各的。极坐标插值让旋转真的转过去算出 (θ, σ) 还不够得在对的空间里插值。如果直接对坐标 lerp旋转问题照样塌缩。src/core/interpolate.ts 中的interpPolar把运动拆成相似变换 残差两部分P(t) c(t) σ*ᵗ · R(t·θ*) · [(1−t)·aᶜ t·b̃]角度线性插值θ* 已保证走短路径缩放对数线性插值σᵗ exp(t·lnσ)正实数上的测地线方括号里是对齐系内的残差混合三个漂亮的性质若 B 就是 A 转过去的res 0方括号恒为常数 →纯旋转形状全程刚性若无旋转退化为干净的坐标变形弹簧超调时 t 1公式天然外推旋转和缩放会轻微过冲再回弹免费的多汁感 还有一个容易忽略的修正——块体运输src/core/plan.ts全局刚体旋转时若只对各子路径的质心做 lerp偏心部件会走弦弧线内侧箭头的箭头会在中途向箭杆塌陷约 1px。让质心绕全局质心搭乘同一个相似变换整个图标在飞行中保持刚性且 θ 0 时公式连续退化为普通 lerp没有任何特判。弹簧物理与永不跳跃的中断进度 0→1 由一个阻尼谐振子驱动src/core/spring.ts预设kc阻尼比 ζ手感smooth170261.00临界阻尼不超调snappy420300.73干脆轻微过冲bouncy300140.40俏皮半隐式欧拉、1/240 秒子步约 25 行代码不需要任何动画库。中断处理是这套设计最舒服的地方飞行中再次morphTo引擎用当前中间形状作为新起点重建 plan渲染缓冲已经是每子路径 64 个点天然可作为变形源并保留弹簧速度——狂点按钮动画永远连续、永远不跳变。性能与体积小得离谱plan(A, B)全流程重采样 匹配 ProcrustesN 64 时亚毫秒实测 0.01–0.06 ms每帧零数值内存分配插值器直接写入 plan 预分配的Float64Array只有d字符串一帧一次分配核心入口 gzip6.60 KBCI 里用 size-limit 卡着 7 KB 的红线上手体验安装一行命令React 用法就是换个 propimport { MorphIcon } from morphicons/react; MorphIcon icon{open ? X : Menu} springsnappy /想看每对图标的 θ、σ、residual 读数仓库自带 playgroundbun run play # 38 个真实图标逐对展示检测到的相似变换想深入算法细节推荐按这个顺序读src/core/plan.ts对应关系与 Procrustes 对齐→ src/core/interpolate.ts极坐标插值与块体运输→ src/core/spring.ts弹簧数学原理的完整叙述见 README.md 的 How the math works 一节设计决策见 docs/adr/。一句话总结别再手写旋转组了。把两个图标交给一个atan2剩下的——角度、缩放、残差、刚体运输、弹簧过冲——全部由数学涌现。这大概就是用计算替代配置这句话最好的注脚。【免费下载链接】morphiconsAny icon morphs into any other — universal morphing for stroke-based icons with spring physics. Zero dependencies, ~7 KB gzip.项目地址: https://gitcode.com/gh_mirrors/mo/morphicons创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表