# 海报生成方案 - Fabric.js 前端渲染 ## 背景 当前方案在 Python 端下载图片并渲染海报,存在以下问题: - 图片下载耗时长 - 输出静态 PNG,用户无法编辑 - 服务端渲染压力大 ## 新方案概述 **Python 端只生成文案和布局参数,输出 fabric.js JSON,前端负责渲染和合成。** ``` ┌─────────────┐ JSON ┌─────────────┐ Canvas ┌─────────────┐ │ Python AI │ ─────────▶ │ Frontend │ ──────────▶ │ 用户编辑 │ │ 文案生成 │ │ fabric.js │ │ 导出图片 │ └─────────────┘ └─────────────┘ └─────────────┘ ``` ## 数据流 ### 1. 输入 (用户请求) ```json { "category": "民宿", "name": "山舍云端民宿", "description": "藏在莫干山的治愈系民宿", "price": "458元/晚", "location": "莫干山", "features": "独立庭院, 手冲咖啡, 山景露台", "image_url": "https://example.com/image.jpg" } ``` ### 2. Python 输出 (Fabric.js JSON) ```json { "version": "1.0.0", "canvas": { "width": 1080, "height": 1440, "backgroundColor": "#FFF5F0" }, "layout": "hero_bottom", "theme": "sunset", "content": { "title": "去了莫干山才知道...", "subtitle": "藏在山里的治愈系民宿,推开窗便是云海", "highlights": ["独立庭院", "手冲咖啡", "山景露台"], "details": ["管家式服务超贴心", "有机早餐太惊艳"], "price": "¥458", "price_suffix": "/晚", "tags": ["莫干山民宿", "周末度假"] }, "objects": [ { "id": "background_image", "type": "image", "src": "https://example.com/image.jpg", "left": 0, "top": 0, "width": 1080, "height": 1440, "scaleX": 1, "scaleY": 1 }, { "id": "gradient_overlay", "type": "rect", "left": 0, "top": 800, "width": 1080, "height": 640, "fill": { "type": "linear", "coords": {"x1": 0, "y1": 0, "x2": 0, "y2": 640}, "colorStops": [ {"offset": 0, "color": "rgba(89,66,52,0)"}, {"offset": 0.5, "color": "rgba(89,66,52,0.8)"}, {"offset": 1, "color": "rgba(89,66,52,1)"} ] } }, { "id": "title", "type": "textbox", "text": "去了莫干山才知道...", "left": 48, "top": 880, "width": 984, "fontSize": 80, "fontFamily": "PingFang SC", "fontWeight": "bold", "fill": "#FFFFFF", "shadow": "rgba(0,0,0,0.3) 2px 2px 4px" }, { "id": "subtitle", "type": "textbox", "text": "藏在山里的治愈系民宿,推开窗便是云海", "left": 48, "top": 980, "width": 984, "fontSize": 32, "fontFamily": "PingFang SC", "fill": "rgba(255,255,255,0.85)" }, { "id": "price_bg", "type": "rect", "left": 36, "top": 1280, "width": 180, "height": 60, "rx": 12, "ry": 12, "fill": "rgba(255,255,255,0.2)" }, { "id": "price", "type": "text", "text": "¥458", "left": 48, "top": 1290, "fontSize": 48, "fontFamily": "PingFang SC", "fontWeight": "bold", "fill": "#FFFFFF" }, { "id": "price_suffix", "type": "text", "text": "/晚", "left": 160, "top": 1308, "fontSize": 24, "fontFamily": "PingFang SC", "fill": "rgba(255,255,255,0.8)" } ] } ``` ## 架构设计 ### Python 端 ``` domain/aigc/engines/ ├── poster_smart_v1.py # 当前:AI文案 + Python渲染 └── poster_smart_v2.py # 新增:AI文案 + Fabric JSON输出 ``` #### PosterSmartEngineV2 职责 1. **AI 文案生成** - 调用 LLM 生成标题、副标题、亮点等 2. **布局推荐** - 根据内容类型推荐布局 3. **主题推荐** - 根据内容类型推荐配色 4. **Fabric JSON 生成** - 输出标准 fabric.js 格式 ```python class PosterSmartEngineV2(BaseEngine): """智能海报引擎 V2 - 输出 Fabric.js JSON""" async def execute(self, params: dict) -> EngineResult: # 1. AI 生成文案 content = await self._generate_content(params) # 2. 选择布局和主题 layout = content.get('suggested_layout') or self._recommend_layout(params) theme = content.get('suggested_theme') or self._recommend_theme(params) # 3. 生成 Fabric JSON fabric_json = self._generate_fabric_json(content, layout, theme, params) return EngineResult( success=True, data={ 'fabric_json': fabric_json, 'layout': layout, 'theme': theme, 'content': content } ) ``` ### 前端 ``` components/ ├── PosterEditor/ │ ├── index.tsx # 主组件 │ ├── FabricCanvas.tsx # fabric.js 画布 │ ├── LayerPanel.tsx # 图层面板 │ ├── PropertyPanel.tsx # 属性面板 │ └── ExportButton.tsx # 导出按钮 ``` #### 前端职责 1. **加载 Fabric JSON** - 解析 Python 返回的 JSON 2. **渲染画布** - fabric.js 渲染所有对象 3. **用户编辑** - 支持移动、缩放、修改文字 4. **导出图片** - 导出 PNG/JPG ## 布局模板 ### 5 种布局的 Fabric 结构 | 布局 | 结构 | |-----|------| | **hero_bottom** | 图片全屏 + 底部渐变 + 文字叠加 | | **overlay_center** | 图片全屏 + 暗化遮罩 + 居中文字 | | **overlay_bottom** | 图片上半 + 毛玻璃卡片底部 | | **split_vertical** | 左侧渐变/图片 + 右侧文字 | | **card_float** | 图片全屏 + 悬浮白色卡片 | ### 布局模板文件 ``` poster_v2/templates/ ├── hero_bottom.json ├── overlay_center.json ├── overlay_bottom.json ├── split_vertical.json └── card_float.json ``` ## 主题配置 ```python THEMES = { "sunset": { "primary": "#594234", "secondary": "#FFF5F0", "accent": "#D4A574", "text": "#FFFFFF", "text_dark": "#333333", "gradient": ["#FFE4D6", "#D4A574"] }, # ... 其他主题 } ``` ## API 设计 ### 请求 ```http POST /api/v2/aigc/execute Content-Type: application/json { "engine": "poster_smart_v2", "params": { "category": "民宿", "name": "山舍云端民宿", "description": "藏在莫干山的治愈系民宿", "price": "458元/晚", "image_url": "https://example.com/image.jpg" } } ``` ### 响应 (双输出) ```json { "success": true, "data": { "preview_base64": "data:image/png;base64,...", "fabric_json": { "version": "1.0.0", "canvas": { "width": 1080, "height": 1440 }, "objects": [...] }, "layout": "hero_bottom", "theme": "sunset", "content": { "title": "去了莫干山才知道...", "subtitle": "...", "highlights": [...], "price": "¥458" } } } ``` ### 输出说明 | 字段 | 说明 | |-----|------| | `preview_base64` | **预览 PNG** - 无底图,使用主题渐变色背景,快速预览文案排版 | | `fabric_json` | **Fabric JSON** - 完整的编辑数据,含图片占位,前端加载后可编辑 | | `layout` | 推荐布局 | | `theme` | 推荐主题 | | `content` | AI 生成的文案内容 | ## 实现步骤 ### Phase 1: Python 端 (2-3天) 1. [ ] 创建 `poster_smart_v2.py` 引擎 2. [ ] 实现 5 种布局的 Fabric JSON 生成器 3. [ ] 复用现有的 AI 文案生成逻辑 4. [ ] 添加 API 路由 ### Phase 2: 前端 (3-5天) 1. [ ] 集成 fabric.js 2. [ ] 实现 FabricCanvas 组件 3. [ ] 实现图层面板 4. [ ] 实现属性编辑 5. [ ] 实现导出功能 ### Phase 3: 优化 (1-2天) 1. [ ] 字体加载优化 2. [ ] 图片懒加载 3. [ ] 撤销/重做 4. [ ] 模板保存 ## 兼容性 - 保留 `poster_smart_v1` 引擎,支持服务端渲染 - 新增 `poster_smart_v2` 引擎,支持 Fabric JSON 输出 - 前端根据需求选择调用哪个引擎 ## 优势 | 方面 | 收益 | |-----|------| | **性能** | 服务端不再处理图片,响应更快 | | **可编辑** | 用户可自由调整文字、位置、颜色 | | **灵活性** | 前端可扩展更多编辑功能 | | **复用性** | Fabric JSON 可保存为模板复用 | ## 风险与对策 | 风险 | 对策 | |-----|------| | 字体不一致 | 前端加载相同字体文件 | | 跨域图片 | 配置 CORS 或使用代理 | | 复杂效果 | 渐变、阴影用 fabric.js 内置功能 |