/** * 缩略图管理器 - 负责生成和缓存图层和元素的预览缩略图 */ export class ThumbnailManager { constructor(canvas, options = {}) { this.canvas = canvas; this.layers = options.layers || []; // 图层管理器 this.layerThumbSize = options.layerThumbSize || { width: 48, height: 48 }; this.elementThumbSize = options.elementThumbSize || { width: 36, height: 36, }; this.layerThumbnails = new Map(); // 图层缩略图缓存 this.elementThumbnails = new Map(); // 元素缩略图缓存 this.defaultThumbnail = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="; // 1x1 透明图 } /** * 生成图层缩略图 * @param {Object} layer 图层对象ID */ generateLayerThumbnail(layerId) { // const layer = this?.layers.value?.find((layer) => layer.id === layerId); // if (!layer) return; // // 延迟执行,避免阻塞UI // requestAnimationFrame(() => { // this._generateLayerThumbnailNow(layer); // }); } /** * 立即生成图层缩略图 * @param {Object} layer 图层对象 * @private */ _generateLayerThumbnailNow(layer) { if ( !layer || !this.canvas || !layer.fabricObjects || !layer.fabricObjects?.length ) return; try { let thumbnail = null; if (!layer.children?.length) { // 如果是元素图层,直接生成元素缩略图 thumbnail = this._generateThumbnailFromObjects( layer.fabricObjects, this.layerThumbSize.width, this.layerThumbSize.height ); } else if (layer.type === "group" || layer.children?.length) { const fabricObjects = layer.children.reduce((pre, next) => { if (next.fabricObjects.length) { pre.push(...next.fabricObjects); } return pre; }, []); // 如果是分组图层,合并所有子对象的缩略图 thumbnail = this._generateThumbnailFromObjects( fabricObjects, this.layerThumbSize.width, this.layerThumbSize.height ); } // 保存到缩略图缓存 if (thumbnail) { this.layerThumbnails.set(layer.id, thumbnail); } else { // 如果无法生成缩略图,使用默认缩略图 this.layerThumbnails.set(layer.id, this.defaultThumbnail); } } catch (error) { console.error("生成图层缩略图出错:", error); } } /** * 生成元素缩略图 * @param {Object} element 元素对象 * @param {Object} fabricObject fabric对象 */ generateElementThumbnail(element, fabricObject) { if (!element || !element.id || !fabricObject) return; // 延迟执行,避免阻塞UI requestAnimationFrame(() => { try { const thumbnail = this._generateThumbnailFromObject( fabricObject, this.elementThumbSize.width, this.elementThumbSize.height ); if (thumbnail) { this.elementThumbnails.set(element.id, thumbnail); } } catch (error) { console.error("生成元素缩略图出错:", error); } }); } /** * 从fabric对象生成缩略图 * @param {Object} obj fabric对象 * @param {Number} width 缩略图宽度 * @param {Number} height 缩略图高度 * @returns {String} 缩略图数据URL * @private */ _generateThumbnailFromObject(obj, width, height) { if (!obj || !this.canvas) return null; // 保存对象状态 const originalState = { active: obj.active, visible: obj.visible, left: obj.left, top: obj.top, scaleX: obj.scaleX, scaleY: obj.scaleY, opacity: obj.opacity, }; // 临时修改对象状态 obj.set({ active: false, visible: true, opacity: 1, }); // 创建临时画布 const tempCanvas = document.createElement("canvas"); tempCanvas.width = width; tempCanvas.height = height; const tempCtx = tempCanvas.getContext("2d"); // 获取对象边界 const bounds = obj.getBoundingRect(); // 绘制缩略图 try { // 清空画布 tempCtx.clearRect(0, 0, width, height); // 计算缩放比例 const scaleFactorX = width / bounds.width; const scaleFactorY = height / bounds.height; const scaleFactor = Math.min(scaleFactorX, scaleFactorY) * 0.8; // 保留一些边距 // 居中绘制 const centerX = width / 2; const centerY = height / 2; tempCtx.save(); tempCtx.translate(centerX, centerY); tempCtx.scale(scaleFactor, scaleFactor); tempCtx.translate( -bounds.left - bounds.width / 2, -bounds.top - bounds.height / 2 ); // 绘制对象 obj.render(tempCtx); tempCtx.restore(); // 转换为数据URL const dataUrl = tempCanvas.toDataURL("image/png"); // 恢复对象状态 obj.set(originalState); return dataUrl; } catch (error) { console.error("绘制对象缩略图出错:", error); // 恢复对象状态 obj.set(originalState); return null; } } /** * 从多个fabric对象生成组合缩略图 * @param {Array} objects fabric对象数组 * @param {Number} width 缩略图宽度 * @param {Number} height 缩略图高度 * @returns {String} 缩略图数据URL * @private */ _generateThumbnailFromObjects(objects, width, height) { if (!objects || !objects.length || !this.canvas) return null; // 创建临时画布 const tempCanvas = document.createElement("canvas"); tempCanvas.width = width; tempCanvas.height = height; const tempCtx = tempCanvas.getContext("2d"); // 计算所有对象的总边界 let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; objects.forEach((obj) => { if (!obj.visible) return; const bounds = obj.getBoundingRect(); minX = Math.min(minX, bounds.left); minY = Math.min(minY, bounds.top); maxX = Math.max(maxX, bounds.left + bounds.width); maxY = Math.max(maxY, bounds.top + bounds.height); }); const groupWidth = maxX - minX; const groupHeight = maxY - minY; // 如果没有有效对象,返回null if (groupWidth <= 0 || groupHeight <= 0) return null; // 保存对象状态 const originalStates = objects.map((obj) => ({ obj, state: { active: obj.active, visible: obj.visible, opacity: obj.opacity, }, })); // 临时修改对象状态 originalStates.forEach((item) => { item.obj.set({ active: false, visible: true, opacity: 1, }); }); // 绘制缩略图 try { // 清空画布 tempCtx.clearRect(0, 0, width, height); // 计算缩放比例 const scaleFactorX = width / groupWidth; const scaleFactorY = height / groupHeight; const scaleFactor = Math.min(scaleFactorX, scaleFactorY) * 0.8; // 保留一些边距 // 居中绘制 const centerX = width / 2; const centerY = height / 2; tempCtx.save(); tempCtx.translate(centerX, centerY); tempCtx.scale(scaleFactor, scaleFactor); tempCtx.translate(-(minX + groupWidth / 2), -(minY + groupHeight / 2)); // 按顺序绘制所有对象 objects.forEach((obj) => { if (obj.visible) { obj.render(tempCtx); } }); tempCtx.restore(); // 转换为数据URL const dataUrl = tempCanvas.toDataURL("image/png"); // 恢复对象状态 originalStates.forEach((item) => { item.obj.set(item.state); }); return dataUrl; } catch (error) { console.error("绘制组合缩略图出错:", error); // 恢复对象状态 originalStates.forEach((item) => { item.obj.set(item.state); }); return null; } } /** * 批量生成图层缩略图 * @param {Array} layers 图层数组 */ generateAllLayerThumbnails(layers) { if (!layers || !Array.isArray(layers)) return; // 使用requestAnimationFrame批量生成,避免阻塞主线程 requestAnimationFrame(() => { layers.forEach((layer) => { if (layer && layer.id) { this._generateLayerThumbnailNow(layer); } }); }); } /** * 获取图层缩略图 * @param {String} layerId 图层ID * @returns {String|null} 缩略图URL或null */ getLayerThumbnail(layerId) { if (!layerId) return null; return this.layerThumbnails.get(layerId) || null; } /** * 获取元素缩略图 * @param {String} elementId 元素ID * @returns {String|null} 缩略图URL或null */ getElementThumbnail(elementId) { if (!elementId) return null; return this.elementThumbnails.get(elementId) || null; } /** * 清除图层缩略图 * @param {String} layerId 图层ID */ clearLayerThumbnail(layerId) { if (layerId && this.layerThumbnails.has(layerId)) { this.layerThumbnails.delete(layerId); } } /** * 清除元素缩略图 * @param {String} elementId 元素ID */ clearElementThumbnail(elementId) { if (elementId && this.elementThumbnails.has(elementId)) { this.elementThumbnails.delete(elementId); } } /** * 清除所有缩略图 */ clearAllThumbnails() { this.layerThumbnails.clear(); this.elementThumbnails.clear(); } /** * 释放资源 */ dispose() { this.clearAllThumbnails(); this.canvas = null; } }