273 lines
8.4 KiB
JavaScript
273 lines
8.4 KiB
JavaScript
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;
|
||
}
|
||
}
|