36 KiB
项目计划(2026-02-08)
1. 当前状态
1.1 已完成(保留能力)
- Web 应用主链路可用:字体选择、预览、收藏、导出(SVG/PNG)
- Python CLI 可独立运行:
font2svg.py、pic2svg.py - 字体清单生成脚本可用:
scripts/generate-font-list.py
1.2 新增目标
- 在不影响现有 Web/CLI 的前提下,新增微信小程序版本(新目录:
miniprogram/) - 小程序版本以移动端体验为主,不做 Web 页面 1:1 迁移
2. 小程序专项范围
2.1 技术选型
开发方式:微信小程序原生开发(TypeScript + WXML + WXSS)
- 理由:充分利用 Worker、Canvas 2D 等原生能力,避免框架转换损耗
- 放弃 Taro/uni-app:复杂业务逻辑转换可能引入额外兼容问题
核心算法复用:直接复制 Web 端 utils/ 核心模块
svg-builder.ts:字形路径生成与 SVG 构建(纯函数,无浏览器 API 依赖)text-layout.ts:文本换行逻辑font-loader.ts:适配为小程序 API(wx.downloadFile+wx.getFileSystemManager)
字体资源托管:云存储(微信云开发 / 第三方 OSS)
- 字体文件上传到云端,生成 CDN URL
fonts.json清单文件托管(含字体元数据)- 按需下载,突破小程序 2MB 主包限制
2.2 范围内(MVP)
- 文本输入 + 字体选择(搜索/分类/收藏)+ 实时预览
- 字号、颜色调节
- 导出 SVG(文件系统写入 + 分享)
- 导出 PNG(Canvas 2D 渲染 + 保存相册)
- 本地状态持久化(文本、字体、参数)
2.3 范围外(后续迭代)
- HarfBuzz/WASM 高级 shaping(首版关闭,使用 opentype.js 基础能力)
- 多字体对比预览(Web 版特性,小程序屏幕受限)
- 批量导出与 ZIP 打包(小程序 API 限制)
- 字体树分类折叠展开(简化为扁平列表 + 搜索)
3. 移动端差异与技术策略
3.1 交互策略
- 单列流程:输入 -> 预览 -> 导出
- 大按钮、触控优先、避免多栏复杂布局
3.2 性能策略
- 字形生成放入
Worker,主线程专注渲染 - 长文本分批处理,支持任务取消
- 字体对象缓存与内存上限控制
3.3 平台适配策略
- 文件系统:
wx.getFileSystemManager()+writeFile/saveFile - PNG 导出:
wx.canvasToTempFilePath - 文件分享:
wx.shareFileMessage - 图片保存:
wx.saveImageToPhotosAlbum(带授权处理) - 预览:优先
image组件渲染 SVG,必要时回退 PNG
3.4 包体策略
- 字体与 Worker 支持分包/按需加载
- 首包仅保留必要资源
4. 目录结构设计
miniprogram/
├── pages/ # 页面
│ ├── index/ # 主页(输入+预览+导出)
│ │ ├── index.wxml
│ │ ├── index.ts
│ │ ├── index.wxss
│ │ └── index.json
│ └── font-picker/ # 字体选择页
│ ├── font-picker.wxml
│ ├── font-picker.ts
│ ├── font-picker.wxss
│ └── font-picker.json
├── components/ # 组件
│ ├── text-input/ # 文本输入组件
│ ├── font-item/ # 字体列表项
│ ├── preview-card/ # 预览卡片
│ └── export-panel/ # 导出面板
├── workers/ # Worker 线程
│ └── svg-generator/ # SVG 生成 Worker
│ └── index.ts
├── utils/ # 工具函数
│ ├── core/ # 核心算法(复用 Web)
│ │ ├── svg-builder.ts # SVG 生成核心
│ │ ├── text-layout.ts # 文本换行
│ │ └── glyph-path.ts # 字形路径
│ ├── mp/ # 小程序专用
│ │ ├── font-loader-mp.ts # 字体加载适配
│ │ ├── storage-mp.ts # 存储适配
│ │ ├── canvas-export.ts # Canvas 导出
│ │ └── share-mp.ts # 分享/保存
│ └── worker-manager.ts # Worker 管理器
├── typings/ # 类型定义(复用 Web types/)
│ └── font.d.ts
├── assets/ # 静态资源
│ └── icons/ # 图标(复用 Web)
├── styles/ # 全局样式
│ └── variables.wxss # 设计 token(基于 Figma)
├── miniprogram_npm/ # npm 构建产物
├── app.ts # 小程序入口
├── app.json # 全局配置
├── app.wxss # 全局样式
├── package.json # npm 依赖
├── tsconfig.json # TypeScript 配置
└── project.config.json # 微信开发者工具配置
5. 里程碑与验收
M1:工程骨架(P0)
实施步骤:
- 创建
miniprogram/目录,微信开发者工具初始化原生项目 - 配置
project.config.json:- 设置
miniprogramRoot、基础库版本 ≥ 2.9.0 - 启用 TypeScript 编译插件
- 设置
- 创建
package.json,安装依赖:{ "dependencies": { "opentype.js": "^1.3.4" }, "devDependencies": { "miniprogram-api-typings": "latest", "typescript": "~5.9.3" } } - 配置
tsconfig.json(参考frontend/tsconfig.json) - 创建
app.ts、app.json、app.wxss - 创建首页
pages/index/(空白页面) - 微信开发者工具"构建 npm"
验收标准:
- 开发者工具可启动并进入首页(显示空白页面)
- TypeScript 编译无错误
- npm 包构建成功(
miniprogram_npm/生成)
M2:核心算法迁移(P0)
实施步骤:
-
复制纯算法模块:
frontend/src/utils/svg-builder.ts→miniprogram/utils/core/svg-builder.ts- 保留
generateSvg()函数(移除generateSvgWithHarfbuzz())
- 保留
frontend/src/utils/text-layout.ts→miniprogram/utils/core/text-layout.ts- 提取字形路径逻辑到
miniprogram/utils/core/glyph-path.ts
-
复制类型定义:
frontend/src/types/font.d.ts→miniprogram/typings/font.d.ts
-
平台适配层开发:
miniprogram/utils/mp/font-loader-mp.ts:loadFontFromUrl():使用wx.downloadFile()+wx.getFileSystemManager()
miniprogram/utils/mp/storage-mp.ts:- 封装
wx.getStorageSync/setStorageSync - 提供类型安全的
getStorage<T>、setStorage<T>方法
- 封装
-
编写单元测试:
miniprogram/utils/core/__tests__/svg-builder.test.ts- 测试用例:单字符、多字符、空字符串、特殊字符
验收标准:
- 输入文本 + Font 对象可生成有效 SVG 字符串
- SVG 结构正确(包含
<svg>、<path>、viewBox) - 核心算法单测通过(覆盖率 > 80%)
M3:预览链路(P0)
实施步骤:
-
主页布局(
pages/index/):- 文本输入区(
<textarea>) - 字体选择器(跳转到
font-picker页面) - 参数调节(字号
<slider>、颜色<picker>) - 预览区(
<image>或<canvas>) - 导出按钮(底部固定)
- 文本输入区(
-
字体选择页(
pages/font-picker/):- 搜索框(
<input>实时过滤) - 字体列表(
<scroll-view>+ 虚拟列表优化) - 从云端加载
fonts.json
- 搜索框(
-
预览生成逻辑:
- 用户选择字体 → 触发
loadFontFromUrl() - 调用
generateSvg()生成 SVG 字符串 - 渲染方案:
<image src="data:image/svg+xml,{{encodedSvg}}">
- 用户选择字体 → 触发
-
状态管理:
- 使用
storage-mp持久化:inputText、fontSize、textColor、selectedFontId - 页面
onLoad()恢复状态
- 使用
验收标准:
- 真机可稳定预览(iOS + Android 各 1 款机型)
- 文本修改后预览更新延迟 < 500ms
- 切换字体后预览正确渲染
- 调整字号/颜色后预览实时刷新
M4:导出与分享(P0)
实施步骤:
-
SVG 导出(
utils/mp/share-mp.ts):saveSvgToLocal(svgString, filename):- 使用
wx.getFileSystemManager().writeFile()写入用户目录 - 返回文件临时路径
- 使用
shareFileToChat(filePath):- 调用
wx.shareFileMessage()分享到微信聊天
- 调用
-
PNG 导出(
utils/mp/canvas-export.ts):svgToPng(svgString, width, height):- 创建离屏 Canvas(
wx.createOffscreenCanvas({ type: '2d' })) - SVG → Image → Canvas 渲染
- 使用
canvas.toDataURL()或wx.canvasToTempFilePath()导出
- 创建离屏 Canvas(
savePngToPhotos(pngPath):- 调用
wx.saveImageToPhotosAlbum() - 处理授权拒绝(引导用户手动开启)
- 调用
-
错误处理:
- 文件写入失败提示
- 授权拒绝引导(显示设置路径)
- 分享取消提示
验收标准:
- SVG 导出成功率 > 95%(真机测试 20 次)
- PNG 导出图片清晰度正常(无模糊、颜色偏差)
- 授权拒绝后提示明确,可引导用户设置
- 文件分享到微信聊天后可正常打开
M5:性能与稳定性(P1)
实施步骤:
-
Worker 化字形生成:
- 创建
workers/svg-generator/index.ts - 主线程 → Worker 消息协议:
type GenerateRequest = { id: string fontBuffer: ArrayBuffer text: string options: SvgGenerateOptions } - Worker 内加载 opentype.js,调用
generateSvg(),返回结果 - 创建
utils/worker-manager.ts管理 Worker 生命周期
- 创建
-
字体缓存策略:
- 全局缓存已解析的 Font 对象(Map<fontId, Font>)
- LRU 淘汰:最多缓存 20 个字体
- 页面
onUnload释放 Worker 资源
-
性能优化:
- 限制并发 Worker 任务数(最多 3 个)
- 超时任务自动取消(5 秒)
- 长文本分段处理(每段 100 字)
-
真机回归测试:
- iOS:iPhone SE2、iPhone 12
- Android:小米 10、华为 P40
- 测试场景:
- 500 字长文本预览
- 连续切换 10 个字体
- 连续导出 5 次 PNG
验收标准:
- 中等机型(骁龙 750G)长文本(500 字)预览生成时间 < 3s
- 主线程无明显卡顿(帧率 > 50fps)
- 连续操作 10 次无内存泄漏(内存增长 < 50MB)
- 低端机(iPhone SE2)可降级正常使用
6. 代码复用策略
6.1 直接复用(无需修改)
| Web 文件 | 小程序文件 | 说明 |
|---|---|---|
frontend/src/utils/svg-builder.ts |
miniprogram/utils/core/svg-builder.ts |
SVG 生成核心(移除 HarfBuzz 部分) |
frontend/src/utils/text-layout.ts |
miniprogram/utils/core/text-layout.ts |
文本换行逻辑 |
frontend/src/types/font.d.ts |
miniprogram/typings/font.d.ts |
类型定义 |
frontend/src/assets/icons/*.svg |
miniprogram/assets/icons/*.svg |
SVG 图标 |
6.2 适配复用(需修改 API)
| Web 文件 | 小程序文件 | 修改点 |
|---|---|---|
frontend/src/utils/font-loader.ts |
miniprogram/utils/mp/font-loader-mp.ts |
fetch → wx.downloadFile() + wx.getFileSystemManager() |
frontend/src/utils/download.ts |
miniprogram/utils/mp/canvas-export.ts |
Blob → wx.canvasToTempFilePath(),移除 JSZip |
frontend/src/stores/fontStore.ts |
pages/index/index.ts 内联状态 |
localStorage → wx.storage,简化 Pinia 为页面级状态 |
6.3 重新实现(平台差异大)
frontend/src/components/SvgPreview.vue→pages/index/index.wxml预览区- 移除
IntersectionObserver,改用<scroll-view bindscrolltolower> - 移除分批渲染(单字体预览无需分批)
- 移除
frontend/src/components/FontTree.vue→pages/font-picker/- 树形结构降级为扁平列表 + 搜索
- 移除分类折叠展开
7. 资源处理方案
7.1 字体资源托管
方案:Cloudflare CDN + 海外服务器(已选定)
7.1.1 架构设计
字体文件流转:
海外服务器(fonts.biboer.cn)
↓ 托管静态文件
Cloudflare CDN(自动缓存)
↓ 全球加速
微信小程序(按需下载)
优势:
- ✅ 免备案:海外服务器无需 ICP 备案
- ✅ 免费 CDN:Cloudflare 免费计划提供全球加速
- ✅ 自动 HTTPS:Cloudflare 提供免费 SSL 证书
- ✅ 高可用:自动 DDoS 防护 + 故障转移
- ✅ 缓存优化:字体文件自动缓存到全球节点
7.1.2 服务器端配置
Nginx 静态文件服务配置:
# /etc/nginx/sites-available/font2svg
server {
listen 80;
server_name fonts.biboer.cn;
# Cloudflare 会自动处理 HTTPS,这里只需 HTTP
# 字体文件根目录
root /var/www/font2svg;
# 字体文件访问
location /fonts/ {
# CORS 配置(允许小程序跨域)
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range";
# 缓存控制(30天)
expires 30d;
add_header Cache-Control "public, immutable";
# 字体 MIME 类型
types {
font/ttf ttf;
font/otf otf;
font/woff woff;
font/woff2 woff2;
}
# 支持 OPTIONS 预检请求
if ($request_method = OPTIONS) {
return 204;
}
}
# fonts.json 配置
location = /fonts.json {
add_header Access-Control-Allow-Origin *;
add_header Content-Type application/json;
expires 1h; # 短缓存,方便更新
}
# 可选:Web 应用代理
location / {
proxy_pass http://localhost:5173;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
部署命令:
# 1. 创建目录
ssh user@fonts.biboer.cn "mkdir -p /var/www/font2svg/fonts"
# 2. 上传字体文件
scp -r frontend/public/fonts/* user@fonts.biboer.cn:/var/www/font2svg/fonts/
# 3. 上传 fonts.json
scp frontend/public/fonts.json user@fonts.biboer.cn:/var/www/font2svg/
# 4. 设置权限
ssh user@fonts.biboer.cn "chmod -R 755 /var/www/font2svg"
# 5. 重启 Nginx
ssh user@fonts.biboer.cn "sudo systemctl restart nginx"
# 6. 测试访问
curl -I https://fonts.biboer.cn/fonts.json
7.1.3 Cloudflare 配置
步骤 1:添加网站到 Cloudflare
# 1. 登录 Cloudflare Dashboard: https://dash.cloudflare.com/
# 2. 添加网站 → 输入 biboer.com
# 3. 选择 Free 计划
# 4. 更新 DNS 服务器(在域名注册商处配置)
步骤 2:DNS 记录配置
类型: A
名称: fonts
内容: [你的服务器IP]
代理状态: 已代理(橙色云朵)✓
TTL: Auto
步骤 3:SSL/TLS 设置
SSL/TLS → 概述
加密模式: 完全(严格)或 灵活
SSL/TLS → 边缘证书
- 始终使用 HTTPS: 开启 ✓
- 自动 HTTPS 重写: 开启 ✓
- 最低 TLS 版本: TLS 1.2
步骤 4:缓存规则(优化字体加载速度)
规则 → 页面规则 → 创建页面规则
URL: fonts.biboer.cn/fonts/*
设置:
- 缓存级别: 缓存所有内容
- 边缘缓存 TTL: 1 个月
- 浏览器缓存 TTL: 1 个月
步骤 5:性能优化
速度 → 优化
- Auto Minify: 无需开启(字体是二进制)
- Brotli: 开启 ✓
- HTTP/3: 开启 ✓
网络
- WebSockets: 开启 ✓
- gRPC: 开启 ✓
7.1.4 更新 fonts.json
[
{
"id": "其他字体/AlimamaDaoLiTi",
"name": "AlimamaDaoLiTi",
"category": "其他字体",
"path": "https://fonts.biboer.cn/fonts/其他字体/AlimamaDaoLiTi.ttf",
"size": 2345678
},
{
"id": "庞门正道/庞门正道粗书体",
"name": "庞门正道粗书体",
"category": "庞门正道",
"path": "https://fonts.biboer.cn/fonts/庞门正道/庞门正道粗书体.ttf",
"size": 3456789
}
]
7.1.5 小程序后台配置
微信公众平台配置步骤:
1. 登录: https://mp.weixin.qq.com/
2. 进入: 开发 → 开发管理 → 服务器域名
3. downloadFile 合法域名 → 添加:
https://fonts.biboer.cn
注意事项:
- 必须使用 HTTPS(Cloudflare 自动提供)
- 域名无需备案(海外服务器)
- 每月最多修改 5 次
- 配置后等待 5-10 分钟生效
7.1.6 小程序端加载代码
// miniprogram/utils/mp/font-loader-mp.ts
export async function loadFontFromUrl(
url: string,
onProgress?: (loaded: number, total: number) => void
): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
// 1. 下载字体文件(通过 Cloudflare CDN)
const downloadTask = wx.downloadFile({
url: url, // https://fonts.biboer.cn/fonts/xxx.ttf
success: (res) => {
if (res.statusCode === 200) {
// 2. 读取为 ArrayBuffer
wx.getFileSystemManager().readFile({
filePath: res.tempFilePath,
success: (data) => {
resolve(data.data as ArrayBuffer)
},
fail: reject
})
} else {
reject(new Error(`HTTP ${res.statusCode}`))
}
},
fail: reject
})
// 3. 进度回调
if (onProgress) {
downloadTask.onProgressUpdate((res) => {
onProgress(res.totalBytesWritten, res.totalBytesExpectedToWrite)
})
}
})
}
// 示例:加载 fonts.json
export async function loadFontsList(): Promise<FontInfo[]> {
return new Promise((resolve, reject) => {
wx.request({
url: 'https://fonts.biboer.cn/fonts.json',
method: 'GET',
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data as FontInfo[])
} else {
reject(new Error(`加载字体列表失败: ${res.statusCode}`))
}
},
fail: reject
})
})
}
7.2 图标资源处理
方案选择:
- 小程序
<image>组件支持 SVG(基础库 ≥ 2.3.0) - 直接复制
frontend/src/assets/icons/*.svg→miniprogram/assets/icons/ - 按 Figma 设计稿调整尺寸(假设 48rpx)
路径映射(创建 miniprogram/utils/icons.ts):
export const ICONS = {
SEARCH: '/assets/icons/search.svg',
FAVORITE: '/assets/icons/星程字体.svg',
EXPORT_SVG: '/assets/icons/export-svg.svg',
EXPORT_PNG: '/assets/icons/export-png.svg',
ARROW_RIGHT: '/assets/icons/expand.svg',
}
8. UI 设计规范(基于 Figma 实际设计稿)
8.1 设计稿概览
整体尺寸:426 x 956 px(微信小程序标准尺寸)
页面结构(单页垂直布局):
- 顶部工具栏 (410x48px) - Logo + 品牌 + 字号滑块 + 颜色选择
- 输入和导出栏 (410x38.78px) - 文本输入框 + 导出按钮组
- 预览区 (410x649.21px) - 可滚动的多字体预览卡片列表
- 底部选择区 (410x180px) - 左:字体选择(树形) | 右:已收藏字体
布局特点:
- 无 TabBar,单页垂直布局
- 预览区占据主要高度(68%),支持滚动
- 底部字体选择和收藏并排显示(各 200px 宽)
- 支持多字体同时预览对比
8.2 颜色方案(从 Figma 提取)
| 颜色名称 | HEX 值 | 用途 |
|---|---|---|
| 主色/primary-6 | #9B6BC2 |
主按钮、滑块、选中状态 |
| 主色/primary-7 | #8552A1 |
主色深色变体 |
| 主色/primary-1 | #F3EDF7 |
主色浅色背景 |
| 填充/fill-1 | #F7F8FA |
输入框背景 |
| 填充/fill-3 | #E5E6EB |
滑块轨道、边框 |
| 填充/fill-4 | #C9CDD4 |
分割线、次要边框 |
| 填充/white | #FFFFFF |
纯白背景 |
| 文字/text-1 | #FFFFFF |
白色文字 |
| 文字/text-2 | #C9CDD4 |
禁用文字 |
| 文字/text-3 | #86909C |
次要文字(字体名称) |
| 文字/text-4 | #4E5969 |
主要文字 |
8.3 尺寸规范(转换为 rpx)
字体大小:
- 标题:28rpx (14px × 2)
- 字体分类名:21rpx (10.5px × 2)
- 字体名称(预览):16rpx (8px × 2)
- 字体名称(列表):14rpx (7px × 2)
- 输入提示:12.928rpx (6.464px × 2)
组件尺寸:
- Logo/图标:96rpx × 96rpx (48px × 2)
- 字号滑块高度:~49rpx (24.504px × 2)
- 滑块调节按钮:24.504rpx × 24.504rpx
- 颜色选择器:48rpx × 48rpx
- 输入框高度:77.57rpx (38.785px × 2)
- 导出按钮(SVG/PNG):61.714rpx × 61.714rpx
- 复选框:21rpx × 21rpx (10.5px × 2)
- 收藏星标图标:21rpx × 19.516rpx
间距:
- 组件外边距:16rpx (8px × 2)
- 组件内边距:6.464rpx (3.232px × 2)
- 卡片圆角:12.928rpx (6.464px × 2)
- 列表项间距:14rpx (7px × 2)
- 预览卡片间距:6.4rpx (3.2px × 2)
8.4 详细组件规范
8.4.1 顶部工具栏(Frame 7)
[Logo 96×96] [TextToSVG文字图] [A-减号 滑块轨道 A+加号] [颜色选择器 48×48]
- Logo:圆角 24rpx,紫色渐变背景(径向渐变)
- 品牌文字:150px 宽图片资源
- 字号滑块:
- 轨道:背景 #E5E6EB,高度 3.064rpx,圆角 20rpx
- 填充:背景 #9B6BC2
- 拖动按钮:白色圆形,阴影,尺寸 ~36rpx
- 减号/加号图标:24.504rpx
- 颜色选择器:圆形色轮图标
- 间距:组件间 16rpx
8.4.2 输入和导出栏(Frame 8)
[输入框(flex-1)] [导出图标 + SVG按钮 + PNG按钮]
- 输入框:
- 背景:#F7F8FA
- 圆角:12.928rpx (6.464px × 2)
- 内边距:6.464rpx
- 占位文字:灰色 #4E5969,12.928rpx
- 导出按钮组:
- 容器:白色背景,边框 #E5E6EB (1.286rpx),圆角 10.286rpx
- 内边距:上下 5.142rpx,左右 10.286rpx
- 图标间距:11.572rpx
- 导出文字图标 + SVG 图标 + PNG 图标
8.4.3 预览区(Frame 11/12)
[标题:效果预览 28rpx]
[字体卡片1]
├─ 折叠栏:[展开图标] 小米小松字体 [复选框]
└─ 预览内容:星程字体 SVG 渲染
[字体卡片2]
├─ 折叠栏:...
└─ 预览内容:...
[...]
- 外容器:
- 边框:#F7E0E0 (0.8rpx)
- 圆角:12.8rpx
- 内边距:6.4rpx
- 标题:黑色,28rpx,PingFang SC Regular
- 字体卡片:
- 折叠栏高度:24rpx
- 底部边框:#C9CDD4 (0.48rpx)
- 展开图标:16rpx × 16rpx(树形折叠图标)
- 字体名称:灰色 #86909C,16rpx
- 复选框:16rpx × 16rpx(选中时紫色填充)
- 预览内容区:
- 背景:白色
- 高度:自适应内容(根据文本长度和字号)
- 内边距:3.706rpx
- SVG 预览图片自动居中显示
- 卡片间距:6.4rpx
8.4.4 底部选择区(Frame 2130)
左侧:字体选择 (400rpx 宽)
[标题:字体选择 28rpx]
├── 小米字体(分类,可折叠)
│ ├─ [图标] 小米大宋字体 [复选框] [星标]
│ └─ [图标] 小米小松字体 [复选框] [星标✓]
├── 星程字体(分类,可折叠)
│ ├─ [图标] 星程中楷体 [复选框] [星标]
│ └─ [图标] 星程小标宋 [复选框] [星标]
- 容器:
- 边框:#F7E0E0 (1.166rpx)
- 圆角:18.666rpx
- 内边距:9.334rpx
- 分类标题:
- 字体:PingFang SC Medium
- 大小:21rpx (10.5px × 2)
- 颜色:黑色
- 树形缩进:
- 缩进线:19.834rpx 宽
- 折叠/展开图标:18.666rpx
- 字体项:
- 高度:28rpx (14px × 2)
- 底部边框:#C9CDD4 (1.166rpx)
- 布局:[图标 18.666rpx] + [名称 flex-1 灰色 14rpx] + [复选框 21rpx] + [星标 21rpx]
- 间距:9.334rpx
右侧:已收藏字体 (400rpx 宽)
- 布局同左侧,但无分类结构
- 仅显示已收藏(星标已填充)的字体
- 标题:"已收藏字体"
8.5 交互规范(基于 Figma Annotations)
文本输入:
- 输入后回车或点击"预览"按钮生效
- 实时更新所有已勾选字体的预览
字号调节:
- 拖动滑块或点击减号/加号按钮
- 预览窗口文字大小动态变化
字体选择:
- 勾选复选框 → 添加到预览区顶部
- 取消勾选 → 从预览区移除
- 点击空心星标 → 收藏该字体(变为实心红色)
- 点击实心星标 → 取消收藏(变为空心)
预览卡片:
- 点击折叠栏(字体名称区域)→ 展开/收起预览内容
- 点击预览内容区域 → 切换导出选中状态(复选框)
- 预览区支持滚动显示多个字体
导出:
- 点击 SVG 按钮 → 导出预览区已勾选项的 SVG 文件
- 点击 PNG 按钮 → 导出预览区已勾选项的 PNG 图片
字体树:
- 从
fonts/目录读取所有字体 - 字体按目录树状分组
- 支持展开和收拢分类
- 支持单个选择或批量框选
8.6 设计 Token(miniprogram/styles/variables.wxss)
/* 颜色 */
--color-primary: #9B6BC2;
--color-primary-dark: #8552A1;
--color-primary-light: #F3EDF7;
--color-bg: #F7F8FA;
--color-bg-white: #FFFFFF;
--color-border: #E5E6EB;
--color-border-light: #C9CDD4;
--color-border-error: #F7E0E0;
--color-text: #4E5969;
--color-text-secondary: #86909C;
--color-text-disabled: #C9CDD4;
--color-text-white: #FFFFFF;
--color-danger: #FF0D0D;
/* 字号 */
--font-size-title: 28rpx;
--font-size-subtitle: 21rpx;
--font-size-body: 16rpx;
--font-size-caption: 14rpx;
--font-size-tiny: 12rpx;
/* 间距 */
--space-xs: 6rpx;
--space-sm: 12rpx;
--space-md: 16rpx;
--space-lg: 28rpx;
/* 圆角 */
--radius-sm: 10rpx;
--radius-md: 12rpx;
--radius-lg: 24rpx;
/* 尺寸 */
--icon-size-xs: 16rpx;
--icon-size-sm: 18rpx;
--icon-size-md: 21rpx;
--icon-size-lg: 48rpx;
--icon-size-xl: 96rpx;
/* 边框 */
--border-width-thin: 0.8rpx;
--border-width-normal: 1.166rpx;
--border-width-thick: 1.286rpx;
## 9. 风险与降级
### 9.1 技术风险
| 风险 | 影响 | 降级方案 |
|------|------|---------|
| opentype.js 小程序兼容性问题 | 无法解析字体 | 改造 Python 脚本为云函数 API |
| 包体超限(> 2MB) | 无法提交审核 | 启用分包加载,首包仅保留 3 个示例字体 |
| SVG 兼容差异(部分机型) | 预览显示异常 | 回退 PNG 预览模式(Canvas 渲染) |
| Canvas 2D API 低端机性能差 | 导出 PNG 卡顿/失败 | 限制导出尺寸(最大 2048x2048)|
| Worker 不稳定(部分安卓) | 主线程阻塞 | 降级为主线程同步计算+提示处理中 |
### 9.2 业务风险
| 风险 | 影响 | 降级方案 |
|------|------|---------|
| 字体版权问题 | 法律风险 | 仅保留 SIL OFL 开源字体 |
| 云存储费用超预算 | 成本增加 | 限制字体数量到 50 个+CDN 缓存 |
| 授权拒绝率高 | 保存相册功能受限 | 强化分享到聊天引导 |
| 审核不通过 | 无法上线 | 按审核意见调整,准备替代方案 |
### 9.3 降级触发条件
- **Worker 降级**:Worker 创建失败 3 次或超时率 > 30%
- **PNG 导出降级**:Canvas 渲染失败率 > 20% 或用户机型在黑名单
- **字体数量降级**:初始加载时间 > 3s 或内存占用 > 300MB
## 10. 执行原则
- **小步迭代**:每次只完成一个明确子目标(按里程碑推进)
- **变更聚焦**:仅修改当前里程碑所需文件,避免过度设计
- **验证先行**:每次改动后真机测试核心链路
- **文档同步**:关键变化同步更新 `README.md`/`USAGE.md`
- **性能意识**:每次 commit 前检查包体大小、真机帧率
## 11. 检查清单(上线前)
### 11.1 功能检查
- [ ] 字体列表加载成功(云存储/本地)
- [ ] 搜索字体名称正常
- [ ] 选择字体→预览实时更新
- [ ] 调整字号/颜色→预览正确
- [ ] 导出 SVG→文件写入→可分享
- [ ] 导出 PNG→保存相册成功
- [ ] 授权拒绝→提示引导设置
- [ ] 关闭重开→状态恢复
### 11.2 性能检查
- [ ] 首屏加载时间 < 2s
- [ ] 预览生成时间 < 1s(50 字)
- [ ] 长文本(500 字)< 3s
- [ ] 连续操作无内存泄漏
- [ ] 低端机(iPhone SE2)可正常使用
### 11.3 兼容性检查
- [ ] iOS 真机测试(≥ 2 款)
- [ ] Android 真机测试(≥ 2 款)
- [ ] 不同屏幕尺寸适配正常
- [ ] 横屏显示正常(或禁用)
### 11.4 合规检查
- [ ] 隐私政策包含云存储/相册权限说明
- [ ] 仅保留已获授权字体
- [ ] 无敏感词、广告、诱导分享
- [ ] 符合微信小程序运营规范
## 12. 官方能力依据(已核对)
- npm 支持:`https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html`
- Canvas 指南:`https://developers.weixin.qq.com/miniprogram/dev/framework/ability/canvas.html`
- Canvas 导出:`https://developers.weixin.qq.com/miniprogram/dev/api/canvas/wx.canvasToTempFilePath.html`
- 文件系统:`https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.html`
- 写文件:`https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.writeFile.html`
- 多线程 Worker:`https://developers.weixin.qq.com/miniprogram/dev/framework/workers.html`
- WXWebAssembly:`https://developers.weixin.qq.com/miniprogram/dev/framework/performance/wasm.html`
- image 组件(含 SVG):`https://developers.weixin.qq.com/miniprogram/dev/component/image.html`
---
## 13. 下一步行动
### 13.1 立即执行(无依赖)
1. **M1 工程骨架搭建**:
```bash
cd /Users/gavin/font2svg
# 使用微信开发者工具新建小程序项目
# 选择目录:miniprogram/
# AppID:测试号或已申请的小程序 AppID
# 模板:不使用模板(空项目)
# 语言:TypeScript
- 初始化配置文件:
- 创建
miniprogram/package.json并安装依赖 - 配置
miniprogram/tsconfig.json - 配置
miniprogram/project.config.json
- 创建
13.2 设计稿已确认(可立即实施)
Figma 设计稿关键信息已提取:
- ✅ 单页垂直布局(426×956px)
- ✅ 主色调:紫色 #9B6BC2(品牌色 TextToSVG)
- ✅ 完整颜色方案和尺寸规范
- ✅ 交互规范:支持多字体对比预览、树形字体选择、收藏功能
- ✅ 组件规范:滑块、复选框、预览卡片、字体树
设计稿特色功能:
- 品牌 Logo + 名称:顶部固定,紫色渐变 Logo
- 字号滑块:实时调节预览大小(减号/滑块/加号)
- 颜色选择器:色轮图标,调节文本颜色
- 多字体预览:可同时预览多个字体效果
- 字体树:分类折叠展开,支持收藏星标
- 导出按钮组:SVG + PNG 双格式导出
下一步:可直接开始 M1 和 M3 实施
13.3 并行推进(可同步)
在等待设计稿期间,可以同步进行:
- M2 核心算法迁移:复用 Web 端代码,平台 API 适配
- 字体资源云存储准备:
- 开通微信云开发环境
- 上传字体文件到云存储
- 生成
fonts.json云端配置
13.4 优先级建议
- P0(必须完成):M1 → M2 → M3 → M4
- P1(性能优化):M5(真机测试后评估是否需要 Worker)
- P2(体验增强):收藏功能、搜索历史、多字体对比
附录:快速启动命令
A. 开发环境启动
# 1. 安装微信开发者工具(如未安装)
# https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
# 2. 创建小程序项目目录
cd /Users/gavin/font2svg
mkdir -p miniprogram
# 3. 初始化 npm(在微信开发者工具外执行)
cd miniprogram
npm init -y
npm install opentype.js@1.3.4
npm install -D miniprogram-api-typings typescript@~5.9.3
# 4. 在微信开发者工具中打开 miniprogram/ 目录
# 工具 → 构建 npm
# 5. 开始开发
# 按里程碑 M1 → M2 → M3 → M4 → M5 顺序推进
B. 字体资源部署(Cloudflare + 海外服务器)
# 1. 在服务器创建目录
ssh user@fonts.biboer.cn "mkdir -p /var/www/font2svg/fonts"
# 2. 上传字体文件
cd /Users/gavin/font2svg
scp -r frontend/public/fonts/* user@fonts.biboer.cn:/var/www/font2svg/fonts/
scp frontend/public/fonts.json user@fonts.biboer.cn:/var/www/font2svg/
# 3. 配置 Nginx(见 PLAN.md 7.1.2 节)
ssh user@fonts.biboer.cn
sudo nano /etc/nginx/sites-available/font2svg
# 粘贴配置内容,保存退出
sudo ln -s /etc/nginx/sites-available/font2svg /etc/nginx/sites-enabled/
sudo nginx -t # 测试配置
sudo systemctl restart nginx
# 4. 验证部署
curl -I https://fonts.biboer.cn/fonts.json
# 应返回 200 OK + Access-Control-Allow-Origin: *
# 5. 测试字体下载
curl -I "https://fonts.biboer.cn/fonts/其他字体/AlimamaDaoLiTi.ttf"
# 应返回 200 OK + Content-Type: font/ttf
C. Cloudflare 配置验证
# 1. 检查 DNS 是否生效
nslookup fonts.biboer.cn
# 应返回 Cloudflare 的 IP(非你的服务器 IP)
# 2. 检查 SSL 证书
curl -vI https://fonts.biboer.cn/fonts.json 2>&1 | grep -i "SSL\|issuer"
# 应包含 Cloudflare SSL 证书信息
# 3. 检查缓存状态
curl -I https://fonts.biboer.cn/fonts.json | grep -i "cf-cache-status"
# HIT = 已缓存,MISS = 未缓存(首次访问),DYNAMIC = 未缓存
# 4. 测试 CORS
curl -H "Origin: https://servicewechat.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS -I \
https://fonts.biboer.cn/fonts.json
# 应包含 Access-Control-Allow-Origin: *
D. 小程序开发者工具调试
// 1. 开启调试模式(跳过域名校验)
// 开发者工具 → 详情 → 本地设置
// ✓ 不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书
// 2. 在控制台测试字体加载
wx.request({
url: 'https://fonts.biboer.cn/fonts.json',
success: (res) => console.log('fonts.json 加载成功', res.data),
fail: (err) => console.error('fonts.json 加载失败', err)
})
wx.downloadFile({
url: 'https://fonts.biboer.cn/fonts/其他字体/AlimamaDaoLiTi.ttf',
success: (res) => {
console.log('字体下载成功', res.tempFilePath)
// 检查文件大小
wx.getFileInfo({
filePath: res.tempFilePath,
success: (info) => console.log('文件大小:', info.size, 'bytes')
})
},
fail: (err) => console.error('字体下载失败', err)
})
// 3. 正式发布前必须配置合法域名
// 微信公众平台 → 开发 → 开发管理 → 服务器域名
// downloadFile 合法域名: https://fonts.biboer.cn
E. 常见问题排查
问题 1:字体下载失败 "不在合法域名列表中"
原因:未配置 downloadFile 合法域名或配置未生效
解决:
1. 检查小程序后台是否已添加 https://fonts.biboer.cn
2. 等待 5-10 分钟让配置生效
3. 开发阶段可暂时开启"不校验合法域名"
问题 2:CORS 错误
原因:Nginx 未配置 CORS 头
解决:
1. 确认 Nginx 配置包含 Access-Control-Allow-Origin *
2. 重启 Nginx: sudo systemctl restart nginx
3. 清除 Cloudflare 缓存: Dashboard → 缓存 → 清除所有内容
问题 3:字体文件无法访问(404)
原因:文件路径不正确或权限问题
解决:
1. 检查文件是否存在:
ssh user@fonts.biboer.cn "ls -lh /var/www/font2svg/fonts/"
2. 检查权限:
ssh user@fonts.biboer.cn "chmod -R 755 /var/www/font2svg"
3. 检查 Nginx 配置的 root 路径是否正确
问题 4:Cloudflare 缓存过多导致更新不生效
解决:
1. Cloudflare Dashboard → 缓存 → 配置 → 清除所有内容
2. 或使用 URL 清除特定文件:
https://fonts.biboer.cn/fonts.json
3. 开发阶段可临时关闭缓存:
页面规则 → 缓存级别 → 绕过
问题 5:字体文件中文编码问题
原因:URL 未编码中文字符
解决:
// 在小程序中使用 encodeURIComponent
const fontPath = 'https://fonts.biboer.cn/fonts/' +
encodeURIComponent('其他字体/AlimamaDaoLiTi.ttf')
wx.downloadFile({ url: fontPath })
计划更新日期:2026-02-08
下次更新:M1 完成后,根据实际情况调整后续里程碑细节
部署方案:Cloudflare CDN + 海外服务器(fonts.biboer.cn,免备案)