前端图形学【免费下载链接】bloubSVG recreation of the x.ai bot avatar. One shape morphing through 14 states, measured off the reference video frame by frame.项目地址https://gitcode.com/gh_mirrors/bl/bloub点击查看免费下载bloub 是一个用纯 SVG 复刻 x.ai 机器人头像的开源项目一个黑色填充形状在 14 种状态之间平滑变形双眼独立运动。它把导出做到了极致——头像可导出为SVG、PNG、动画 SVG 和 GIF整条时间轴可导出为GIF 或 MP4其中静态格式完全零依赖唯一的视频编码器也在首次请求时才按需加载。本文将带你逐层拆解 bloub 导出功能的实现原理包括那个堪称全项目最精妙的手写 LZW 编码器。一张表看懂四种格式各有什么定位 格式导出对象帧率/尺寸依赖适合场景PNG静态头像1024×1024无canvas所有平台头像Discord 128、X 400、GitHub 500、Slack 512SVG静态头像矢量无级无XMLSerializer要放大、要编辑、要贴进 Figma 的场合动画 SVG静态头像 眼神动画30 关键帧/秒无通用比 GIF 更小更顺GIF头像 / 整个周期20 fps / 320px无手写 LZWDiscord / Slack 动态头像它们拒收 SVGMP4整个周期30 fps / 1024pxmediabunny按需加载展示完整时间轴的场合完整的格式目录定义在 src/ui/export.ts 的ACTIONS常量中官方设计说明见 docs/export.md。SVG 与 PNG序列化活的 SVG而不是重画一遍bloub 导出静态图的第一原则是导出屏幕上的那个 SVG而不是在旁边再造一个。为什么敢这么做因为机器人组件 BloubBot.vue 输出的 SVG 天生自包含没有var(--...)CSS 变量、没有 Tailwind 类名每个形状的fill都是写死的十六进制色值。于是导出只需要三步见 src/ui/capture.tscloneNode克隆屏幕上正在显示的 SVG 节点去掉页面专属的class补上xmlns、viewBox和显式的width/heightFirefox 对没有固有尺寸的 SVG 拒绝栅格化会输出空白画布用XMLSerializer序列化、剥掉 XML 注释就是一份干净的 SVG 文件。PNG 则走 canvas 栅格化把 SVG 包装成 Blob 创建对象 URL而不是data:URL——btoa会被aria-label里的法语重音搞崩解码后drawImage最后canvas.toBlob(image/png)。两个安全前提也值得一提SVG 里没有foreignObject和image元素这是仅有的两种会污染 canvas 导致toBlob失败的东西。另外注意两个刻意的产品决策见 src/ui/export.tsPNG 只给 1024 一种尺寸——要不要更大尺寸不是用户该做的主想要无级放大请拿 SVG不提供静态 GIF——它的透明度只有 1 bitPNG 那里 8 bit alpha 的平滑边缘在 GIF 里会变成锯齿台阶。动画 SVG为什么它比 GIF 更小、还更丝滑在 bloub 里静止时身体几乎是纹丝不动的——实测 3 秒内轮廓只移动 1.17 个单位半径为 100按导出尺寸算约 1.5 个像素。所以活全部来自眼睛目光漂移 眨眼。基于这个测量src/ui/anime.ts 的svgAnime不重建任何图形只把首帧 SVG 中每个眼睛的transform属性替换成一段 CSS 关键帧动画。于是关键帧按 30/s 输出——一个关键帧只是一串矩阵文本密度几乎免费浏览器负责插值——动画按屏幕刷新率平滑播放而 GIF 这类翻页书格式 20 fps 下眨眼0.18 秒只能分到 3~4 帧肉眼可见地卡顿最终成品约16 kB而最早做过的动画 WebP 方案又卡又重167 kB直接淘汰。还有两个不能少的细节transform-box: view-box; transform-origin: 0 0否则 SVG 上的 CSS 变换会绕包围盒中心转眼睛直接飞到球的另一边以及animation-direction: alternate——目光漂移的周期刻意取互质数、永不重复普通循环会在接缝处跳变而往返播放能严丝合缝地闭合成环反转的眨眼依然是眨眼。GIF唯一会提问的导出和它的手写 LZW 编码器GIF 存在的全部理由是平台可达性Discord 和 Slack 接受 GIF 做动态头像但任何地方都不接受 SVG。而它 1 bit 的透明度逼出了唯一的一个交互——GifDialog.vue 会问你要透明底还是白色底透明底的抗锯齿边缘被 50% 阈值切断呈台阶状实心底色则能让边缘有东西可融入而重新变滑。选白底时眼睛本质是填充了paper色的洞必须被替换成背景色否则会留一圈暗环。GIF 的容器在 src/ui/anime.ts 的gifIndexe里逐字节手拼GIF89a头、Netscape 无限循环扩展、每帧的 Graphic Control Extension透明帧用清除至背景消除模式防止拖影实心帧用保持原样防止闪烁帧延迟以百分之一秒为单位、最低 20 和 1 在不同阅读器里行为不一致。手写 LZW 编码器一个 bit 都不能错真正的精华是 anime.ts 中的lzw函数。GIF 用 LZW 压缩bloub 没有引入任何压缩库而是从零实现变长编码、从最低有效位开始逐位写入、超过 255 字节切子块。它藏着一个整个格式唯一的深坑编码器在发出一个代码之后立刻把自己的字典条目写进去而解码器要等到读到下一个代码才写入自己的条目。解码器因此永远落后一个条目编码器扩展码宽的动作也必须相应地晚一步——判断条件必须是suivant (1 taille)绝不能写成。写错这一个比较符两边就会失同步所有阅读器都会拒绝这个文件而文件结构上看不出任何破绽。所以测试 anime.test.ts 干脆内置了一个完整的 GIF 解码器做往返测试把编码流解码回像素逐位比对——结构断言抓不住这种错误只有像素能。调色板按频率选渲染走两遍一个 30 秒的周期有 624 帧、颜色远超 255 种调色板必须取舍。bloub 的做法anime.ts第一遍只统计每种颜色的像素数不保留像素否则会占 255 MB 内存定板取出现最多的255 种颜色index 0 留给透明没被选中的颜色归并到最近邻带缓存每种颜色只找一次第二遍重新渲染渲染是确定性的重放得到一模一样的帧逐帧索引化后编码。实测输出60 帧 320² 的动画 GIF 仅 147 kBChrome 解码回来透明角完好、帧间 2033 个像素在变化。MP4质量旋钮是量化器不是码率导出完整周期是另一道题帧数、码流、内存全都要重新算详见 docs/export.md。帧永远不累积src/ui/video.ts 逐帧渲染、逐帧await source.add(...)——正是这个await把编码器的背压传回来把内存锁死在一帧的规模视频永远不透明VideoEncoder对 H.264 和 VP9 都拒绝alpha: keep所以对话框对 MP4 直接隐藏背景选项组编码器按需加载mediabunny 高达 43 kB gzip静态引入比被否掉的 vue-i18n 还大所以只在导出 MP4 的那一刻动态 import平时网站只多付 0.7 kB 的代价分辨率与帧率按格式分离GIF 受文件体积约束320px/20fps视频压缩的是运动而非逐帧存储理直气壮用 1024px/30fps。混用过的版本测出 93 kbps画质只有缩略图——这正是它看着像 GIF 的根源最反直觉的一点质量旋钮是QUANTIZER量化器 QP不是码率。Chrome 117 支持固定量化器编码时mediabunny 的QUALITY_HIGH等命名档位根本不设码率调高码率什么都改变不了。实测边缘误差QP 22 → 3.06QP 16 → 1.75QP 10 → 0.58。最终 video.ts 显式传new Quality({ quantizer: 12, bitrate })码率只作为不支持固定量化器的浏览器的回退。实测默认周期31.2 秒MP4 936 帧 1024² 仅 5.48 MB、约 4 秒完成如果用MediaRecorder实时录制得干等整整 31.2 秒——编码器胜出。怎么选三句话决策指南 要头像→ 拿 PNG1024 够用一切平台要进 Figma / Illustrator 可编辑 → 复制SVG 文本bloub 特意用writeText而非图片粘贴粘回去才是可编辑矢量要动态头像Discord / Slack→ GIF白底最稳妥要展示整条动画时间轴→ MP41024/30fps需要往群里甩小文件时再考虑 GIF。核心文件速查文件职责src/ui/export.ts纯逻辑层导出画框、文件名规则、各格式参数目录可 node 环境测试src/ui/capture.tsDOM 层SVG 序列化、canvas 栅格化、剪贴板、离屏序列渲染src/ui/anime.ts动画 SVG 组装 GIF 容器拼装 手写 LZW 编码器 频率调色板src/ui/video.tsMP4 编码mediabunny 动态加载、量化器 QP12src/components/GifDialog.vueGIF 背景选择对话框唯一会提问的导出docs/export.md官方导出设计文档含全部实测数据从 XMLSerializer 到手写 LZWbloub 的导出系统证明了一个像素级的复刻项目其输出管线可以和绘制管线一样讲究——每个参数都是测量出来的每个看似多余的测试都在守护一个真实的坑。赞分享前端图形学【免费下载链接】bloubSVG recreation of the x.ai bot avatar. One shape morphing through 14 states, measured off the reference video frame by frame.项目地址https://gitcode.com/gh_mirrors/bl/bloub点击查看免费下载相关推荐Mermaid.js数据导出PNG、SVG、PDF等多种格式的导出方案Mermaid.js数据导出PNG、SVG、PDF等多种格式的导出方案 引言为什么需要图表导出功能 在日常开发和技术文档编写中Mermaid.js已经成图表库前端数据可视化X6 画布导出插件 Export 完全指南SVG / PNG / JPEG 导出 API 与源码级原理X6 画布导出插件 Export 完全指南SVG / PNG / JPEG 导出 API 与源码级原理 X6 是使用 SVG 与 HTML 进行渲染的 Jav前端图形学Gephi导出功能完全指南PNG、SVG、PDF格式的最佳实践Gephi导出功能完全指南PNG、SVG、PDF格式的最佳实践 Gephi作为专业的开源网络可视化平台提供了强大的导出功能让用户能够将精心设计的网络图保存数据可视化数据分析桌面应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考