8.7 KiB
多角度编辑器技术实现记录
背景
本功能在 TEST-s 分支中为图片输入链路增加“图片多角度”生成能力。当前入口已从底部固定生成控制台迁移到画布连接菜单:用户创建图片输入节点并上传图片后,从图片输出端拖拽打开菜单,在“图片对比”下方点击“图片多角度”,弹出多角度编辑器,配置视角后生成新的图片生成节点。
核心文件:
src/components/MultiAngleEditor.tsxsrc/components/ConnectionDropMenu.tsxsrc/components/WorkflowCanvas.tsxsrc/components/composer/GenerationComposer.tsxsrc/i18n/index.tsxsrc/components/__tests__/MultiAngleEditor.test.tsxsrc/components/__tests__/ConnectionDropMenu.test.tsxsrc/components/__tests__/WorkflowCanvas.test.tsxsrc/components/__tests__/GenerationComposer.test.tsx
当前功能范围
已实现:
- 从底部控制台移除“多角度”入口。
- 在图片输出拖拽菜单中新增“图片多角度”动作,位置位于“图片对比”下方。
- 点击“图片多角度”后打开独立浮层编辑器。
- 编辑器支持自定义、多种预设、地球仪视角仪表、摄像头视线、图片预览、提示词开关。
- 点击“生成多角度”后创建新的
nanoBanana图片生成节点。 - 新节点会写入原图片作为
inputImages,并写入非空多角度提示词。 - 新节点会和源图片节点建立图片连线,并立即调用
regenerateNode(newNodeId)。 - 多角度提示词不包含外部平台名称。
未实现:
- 真正 3D 场景渲染。当前地球仪是 SVG 视角仪表。
- 批量多角度生成。
- 将多角度参数持久化到源图片节点数据。
- 针对不同模型或服务商的提示词适配层。
入口迁移
旧实现把“多角度”作为底部控制台的 chip:
风格 / 标记 / 聚焦 / 多角度
本次调整后,底部控制台只保留常规生成和参考图能力,多角度不再在 GenerationComposer 中渲染。新的入口在 ConnectionDropMenu 的图片目标菜单中:
export type MenuAction = "splitGridImmediate" | "multiAngleImage";
IMAGE_TARGET_OPTIONS 中新增动作项,放在 imageCompare 后:
{
type: "multiAngleImage",
label: "Image Multi-Angle",
isAction: true,
icon: ...
}
中文文案由 src/i18n/index.tsx 提供:
connectionMenu.multiAngleImage = 图片多角度
状态模型
多角度配置集中在 src/components/MultiAngleEditor.tsx:
export interface MultiAngleSettings {
preset: MultiAnglePresetId;
horizontal: number;
vertical: MultiAngleVertical;
zoom: MultiAngleZoom;
includePrompt: boolean;
promptText: string;
}
字段含义:
preset:当前预设 ID。horizontal:水平环绕角度,范围0-315。vertical:垂直俯仰,可选值-30 / 0 / 30 / 60。zoom:景别缩放,0=全景,1=中景,2=特写。includePrompt:是否启用额外提示词输入框。promptText:用户输入或预设写入的额外提示词。
默认值:
export const DEFAULT_MULTI_ANGLE_SETTINGS = {
preset: "custom",
horizontal: 0,
vertical: -30,
zoom: 1,
includePrompt: false,
promptText: "",
};
预设配置
预设由 MULTI_ANGLE_PRESET_SETTINGS 统一维护。
| 预设 | horizontal | vertical | zoom | includePrompt | promptText |
|---|---|---|---|---|---|
| 自定义 | 0 | -30 | 中景 | false | 空 |
| 鱼眼视角 | 0 | 30 | 特写 | true | 超近距离鱼眼镜头,强烈超广角透视,圆形画面边界与黑色暗角,明显桶形畸变,画面中心主体放大,边缘向外弯曲拉伸 |
| 倾斜视角 | 45 | -30 | 中景 | true | dutch angle, tilted frame |
| 正面俯拍 | 0 | 60 | 中景 | false | 空 |
| 正面仰拍 | 0 | -30 | 中景 | false | 空 |
| 全景俯拍 | 45 | 30 | 全景 | false | 空 |
| 背面视角 | 180 | 0 | 中景 | false | 空 |
点击预设会替换当前 MultiAngleSettings,并同步右侧参数、左侧地球仪、摄像头位置和提示词状态。
视角仪表
MultiAngleEditor 内部用 SVG 实现地球仪式视角仪表。
主要元素:
- 地球仪网格:
circle、多组ellipse和中心基准线。 - 图片预览:始终在中心,仅受景别缩放影响。
- 摄像头:独立高层级 SVG,避免被图片遮挡。
- 视线:从摄像头连到图片中心。
水平环绕:
const horizontalRadians = (settings.horizontal * Math.PI) / 180;
const cameraX = 100 + Math.sin(horizontalRadians) * 78;
垂直俯仰:
const cameraY = 100 - (settings.vertical / 60) * 52;
const globePitchRotation = -(settings.vertical / 60) * 16;
const globePitchScaleY = 1 - Math.abs(settings.vertical) / 260;
图片本体不随垂直俯仰上下移动,只有摄像头和地球仪网格随参数变化。
鼠标交互
左侧地球仪区域支持拖拽:
- 鼠标相对中心的角度换算为水平环绕。
- 鼠标垂直位置吸附到
-30 / 0 / 30 / 60。 - 拖拽更新
MultiAngleSettings。 - 右侧滑块调整时,左侧摄像头和地球仪同步更新。
地球仪区域使用:
data-composer-local-pointer="true"
同时在事件回调中 stopPropagation(),避免拖拽被 React Flow 画布平移或选区逻辑截获。
提示词策略
多角度提示词由 buildMultiAnglePrompt(settings) 生成。
模板:
【多角度】基于输入图片生成,保持同一主体和画风。使用{水平描述},{垂直描述},景别为{景别}{可选额外提示词},构图稳定、画面自然。【/多角度】
水平提示词:
0°:
水平环绕 0°,相机正对主体
非 0°:
水平环绕 {angle}°,按目标视角等效为主体向右转 {360 - angle}°,不要生成反向视角
垂直提示词:
vertical > 0:
垂直俯仰 {vertical}°,正面俯拍视角,相机从上方向下看主体
vertical < 0:
垂直俯仰 {vertical}°,正面仰拍视角,相机从下方向上看主体
即使 includePrompt=false,生成时也会发送基础多角度提示词,避免图片生成接口因空文本报 Missing text input。
运行逻辑
WorkflowCanvas 维护浮层状态:
interface MultiAngleDialogState {
sourceNodeId: string;
sourceHandleId: string;
flowPosition: { x: number; y: number };
settings: MultiAngleSettings;
}
点击菜单动作时:
- 读取当前连接拖拽状态。
- 确认来源是图片输出。
- 写入
multiAngleDialog状态。 - 关闭连接菜单。
点击“生成多角度”时:
- 通过
getImageFromNode(sourceNodeId)读取源图。 - 创建新的
nanoBanana节点。 - 写入:
inputImages: [sourceImage]inputPrompt: buildMultiAnglePrompt(settings)- 默认图像模型和基础生成参数
- 创建从源图片节点到新生成节点的图片连线。
- 选中新节点。
- 关闭多角度浮层。
- 调用
regenerateNode(newNodeId)。
相关实现函数:
function buildMultiAngleNodeData(
sourceImage: string,
settings: MultiAngleSettings
): Partial<NanoBananaNodeData>
测试覆盖
当前相关测试:
src/components/__tests__/MultiAngleEditor.test.tsx
src/components/__tests__/ConnectionDropMenu.test.tsx
src/components/__tests__/WorkflowCanvas.test.tsx
src/components/__tests__/GenerationComposer.test.tsx
覆盖点:
- 多角度编辑器默认参数。
- 鱼眼预设参数和固定提示词。
- 地球仪拖拽和右侧参数同步。
- 多角度提示词不包含外部平台名称。
- 图片拖拽菜单包含“图片多角度”动作。
- “图片多角度”动作为
isAction=true,不会被当作普通节点创建。 - 画布菜单动作打开多角度浮层。
- 点击生成后创建并连接新的图片生成节点。
- 底部控制台不再渲染“多角度”入口。
建议验证命令:
npm run test:run -- src/components/__tests__/MultiAngleEditor.test.tsx src/components/__tests__/ConnectionDropMenu.test.tsx src/components/__tests__/WorkflowCanvas.test.tsx src/components/__tests__/GenerationComposer.test.tsx
维护注意点
- 新增多角度预设时,扩展
MultiAnglePresetId、MULTI_ANGLE_PRESETS、MULTI_ANGLE_PRESET_SETTINGS和对应测试。 - 修改提示词语义时,优先调整
buildMultiAnglePrompt()内部的水平/垂直描述函数。 - 不要把多角度入口重新放回
GenerationComposer,当前产品入口是图片输出拖拽菜单。 - 如果未来要做真实 3D 视角,应替换
MultiAngleEditor内部 SVG 仪表,但保留外部MultiAngleSettings合约。 .env.local和服务商 API key 与本功能无关,本次实现没有改动任何服务商配置。