Files
aida_front/src/component/Canvas/CanvasEditor/managers/ThumbnailManager.js
2026-01-19 16:57:11 +08:00

273 lines
8.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { findObjectById } from "../utils/helper";
import { findLayerRecursively } from "../utils/layerHelper";
import { restoreFabricObject } from "../utils/objectHelper";
import { createRasterizedImage } from "../utils/rasterizedImage";
/**
* 缩略图管理器 - 负责生成和缓存图层和元素的预览缩略图
*/
export class ThumbnailManager {
constructor(canvas, options = {}) {
this.canvas = canvas;
this.layers = options.layers || []; // 图层管理器
this.layerThumbSize = options.layerThumbSize || { width: 48, height: 48 };
// this.layerThumbnails = new Map(); // 图层缩略图缓存 - 改成使用图层对象的thumbnailUrl属性
// 使用图层对象的thumbnailUrl属性来存储缩略图URL
this.defaultThumbnail =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="; // 1x1 透明图
}
/**
* 生成图层缩略图
* @param {Object} layer 图层对象ID
*/
async generateLayerThumbnail(layerId) {
const fabricObjects = this._collectLayersAndObjects(layerId);
const { layer } = findLayerRecursively(this.layers.value, layerId);
if (!fabricObjects || fabricObjects.length === 0) {
// console.warn("⚠️ 无法生成缩略图:没有可栅格化的对象 返回空缩略图");
// 如果没有对象,返回默认缩略图
if (layer) {
layer.thumbnailUrl = this.defaultThumbnail; // 更新图层对象的缩略图
}
return this.defaultThumbnail;
}
// 延迟执行避免阻塞UI
fabricObjects.length > 0 &&
requestIdleCallback(() => {
setTimeout(async () => {
const base64 = await this._generateLayerThumbnailNow(fabricObjects, layer);
// this.layerThumbnails.set(layerId, base64);
try {
const { layer, parent } = findLayerRecursively(this.layers.value, layerId);
if (layer) {
layer.thumbnailUrl = base64; // 更新图层对象的缩略图
}
if (parent) {
// 如果是组图层,则同步更新父图层的缩略图
this.generateLayerThumbnail(parent.id);
}
} catch (error) {
console.error("生成图层缩略图时出错:", error);
}
});
});
}
/**
* 批量生成图层缩略图
* @param {Array} layers 图层数组
*/
generateAllLayerThumbnails(layers) {
if (!layers || !Array.isArray(layers)) return;
requestIdleCallback(() => {
setTimeout(() => {
layers.forEach((layer) => {
if (layer && layer.id) {
this.generateLayerThumbnail(layer.id);
if (layer.children && layer.children.length) {
this.generateLayerThumbnail(layer.id);
}
}
});
});
});
}
// 生成图片
async _generateLayerThumbnailNow(fabricObjects, layer) {
if (!fabricObjects || fabricObjects.length === 0) {
console.warn("⚠️ 没有对象需要生成缩略图,返回默认缩略图");
return this.defaultThumbnail;
}
try {
let clippingMaskFabricObject = null;
if (layer?.clippingMask) {
// 重新创建遮罩对象
clippingMaskFabricObject = await restoreFabricObject(layer?.clippingMask, this.canvas);
// clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
absolutePositioned: true,
});
// clippingMaskFabricObject.dirty = true;
clippingMaskFabricObject.setCoords();
}
return await createRasterizedImage({
canvas: this.canvas, // 画布对象 必填
fabricObjects, // 要栅格化的对象列表 - 按顺序 必填
maskObject: clippingMaskFabricObject || null, // 用于裁剪的对象 - 可选
trimWhitespace: true, // 是否裁剪空白区域
trimPadding: 2, // 裁剪边距
quality: 0.2, // 图像质量
format: "png", // 图像格式
scaleFactor: 0.2, // 高清倍数 - 默认是画布的高清倍数
isReturenDataURL: true, // 是否返回DataURL而不是fabric.Image对象
isThumbnail: true, // 为缩略图
});
} catch (error) {
return this.defaultThumbnail; // 出错时返回默认缩略图
}
}
/**
* 收集要栅格化的图层和对象
* @private
*/
_collectLayersAndObjects(layerId) {
if (!layerId) {
// console.warn("⚠️ 无效的图层ID无法收集对象");
return [];
}
const { layer } = findLayerRecursively(this.layers.value, layerId);
if (!layer) {
console.warn("⚠️ 无效的图层,无法收集对象");
return [];
}
let layersToRasterize = [];
if (layer.children && layer.children.length > 0) {
// 组图层:收集自身和所有子图层
layersToRasterize = this._collectLayersToRasterize(layer);
} else {
// 普通图层:只收集自身
layersToRasterize = [layer];
}
// 收集所有图层的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,
// });
// }
// }
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,
});
}
}
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,
});
}
}
});
}
});
// 按z-index排序确保保持原有的渲染顺序
objectsWithZIndex.sort((a, b) => a.zIndex - b.zIndex);
// 提取排序后的对象
const objectsToRasterize = objectsWithZIndex.map((item) => item.object);
// console.log(
// `📊 收集到 ${layersToRasterize.length} 个图层,${objectsToRasterize.length} 个对象进行栅格化`
// );
// console.log(
// "🔢 对象z-index顺序:",
// objectsWithZIndex.map((item) => ({
// id: item.object.id,
// type: item.object.type,
// zIndex: item.zIndex,
// }))
// );
return objectsToRasterize;
}
/**
* 收集要栅格化的图层(递归收集子图层)
* @param {Object} sourceLayer 源图层
* @returns {Array} 图层数组
* @private
*/
_collectLayersToRasterize(sourceLayer) {
const result = [sourceLayer];
// 如果是组图层,收集所有子图层
if (sourceLayer.children && sourceLayer.children.length > 0) {
sourceLayer.children.forEach((childLayer) => {
if (childLayer) {
result.push(...this._collectLayersToRasterize(childLayer));
}
});
}
return result;
}
/**
* 获取图层缩略图
* @param {String} layerId 图层ID
* @returns {String|null} 缩略图URL或null
*/
getLayerThumbnail(layerId) {
if (!layerId) return null;
// return this.layerThumbnails.get(layerId) || null;
}
/**
* 清除图层缩略图
* @param {String} layerId 图层ID
*/
clearLayerThumbnail(layerId) {
if (layerId && this.layerThumbnails.has(layerId)) {
// this.layerThumbnails.delete(layerId);
}
}
/**
* 清除所有缩略图
*/
clearAllThumbnails() {
// this.layerThumbnails.clear();
}
/**
* 释放资源
*/
dispose() {
this.clearAllThumbnails();
this.canvas = null;
}
}