Files
aida_front/src/component/Canvas/CanvasEditor/managers/ThumbnailManager.js

273 lines
8.4 KiB
JavaScript
Raw Normal View History

2025-06-22 13:52:28 +08:00
import { findObjectById } from "../utils/helper";
import { findLayerRecursively } from "../utils/layerHelper";
import { restoreFabricObject } from "../utils/objectHelper";
2025-06-22 13:52:28 +08:00
import { createRasterizedImage } from "../utils/rasterizedImage";
2025-06-09 10:25:54 +08:00
/**
* 缩略图管理器 - 负责生成和缓存图层和元素的预览缩略图
*/
export class ThumbnailManager {
constructor(canvas, options = {}) {
this.canvas = canvas;
this.layers = options.layers || []; // 图层管理器
this.layerThumbSize = options.layerThumbSize || { width: 48, height: 48 };
2025-06-22 13:52:28 +08:00
// this.layerThumbnails = new Map(); // 图层缩略图缓存 - 改成使用图层对象的thumbnailUrl属性
// 使用图层对象的thumbnailUrl属性来存储缩略图URL
2025-06-09 10:25:54 +08:00
this.defaultThumbnail =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="; // 1x1 透明图
}
/**
* 生成图层缩略图
* @param {Object} layer 图层对象ID
*/
2025-06-22 13:52:28 +08:00
async generateLayerThumbnail(layerId) {
const fabricObjects = this._collectLayersAndObjects(layerId);
const { layer } = findLayerRecursively(this.layers.value, layerId);
2025-06-09 10:25:54 +08:00
if (!fabricObjects || fabricObjects.length === 0) {
2026-01-19 16:57:11 +08:00
// console.warn("⚠️ 无法生成缩略图:没有可栅格化的对象 返回空缩略图");
// 如果没有对象,返回默认缩略图
if (layer) {
layer.thumbnailUrl = this.defaultThumbnail; // 更新图层对象的缩略图
}
return this.defaultThumbnail;
}
2025-06-22 13:52:28 +08:00
// 延迟执行避免阻塞UI
fabricObjects.length > 0 &&
2025-06-29 23:29:47 +08:00
requestIdleCallback(() => {
setTimeout(async () => {
const base64 = await this._generateLayerThumbnailNow(fabricObjects, layer);
// this.layerThumbnails.set(layerId, base64);
2025-06-29 23:29:47 +08:00
try {
const { layer, parent } = findLayerRecursively(this.layers.value, layerId);
2025-06-29 23:29:47 +08:00
if (layer) {
layer.thumbnailUrl = base64; // 更新图层对象的缩略图
}
2025-06-09 10:25:54 +08:00
2025-06-29 23:29:47 +08:00
if (parent) {
// 如果是组图层,则同步更新父图层的缩略图
this.generateLayerThumbnail(parent.id);
}
} catch (error) {
console.error("生成图层缩略图时出错:", error);
2025-06-22 13:52:28 +08:00
}
2025-06-29 23:29:47 +08:00
});
2025-06-22 13:52:28 +08:00
});
2025-06-09 10:25:54 +08:00
}
/**
2025-06-22 13:52:28 +08:00
* 批量生成图层缩略图
* @param {Array} layers 图层数组
2025-06-09 10:25:54 +08:00
*/
2025-06-22 13:52:28 +08:00
generateAllLayerThumbnails(layers) {
if (!layers || !Array.isArray(layers)) return;
2025-06-09 10:25:54 +08:00
2025-06-29 23:29:47 +08:00
requestIdleCallback(() => {
setTimeout(() => {
layers.forEach((layer) => {
if (layer && layer.id) {
this.generateLayerThumbnail(layer.id);
if (layer.children && layer.children.length) {
this.generateLayerThumbnail(layer.id);
}
}
});
});
2025-06-09 10:25:54 +08:00
});
}
2025-06-22 13:52:28 +08:00
// 生成图片
async _generateLayerThumbnailNow(fabricObjects, layer) {
2025-06-22 13:52:28 +08:00
if (!fabricObjects || fabricObjects.length === 0) {
console.warn("⚠️ 没有对象需要生成缩略图,返回默认缩略图");
return this.defaultThumbnail;
2025-06-09 10:25:54 +08:00
}
2025-06-29 23:29:47 +08:00
try {
let clippingMaskFabricObject = null;
if (layer?.clippingMask) {
// 重新创建遮罩对象
clippingMaskFabricObject = await restoreFabricObject(layer?.clippingMask, this.canvas);
2026-01-02 11:24:11 +08:00
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
absolutePositioned: true,
});
2026-01-02 11:24:11 +08:00
// clippingMaskFabricObject.dirty = true;
clippingMaskFabricObject.setCoords();
}
2025-06-29 23:29:47 +08:00
return await createRasterizedImage({
canvas: this.canvas, // 画布对象 必填
fabricObjects, // 要栅格化的对象列表 - 按顺序 必填
maskObject: clippingMaskFabricObject || null, // 用于裁剪的对象 - 可选
2025-06-29 23:29:47 +08:00
trimWhitespace: true, // 是否裁剪空白区域
trimPadding: 2, // 裁剪边距
quality: 0.2, // 图像质量
format: "png", // 图像格式
scaleFactor: 0.2, // 高清倍数 - 默认是画布的高清倍数
isReturenDataURL: true, // 是否返回DataURL而不是fabric.Image对象
isThumbnail: true, // 为缩略图
});
} catch (error) {
return this.defaultThumbnail; // 出错时返回默认缩略图
}
2025-06-09 10:25:54 +08:00
}
/**
2025-06-22 13:52:28 +08:00
* 收集要栅格化的图层和对象
2025-06-09 10:25:54 +08:00
* @private
*/
2025-06-22 13:52:28 +08:00
_collectLayersAndObjects(layerId) {
if (!layerId) {
2026-01-19 16:57:11 +08:00
// console.warn("⚠️ 无效的图层ID无法收集对象");
2025-06-22 13:52:28 +08:00
return [];
}
2025-06-09 10:25:54 +08:00
2025-06-22 13:52:28 +08:00
const { layer } = findLayerRecursively(this.layers.value, layerId);
2025-06-09 10:25:54 +08:00
2026-01-02 11:24:11 +08:00
if (!layer) {
console.warn("⚠️ 无效的图层,无法收集对象");
return [];
}
let layersToRasterize = [];
2025-06-22 13:52:28 +08:00
if (layer.children && layer.children.length > 0) {
// 组图层:收集自身和所有子图层
layersToRasterize = this._collectLayersToRasterize(layer);
} else {
// 普通图层:只收集自身
layersToRasterize = [layer];
}
2025-06-09 10:25:54 +08:00
2025-06-22 13:52:28 +08:00
// 收集所有图层的fabricObjects并按画布z-index顺序排序
const allCanvasObjects = this.canvas.getObjects();
const objectsWithZIndex = [];
layersToRasterize.forEach((layer) => {
// if (layer.fill) {
// const { object } = findObjectById(this.canvas, layer.fill.id);
// if (object) {
// // 获取对象在画布中的z-index数组索引
// const zIndex = allCanvasObjects.indexOf(object);
// objectsWithZIndex.push({
// object: object,
// zIndex: zIndex,
// layerObj: layer.fill,
// });
// }
// }
2025-06-22 13:52:28 +08:00
if (layer.fabricObject) {
// 如果图层本身有fabricObject直接添加
const { object } = findObjectById(this.canvas, layer.fabricObject.id);
if (object) {
const zIndex = allCanvasObjects.indexOf(object);
objectsWithZIndex.push({
object: object,
zIndex: zIndex,
layerObj: layer.fabricObject,
});
2025-06-09 10:25:54 +08:00
}
2025-06-22 13:52:28 +08:00
}
2025-06-09 10:25:54 +08:00
2025-06-22 13:52:28 +08:00
if (layer.fabricObjects && layer.fabricObjects.length > 0) {
layer.fabricObjects.forEach((layerObj) => {
if (layerObj && layerObj.id) {
const { object } = findObjectById(this.canvas, layerObj.id);
if (object) {
// 获取对象在画布中的z-index数组索引
const zIndex = allCanvasObjects.indexOf(object);
objectsWithZIndex.push({
object: object,
zIndex: zIndex,
layerObj: layerObj,
});
}
}
});
}
});
2025-06-09 10:25:54 +08:00
2025-06-22 13:52:28 +08:00
// 按z-index排序确保保持原有的渲染顺序
objectsWithZIndex.sort((a, b) => a.zIndex - b.zIndex);
// 提取排序后的对象
const objectsToRasterize = objectsWithZIndex.map((item) => item.object);
2026-01-19 16:57:11 +08:00
// console.log(
// `📊 收集到 ${layersToRasterize.length} 个图层,${objectsToRasterize.length} 个对象进行栅格化`
// );
// console.log(
// "🔢 对象z-index顺序:",
// objectsWithZIndex.map((item) => ({
// id: item.object.id,
// type: item.object.type,
// zIndex: item.zIndex,
// }))
// );
2025-06-22 13:52:28 +08:00
return objectsToRasterize;
2025-06-09 10:25:54 +08:00
}
/**
2025-06-22 13:52:28 +08:00
* 收集要栅格化的图层递归收集子图层
* @param {Object} sourceLayer 源图层
* @returns {Array} 图层数组
* @private
2025-06-09 10:25:54 +08:00
*/
2025-06-22 13:52:28 +08:00
_collectLayersToRasterize(sourceLayer) {
const result = [sourceLayer];
// 如果是组图层,收集所有子图层
if (sourceLayer.children && sourceLayer.children.length > 0) {
sourceLayer.children.forEach((childLayer) => {
if (childLayer) {
result.push(...this._collectLayersToRasterize(childLayer));
2025-06-09 10:25:54 +08:00
}
});
2025-06-22 13:52:28 +08:00
}
return result;
2025-06-09 10:25:54 +08:00
}
/**
* 获取图层缩略图
* @param {String} layerId 图层ID
* @returns {String|null} 缩略图URL或null
*/
getLayerThumbnail(layerId) {
if (!layerId) return null;
// return this.layerThumbnails.get(layerId) || null;
2025-06-09 10:25:54 +08:00
}
/**
* 清除图层缩略图
* @param {String} layerId 图层ID
*/
clearLayerThumbnail(layerId) {
if (layerId && this.layerThumbnails.has(layerId)) {
// this.layerThumbnails.delete(layerId);
2025-06-09 10:25:54 +08:00
}
}
/**
* 清除所有缩略图
*/
clearAllThumbnails() {
// this.layerThumbnails.clear();
2025-06-09 10:25:54 +08:00
}
/**
* 释放资源
*/
dispose() {
this.clearAllThumbnails();
this.canvas = null;
}
}