1239 lines
40 KiB
Markdown
1239 lines
40 KiB
Markdown
# 项目计划(2026-02-08)
|
||
|
||
## 0. 手动切换路由方案(2026-02-10)
|
||
|
||
### 0.1 目标
|
||
|
||
- 支持在**不发布小程序新版本**的情况下,从服务器 A(`fonts.biboer.cn`)切换到服务器 B(`mac.biboer.cn`),或反向切换。
|
||
- 切换仅通过手动修改远端配置文件触发,不引入自动健康探测。
|
||
- 避免 A/B 来回抖动切换。
|
||
|
||
### 0.2 已确认约束
|
||
|
||
- 字体目录统一一份:`/fonts/`(A、B 各自托管同结构字体文件)。
|
||
- 配置文件独立管理:
|
||
- Web:`/fonts.json`(可选 `/default.json`)
|
||
- 小程序:`/miniprogram/assets/fonts.json`、`/miniprogram/assets/default.json`
|
||
- 小程序合法域名已包含 A/B 两个域名。
|
||
- 不使用 `version` 字段。
|
||
|
||
### 0.3 路由配置文件(route-config.json)
|
||
|
||
- 建议路径:`/miniprogram/assets/route-config.json`(A、B 都提供)
|
||
- 建议结构:
|
||
|
||
```json
|
||
{
|
||
"active": "A",
|
||
"cooldownMinutes": 10,
|
||
"servers": {
|
||
"A": { "baseUrl": "https://fonts.biboer.cn" },
|
||
"B": { "baseUrl": "https://mac.biboer.cn" }
|
||
}
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
- `active`: 当前希望启用的目标服务器(`A` 或 `B`)。
|
||
- `cooldownMinutes`: 最短驻留时间。
|
||
- `10` 表示 10 分钟内不允许再次切换。
|
||
- `0` 表示可立即切换。
|
||
|
||
### 0.4 双确认切换规则(核心)
|
||
|
||
当客户端当前连接在 A,且读取到 A 配置 `active=B` 时:
|
||
1. 客户端继续请求 B 的 `route-config.json`。
|
||
2. 只有 B 也返回 `active=B`,才允许切换到 B。
|
||
3. 若 A/B 不一致,则保持当前服务器不变。
|
||
4. 若读取 B 失败(超时、非 200、JSON 非法),保持当前服务器不变,并记录失败日志。
|
||
|
||
反向切换(B -> A)同理执行。
|
||
|
||
### 0.5 A/B 交互与同步机制
|
||
|
||
为满足“双确认”,A/B 需要配置一致性机制(交互):
|
||
- 维护端本地保留单一配置源文件(Git 仓库内)。
|
||
- 通过 `deploy.sh` 一次性下发到 A、B。
|
||
- 采用“临时文件 + 原子替换(mv)”发布,避免客户端读取半文件。
|
||
|
||
推荐切换步骤(A -> B):
|
||
1. 同步配置到 B,确保 B 返回 `active=B`。
|
||
2. 再同步配置到 A,改为 `active=B`。
|
||
3. 客户端双确认通过后完成切换。
|
||
|
||
### 0.6 客户端状态与防抖
|
||
|
||
本地持久化字段:
|
||
- `activeServerKey`(当前服务器)
|
||
- `lastSwitchAt`(最后切换时间戳)
|
||
- `routeConfigCache`(最近一次配置)
|
||
- `lastRouteCheckAt`(最后一次读取 route-config 时间戳)
|
||
|
||
切换判定:
|
||
- 若 `cooldownMinutes > 0` 且 `now - lastSwitchAt < cooldownMinutes * 60 * 1000`,拒绝切换。
|
||
- 若 `cooldownMinutes = 0`,通过双确认即可立即切换。
|
||
|
||
### 0.6.1 route-config.json 读取时机
|
||
|
||
1. 启动读取(P0)
|
||
- 小程序冷启动时优先读取 `route-config.json`,再加载 `fonts.json/default.json` 与 API 请求。
|
||
|
||
2. 回前台读取(P0)
|
||
- `App.onShow` 触发时检查是否超过最小间隔(例如 60 秒),超过则读取。
|
||
|
||
3. 失败兜底读取(P0)
|
||
- 当 API 或配置拉取连续失败时,立即触发一次读取并执行双确认逻辑。
|
||
- 若目标服务器配置读取失败,则仅记录日志并维持当前服务器,不执行切换。
|
||
|
||
4. 手动刷新读取(P1,可选)
|
||
- 提供调试入口(如“刷新配置”按钮)用于即时验证切换。
|
||
|
||
节流规则:
|
||
- 若 `now - lastRouteCheckAt < 60s`,跳过非必要读取,避免频繁请求。
|
||
|
||
### 0.7 实施任务拆分
|
||
|
||
1. 小程序端
|
||
- 新增 `route-manager`(加载路由配置、执行双确认、应用 cooldown、持久化状态)。
|
||
- `render-api`、`font-loader` 改为读取 `route-manager` 当前 `baseUrl`。
|
||
- 启动时先加载路由,再加载 `fonts.json/default.json`。
|
||
|
||
2. 服务端与部署
|
||
- A/B 都部署 `route-config.json`。
|
||
- 使用 `deploy.sh` 统一发布:一次性下发到两台服务器并做原子替换。
|
||
- `deploy.sh` 负责同步 `route-config.json`、字体配置文件,并执行可选巡检。
|
||
- 增加巡检命令:检查 A/B 当前 `active` 是否一致。
|
||
|
||
3. 文档
|
||
- 更新 `miniprogram/README.md`:增加无发版切换流程。
|
||
- 增加运维操作手册:A->B、B->A、回滚流程。
|
||
|
||
### 0.8 验收标准
|
||
|
||
- 仅修改远端 `route-config.json`,小程序无需发版即可切换 A/B。
|
||
- `cooldownMinutes=10` 时,10 分钟内不会再次切换。
|
||
- `cooldownMinutes=0` 时,双确认满足后可立即切换。
|
||
- A/B 配置不一致时不发生切换。
|
||
- 目标服务器配置读取失败(超时/非 200/JSON 非法)时不发生切换。
|
||
- 切换后 API、字体清单、默认配置均来自目标服务器。
|
||
|
||
### 0.9 风险与回滚
|
||
|
||
- 风险:A/B 配置不同步导致无法切换(预期保护行为)。
|
||
- 风险:CDN 缓存导致短时间读取旧配置(通过短缓存 + 原子发布缓解)。
|
||
- 回滚:将 A/B 两端 `active` 同步改回原服务器,并设置 `cooldownMinutes=0` 可快速恢复。
|
||
|
||
## 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)
|
||
|
||
**实施步骤**:
|
||
1. 创建 `miniprogram/` 目录,微信开发者工具初始化原生项目
|
||
2. 配置 `project.config.json`:
|
||
- 设置 `miniprogramRoot`、基础库版本 ≥ 2.9.0
|
||
- 启用 TypeScript 编译插件
|
||
3. 创建 `package.json`,安装依赖:
|
||
```json
|
||
{
|
||
"dependencies": {
|
||
"opentype.js": "^1.3.4"
|
||
},
|
||
"devDependencies": {
|
||
"miniprogram-api-typings": "latest",
|
||
"typescript": "~5.9.3"
|
||
}
|
||
}
|
||
```
|
||
4. 配置 `tsconfig.json`(参考 `frontend/tsconfig.json`)
|
||
5. 创建 `app.ts`、`app.json`、`app.wxss`
|
||
6. 创建首页 `pages/index/`(空白页面)
|
||
7. 微信开发者工具"构建 npm"
|
||
|
||
**验收标准**:
|
||
- 开发者工具可启动并进入首页(显示空白页面)
|
||
- TypeScript 编译无错误
|
||
- npm 包构建成功(`miniprogram_npm/` 生成)
|
||
|
||
### M2:核心算法迁移(P0)
|
||
|
||
**实施步骤**:
|
||
1. **复制纯算法模块**:
|
||
- `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`
|
||
|
||
2. **复制类型定义**:
|
||
- `frontend/src/types/font.d.ts` → `miniprogram/typings/font.d.ts`
|
||
|
||
3. **平台适配层开发**:
|
||
- `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>` 方法
|
||
|
||
4. **编写单元测试**:
|
||
- `miniprogram/utils/core/__tests__/svg-builder.test.ts`
|
||
- 测试用例:单字符、多字符、空字符串、特殊字符
|
||
|
||
**验收标准**:
|
||
- 输入文本 + Font 对象可生成有效 SVG 字符串
|
||
- SVG 结构正确(包含 `<svg>`、`<path>`、`viewBox`)
|
||
- 核心算法单测通过(覆盖率 > 80%)
|
||
|
||
### M3:预览链路(P0)
|
||
|
||
**实施步骤**:
|
||
1. **主页布局**(`pages/index/`):
|
||
- 文本输入区(`<textarea>`)
|
||
- 字体选择器(跳转到 `font-picker` 页面)
|
||
- 参数调节(字号 `<slider>`、颜色 `<picker>`)
|
||
- 预览区(`<image>` 或 `<canvas>`)
|
||
- 导出按钮(底部固定)
|
||
|
||
2. **字体选择页**(`pages/font-picker/`):
|
||
- 搜索框(`<input>` 实时过滤)
|
||
- 字体列表(`<scroll-view>` + 虚拟列表优化)
|
||
- 从云端加载 `fonts.json`
|
||
|
||
3. **预览生成逻辑**:
|
||
- 用户选择字体 → 触发 `loadFontFromUrl()`
|
||
- 调用 `generateSvg()` 生成 SVG 字符串
|
||
- 渲染方案:`<image src="data:image/svg+xml,{{encodedSvg}}">`
|
||
|
||
4. **状态管理**:
|
||
- 使用 `storage-mp` 持久化:`inputText`、`fontSize`、`textColor`、`selectedFontId`
|
||
- 页面 `onLoad()` 恢复状态
|
||
|
||
**验收标准**:
|
||
- 真机可稳定预览(iOS + Android 各 1 款机型)
|
||
- 文本修改后预览更新延迟 < 500ms
|
||
- 切换字体后预览正确渲染
|
||
- 调整字号/颜色后预览实时刷新
|
||
|
||
### M4:导出与分享(P0)
|
||
|
||
**实施步骤**:
|
||
1. **SVG 导出**(`utils/mp/share-mp.ts`):
|
||
- `saveSvgToLocal(svgString, filename)`:
|
||
- 使用 `wx.getFileSystemManager().writeFile()` 写入用户目录
|
||
- 返回文件临时路径
|
||
- `shareFileToChat(filePath)`:
|
||
- 调用 `wx.shareFileMessage()` 分享到微信聊天
|
||
|
||
2. **PNG 导出**(`utils/mp/canvas-export.ts`):
|
||
- `svgToPng(svgString, width, height)`:
|
||
- 创建离屏 Canvas(`wx.createOffscreenCanvas({ type: '2d' })`)
|
||
- SVG → Image → Canvas 渲染
|
||
- 使用 `canvas.toDataURL()` 或 `wx.canvasToTempFilePath()` 导出
|
||
- `savePngToPhotos(pngPath)`:
|
||
- 调用 `wx.saveImageToPhotosAlbum()`
|
||
- 处理授权拒绝(引导用户手动开启)
|
||
|
||
3. **错误处理**:
|
||
- 文件写入失败提示
|
||
- 授权拒绝引导(显示设置路径)
|
||
- 分享取消提示
|
||
|
||
**验收标准**:
|
||
- SVG 导出成功率 > 95%(真机测试 20 次)
|
||
- PNG 导出图片清晰度正常(无模糊、颜色偏差)
|
||
- 授权拒绝后提示明确,可引导用户设置
|
||
- 文件分享到微信聊天后可正常打开
|
||
|
||
### M5:性能与稳定性(P1)
|
||
|
||
**实施步骤**:
|
||
1. **Worker 化字形生成**:
|
||
- 创建 `workers/svg-generator/index.ts`
|
||
- 主线程 → Worker 消息协议:
|
||
```typescript
|
||
type GenerateRequest = {
|
||
id: string
|
||
fontBuffer: ArrayBuffer
|
||
text: string
|
||
options: SvgGenerateOptions
|
||
}
|
||
```
|
||
- Worker 内加载 opentype.js,调用 `generateSvg()`,返回结果
|
||
- 创建 `utils/worker-manager.ts` 管理 Worker 生命周期
|
||
|
||
2. **字体缓存策略**:
|
||
- 全局缓存已解析的 Font 对象(Map<fontId, Font>)
|
||
- LRU 淘汰:最多缓存 20 个字体
|
||
- 页面 `onUnload` 释放 Worker 资源
|
||
|
||
3. **性能优化**:
|
||
- 限制并发 Worker 任务数(最多 3 个)
|
||
- 超时任务自动取消(5 秒)
|
||
- 长文本分段处理(每段 100 字)
|
||
|
||
4. **真机回归测试**:
|
||
- 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 静态文件服务配置**:
|
||
|
||
```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;
|
||
}
|
||
}
|
||
```
|
||
|
||
**部署命令**:
|
||
|
||
```bash
|
||
# 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**
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```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 小程序端加载代码
|
||
|
||
```typescript
|
||
// 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`):
|
||
```typescript
|
||
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(微信小程序标准尺寸)
|
||
|
||
**页面结构**(单页垂直布局):
|
||
1. **顶部工具栏** (410x48px) - Logo + 品牌 + 字号滑块 + 颜色选择
|
||
2. **输入和导出栏** (410x38.78px) - 文本输入框 + 导出按钮组
|
||
3. **预览区** (410x649.21px) - 可滚动的多字体预览卡片列表
|
||
4. **底部选择区** (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`)
|
||
|
||
```css
|
||
/* 颜色 */
|
||
--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
|
||
```
|
||
|
||
2. **初始化配置文件**:
|
||
- 创建 `miniprogram/package.json` 并安装依赖
|
||
- 配置 `miniprogram/tsconfig.json`
|
||
- 配置 `miniprogram/project.config.json`
|
||
|
||
### 13.2 设计稿已确认(可立即实施)
|
||
|
||
**Figma 设计稿关键信息已提取**:
|
||
- ✅ 单页垂直布局(426×956px)
|
||
- ✅ 主色调:紫色 #9B6BC2(品牌色 TextToSVG)
|
||
- ✅ 完整颜色方案和尺寸规范
|
||
- ✅ 交互规范:支持多字体对比预览、树形字体选择、收藏功能
|
||
- ✅ 组件规范:滑块、复选框、预览卡片、字体树
|
||
|
||
**设计稿特色功能**:
|
||
1. **品牌 Logo + 名称**:顶部固定,紫色渐变 Logo
|
||
2. **字号滑块**:实时调节预览大小(减号/滑块/加号)
|
||
3. **颜色选择器**:色轮图标,调节文本颜色
|
||
4. **多字体预览**:可同时预览多个字体效果
|
||
5. **字体树**:分类折叠展开,支持收藏星标
|
||
6. **导出按钮组**:SVG + PNG 双格式导出
|
||
|
||
**下一步:可直接开始 M1 和 M3 实施**
|
||
|
||
### 13.3 并行推进(可同步)
|
||
在等待设计稿期间,可以同步进行:
|
||
- **M2 核心算法迁移**:复用 Web 端代码,平台 API 适配
|
||
- **字体资源云存储准备**:
|
||
- 开通微信云开发环境
|
||
- 上传字体文件到云存储
|
||
- 生成 `fonts.json` 云端配置
|
||
|
||
### 13.4 优先级建议
|
||
1. **P0(必须完成)**:M1 → M2 → M3 → M4
|
||
2. **P1(性能优化)**:M5(真机测试后评估是否需要 Worker)
|
||
3. **P2(体验增强)**:收藏功能、搜索历史、多字体对比
|
||
|
||
---
|
||
|
||
## 附录:快速启动命令
|
||
|
||
### A. 开发环境启动
|
||
|
||
```bash
|
||
# 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 + 海外服务器)
|
||
|
||
```bash
|
||
# 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 配置验证
|
||
|
||
```bash
|
||
# 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. 小程序开发者工具调试
|
||
|
||
```javascript
|
||
// 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,免备案)
|