feat(canvas): 优化图片和视频节点尺寸适配

- 在 InsertAssetPayload 类型中为视频添加 width 和 height 属性
- 引入 fitNodeSize 和 nodeSizeFromRatio 工具函数处理节点尺寸计算
- 设置视频节点最大宽度和高度限制为 420px
- 将原有的 fitImageNodeSize 替换为通用的 fitNodeSize 函数
- 为视频节点添加自然宽高元数据存储
- 实现视频文件上传时读取真实宽高信息
- 支持空节点在尺寸比例切换时自动调整外框大小
- 修复视频节点在生成和重试过程中的尺寸适配问题
- 统一图片和视频节点的尺寸调整逻辑
- 添加视频节点纵横比锁定功能
This commit is contained in:
HouYunFei
2026-05-23 18:30:32 +08:00
parent ef7772a703
commit f871a5d015
6 changed files with 66 additions and 34 deletions
+13 -2
View File
@@ -3,7 +3,7 @@
import localforage from "localforage";
import { nanoid } from "nanoid";
export type UploadedFile = { url: string; storageKey: string; bytes: number; mimeType: string };
export type UploadedFile = { url: string; storageKey: string; bytes: number; mimeType: string; width?: number; height?: number };
const store = localforage.createInstance({ name: "infinite-canvas", storeName: "media_files" });
const objectUrls = new Map<string, string>();
@@ -14,7 +14,8 @@ export async function uploadMediaFile(input: string | Blob, prefix = "file"): Pr
await store.setItem(storageKey, blob);
const url = URL.createObjectURL(blob);
objectUrls.set(storageKey, url);
return { url, storageKey, bytes: blob.size, mimeType: blob.type || "application/octet-stream" };
const meta = blob.type.startsWith("video/") ? await readVideoMeta(url) : {};
return { url, storageKey, bytes: blob.size, mimeType: blob.type || "application/octet-stream", ...meta };
}
export async function resolveMediaUrl(storageKey?: string, fallback = "") {
@@ -43,3 +44,13 @@ export function collectMediaStorageKeys(value: unknown, keys = new Set<string>()
Object.values(value).forEach((item) => (Array.isArray(item) ? item.forEach((child) => collectMediaStorageKeys(child, keys)) : collectMediaStorageKeys(item, keys)));
return keys;
}
function readVideoMeta(url: string) {
return new Promise<{ width: number; height: number }>((resolve) => {
const video = document.createElement("video");
const done = () => resolve({ width: video.videoWidth || 1280, height: video.videoHeight || 720 });
video.onloadedmetadata = done;
video.onerror = done;
video.src = url;
});
}