fix: 修复多个已知问题
This commit is contained in:
@@ -28,6 +28,7 @@ import {
|
||||
simplifyLayers,
|
||||
validateLayerAssociations,
|
||||
} from "../utils/layerUtils";
|
||||
import { imageModeHandler } from "../utils/imageHelper";
|
||||
|
||||
export class CanvasManager {
|
||||
constructor(canvasElement, options) {
|
||||
@@ -107,6 +108,23 @@ export class CanvasManager {
|
||||
id: fabricImage.id || generateId("brush_img_"),
|
||||
});
|
||||
|
||||
if (options.imageMode) {
|
||||
imageModeHandler({
|
||||
imageMode: options.imageMode,
|
||||
newImage: fabricImage,
|
||||
canvasWidth: this.canvasWidth.value,
|
||||
canvasHeight: this.canvasHeight.value,
|
||||
});
|
||||
|
||||
// 默认居中
|
||||
fabricImage.set({
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
left: this.canvas.width / 2,
|
||||
top: this.canvas.height / 2,
|
||||
});
|
||||
}
|
||||
|
||||
// 执行高保真合并操作
|
||||
await this.eventManager?.mergeLayerObjectsForPerformance?.({
|
||||
fabricImage,
|
||||
|
||||
@@ -103,7 +103,7 @@ export class LayerManager {
|
||||
this.canvasHeight = options.canvasHeight || 600;
|
||||
|
||||
// 默认背景颜色
|
||||
this.backgroundColor = options.backgroundColor || "#ffffff";
|
||||
this.backgroundColor = options.backgroundColor || { value: "#ffffff" };
|
||||
|
||||
// 复制粘贴相关
|
||||
this.clipboardData = null;
|
||||
@@ -428,7 +428,7 @@ export class LayerManager {
|
||||
name: name,
|
||||
canvasWidth: this.canvasWidth,
|
||||
canvasHeight: this.canvasHeight,
|
||||
backgroundColor: this.backgroundColor,
|
||||
backgroundColor: this.backgroundColor.value,
|
||||
});
|
||||
|
||||
// 直接创建和执行命令
|
||||
@@ -1388,7 +1388,7 @@ export class LayerManager {
|
||||
activeLayerId: this.activeLayerId.value,
|
||||
canvasWidth: this.canvasWidth,
|
||||
canvasHeight: this.canvasHeight,
|
||||
backgroundColor: this.backgroundColor,
|
||||
backgroundColor: this.backgroundColor.value,
|
||||
editorMode: this.editorMode,
|
||||
};
|
||||
}
|
||||
@@ -1724,7 +1724,6 @@ export class LayerManager {
|
||||
* @param {string} backgroundColor 背景颜色
|
||||
*/
|
||||
updateBackgroundColor(backgroundColor) {
|
||||
// 查找背景图层
|
||||
const backgroundLayer = this.layers.value.find(
|
||||
(layer) => layer.isBackground
|
||||
);
|
||||
@@ -1739,7 +1738,8 @@ export class LayerManager {
|
||||
canvas: this.canvas,
|
||||
layers: this.layers,
|
||||
canvasManager: this.canvasManager,
|
||||
backgroundColor: backgroundColor,
|
||||
backgroundColor,
|
||||
backgroundColorValue: this.backgroundColor,
|
||||
});
|
||||
|
||||
// 执行命令
|
||||
@@ -1748,9 +1748,6 @@ export class LayerManager {
|
||||
} else {
|
||||
command.execute();
|
||||
}
|
||||
|
||||
// 更新存储的背景颜色
|
||||
this.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,25 +25,27 @@ export class ThumbnailManager {
|
||||
|
||||
// 延迟执行,避免阻塞UI
|
||||
fabricObjects.length > 0 &&
|
||||
requestAnimationFrame(async () => {
|
||||
const base64 = await this._generateLayerThumbnailNow(fabricObjects);
|
||||
this.layerThumbnails.set(layerId, base64);
|
||||
try {
|
||||
const { layer, parent } = findLayerRecursively(
|
||||
this.layers.value,
|
||||
layerId
|
||||
);
|
||||
if (layer) {
|
||||
layer.thumbnailUrl = base64; // 更新图层对象的缩略图
|
||||
}
|
||||
requestIdleCallback(() => {
|
||||
setTimeout(async () => {
|
||||
const base64 = await this._generateLayerThumbnailNow(fabricObjects);
|
||||
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);
|
||||
if (parent) {
|
||||
// 如果是组图层,则同步更新父图层的缩略图
|
||||
this.generateLayerThumbnail(parent.id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("生成图层缩略图时出错:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("生成图层缩略图时出错:", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,14 +56,17 @@ export class ThumbnailManager {
|
||||
generateAllLayerThumbnails(layers) {
|
||||
if (!layers || !Array.isArray(layers)) return;
|
||||
|
||||
// 使用requestAnimationFrame批量生成,避免阻塞主线程
|
||||
layers.forEach((layer) => {
|
||||
if (layer && layer.id) {
|
||||
this.generateLayerThumbnail(layer.id);
|
||||
if (layer.children && layer.children.length) {
|
||||
this.generateLayerThumbnail(layer.id);
|
||||
}
|
||||
}
|
||||
requestIdleCallback(() => {
|
||||
setTimeout(() => {
|
||||
layers.forEach((layer) => {
|
||||
if (layer && layer.id) {
|
||||
this.generateLayerThumbnail(layer.id);
|
||||
if (layer.children && layer.children.length) {
|
||||
this.generateLayerThumbnail(layer.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,17 +76,22 @@ export class ThumbnailManager {
|
||||
console.warn("⚠️ 没有对象需要生成缩略图,返回默认缩略图");
|
||||
return this.defaultThumbnail;
|
||||
}
|
||||
return await createRasterizedImage({
|
||||
canvas: this.canvas, // 画布对象 必填
|
||||
fabricObjects, // 要栅格化的对象列表 - 按顺序 必填
|
||||
// maskObject = null, // 用于裁剪的对象 - 可选 // TODO: 后期看是否需要裁剪
|
||||
trimWhitespace: true, // 是否裁剪空白区域
|
||||
trimPadding: 2, // 裁剪边距
|
||||
quality: 0.8, // 图像质量
|
||||
format: "png", // 图像格式
|
||||
scaleFactor: 1, // 高清倍数 - 默认是画布的高清倍数
|
||||
isReturenDataURL: true, // 是否返回DataURL而不是fabric.Image对象
|
||||
});
|
||||
try {
|
||||
return await createRasterizedImage({
|
||||
canvas: this.canvas, // 画布对象 必填
|
||||
fabricObjects, // 要栅格化的对象列表 - 按顺序 必填
|
||||
// maskObject = null, // 用于裁剪的对象 - 可选 // TODO: 后期看是否需要裁剪
|
||||
trimWhitespace: true, // 是否裁剪空白区域
|
||||
trimPadding: 2, // 裁剪边距
|
||||
quality: 0.2, // 图像质量
|
||||
format: "png", // 图像格式
|
||||
scaleFactor: 0.2, // 高清倍数 - 默认是画布的高清倍数
|
||||
isReturenDataURL: true, // 是否返回DataURL而不是fabric.Image对象
|
||||
isThumbnail: true, // 为缩略图
|
||||
});
|
||||
} catch (error) {
|
||||
return this.defaultThumbnail; // 出错时返回默认缩略图
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,8 +36,137 @@ export class BaseBrush {
|
||||
* @param {Object} brush fabric笔刷实例
|
||||
* @param {Object} options 配置选项
|
||||
*/
|
||||
configure(brush, options) {
|
||||
throw new Error("必须由子类实现configure方法");
|
||||
configure(brush, options = {}) {
|
||||
if (!brush) return;
|
||||
|
||||
// 基础属性配置
|
||||
if (options.width !== undefined) {
|
||||
brush.width = options.width;
|
||||
}
|
||||
|
||||
if (options.color !== undefined && options.opacity !== undefined) {
|
||||
// 使用RGBA颜色而不是设置globalAlpha
|
||||
brush.color = this._createRGBAColor(options.color, options.opacity);
|
||||
brush.opacity = 1; // 保持fabric层面的opacity为1
|
||||
} else if (options.color !== undefined) {
|
||||
brush.color = options.color;
|
||||
} else if (options.opacity !== undefined) {
|
||||
// 如果只设置了透明度,基于当前颜色创建RGBA
|
||||
const currentColor = brush.color || this.options.color || "#000000";
|
||||
brush.color = this._createRGBAColor(currentColor, options.opacity);
|
||||
brush.opacity = 1;
|
||||
}
|
||||
|
||||
// 配置阴影
|
||||
this.configureShadow(brush, options);
|
||||
|
||||
// 确保不使用globalAlpha,避免圆圈绘制问题
|
||||
if (brush.canvas && brush.canvas.contextTop) {
|
||||
brush.canvas.contextTop.globalAlpha = 1;
|
||||
brush.canvas.contextTop.lineCap = "round";
|
||||
brush.canvas.contextTop.lineJoin = "round";
|
||||
brush.canvas.contextTop.globalCompositeOperation = "source-over";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置笔刷阴影
|
||||
* @param {Object} brush fabric笔刷实例
|
||||
* @param {Object} options 配置选项
|
||||
*/
|
||||
configureShadow(brush, options = {}) {
|
||||
if (!brush) return;
|
||||
|
||||
// 简化的阴影配置获取方法
|
||||
let shadowConfig = null;
|
||||
|
||||
// 尝试从全局获取BrushStore(在Vue组件中已经导入)
|
||||
if (typeof window !== "undefined" && window.BrushStore) {
|
||||
shadowConfig = window.BrushStore.getShadowConfig();
|
||||
} else {
|
||||
// 如果没有全局BrushStore,尝试从选项中获取
|
||||
if (options.shadowEnabled) {
|
||||
shadowConfig = {
|
||||
color: options.shadowColor || "#000000",
|
||||
blur: options.shadowWidth || 0,
|
||||
offsetX: options.shadowOffsetX || 0,
|
||||
offsetY: options.shadowOffsetY || 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (shadowConfig) {
|
||||
// 创建fabric.Shadow实例
|
||||
if (typeof fabric !== "undefined" && fabric.Shadow) {
|
||||
brush.shadow = new fabric.Shadow(shadowConfig);
|
||||
}
|
||||
} else {
|
||||
// 清除阴影
|
||||
brush.shadow = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新笔刷阴影设置
|
||||
*/
|
||||
updateShadow() {
|
||||
if (this.brush) {
|
||||
this.configureShadow(this.brush);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建RGBA颜色字符串
|
||||
* @private
|
||||
* @param {String} color 十六进制颜色或已有颜色
|
||||
* @param {Number} opacity 透明度 (0-1)
|
||||
* @returns {String} RGBA颜色字符串
|
||||
*/
|
||||
_createRGBAColor(color, opacity) {
|
||||
// 如果已经是rgba颜色,先提取RGB部分
|
||||
if (color.startsWith("rgba")) {
|
||||
const rgbaMatch = color.match(
|
||||
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/
|
||||
);
|
||||
if (rgbaMatch) {
|
||||
const [, r, g, b] = rgbaMatch;
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是rgb颜色,提取RGB部分
|
||||
if (color.startsWith("rgb")) {
|
||||
const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
||||
if (rgbMatch) {
|
||||
const [, r, g, b] = rgbMatch;
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理十六进制颜色
|
||||
if (color.startsWith("#")) {
|
||||
const hex = color.replace("#", "");
|
||||
let r, g, b;
|
||||
|
||||
if (hex.length === 3) {
|
||||
r = parseInt(hex[0] + hex[0], 16);
|
||||
g = parseInt(hex[1] + hex[1], 16);
|
||||
b = parseInt(hex[2] + hex[2], 16);
|
||||
} else if (hex.length === 6) {
|
||||
r = parseInt(hex.substring(0, 2), 16);
|
||||
g = parseInt(hex.substring(2, 4), 16);
|
||||
b = parseInt(hex.substring(4, 6), 16);
|
||||
} else {
|
||||
// 无效的十六进制颜色,使用默认
|
||||
r = g = b = 0;
|
||||
}
|
||||
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
}
|
||||
|
||||
// 如果是其他格式的颜色,尝试转换(例如颜色名称)
|
||||
// 这里简化处理,实际项目中可以使用更复杂的颜色解析
|
||||
return color; // fallback到原颜色
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,23 +274,26 @@ export class BaseBrush {
|
||||
* @returns {Boolean} 是否更新成功
|
||||
*/
|
||||
updateProperty(propId, value) {
|
||||
// 基础实现,可以被子类覆盖以处理特殊属性
|
||||
if (propId === "size") {
|
||||
if (this.brush) {
|
||||
this.brush.width = value;
|
||||
return true;
|
||||
this.configure(this.brush, { width: value });
|
||||
}
|
||||
return true;
|
||||
} else if (propId === "color") {
|
||||
if (this.brush) {
|
||||
this.brush.color = value;
|
||||
return true;
|
||||
this.configure(this.brush, { color: value });
|
||||
}
|
||||
return true;
|
||||
} else if (propId === "opacity") {
|
||||
if (this.brush) {
|
||||
this.brush.opacity = value;
|
||||
return true;
|
||||
this.configure(this.brush, { opacity: value });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { CustomPenBrush } from "./types/CustomPenBrush";
|
||||
import { RibbonBrush } from "./types/RibbonBrush";
|
||||
import { ShadedBrush } from "./types/ShadedBrush";
|
||||
// import { SketchyBrush } from "./types/SketchyBrush";
|
||||
import { SpraypaintBrush } from "./types/SpraypaintBrush";
|
||||
// import { SpraypaintBrush } from "./types/SpraypaintBrush";
|
||||
|
||||
/**
|
||||
* 笔刷管理器
|
||||
@@ -116,53 +116,53 @@ export class BrushManager {
|
||||
category: "基础笔刷",
|
||||
});
|
||||
// brushRegistry.register("sketchy", SketchyBrush);
|
||||
brushRegistry.register("spraypaint", SpraypaintBrush, {
|
||||
name: "Spraypaint",
|
||||
description: "喷漆笔刷,模拟喷漆效果",
|
||||
category: "基础笔刷",
|
||||
});
|
||||
// brushRegistry.register("spraypaint", SpraypaintBrush, {
|
||||
// name: "Spraypaint",
|
||||
// description: "喷漆笔刷,模拟喷漆效果",
|
||||
// category: "基础笔刷",
|
||||
// });
|
||||
|
||||
// // 注册喷枪笔刷
|
||||
// brushRegistry.register(
|
||||
// "spray",
|
||||
// class SprayBrush extends PencilBrush {
|
||||
// constructor(canvas, options = {}) {
|
||||
// super(canvas, {
|
||||
// id: "spray",
|
||||
// name: "喷枪",
|
||||
// description: "模拟喷枪效果,创建散点效果",
|
||||
// category: "基础笔刷",
|
||||
// ...options,
|
||||
// });
|
||||
// }
|
||||
brushRegistry.register(
|
||||
"spray",
|
||||
class SprayBrush extends PencilBrush {
|
||||
constructor(canvas, options = {}) {
|
||||
super(canvas, {
|
||||
id: "spray",
|
||||
name: "Spray",
|
||||
description: "模拟喷枪效果,创建散点效果",
|
||||
category: "基础笔刷",
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
// create() {
|
||||
// this.brush = new fabric.SprayBrush(this.canvas);
|
||||
// this.configure(this.brush, this.options);
|
||||
// return this.brush;
|
||||
// }
|
||||
create() {
|
||||
this.brush = new fabric.SprayBrush(this.canvas);
|
||||
this.configure(this.brush, this.options);
|
||||
return this.brush;
|
||||
}
|
||||
|
||||
// configure(brush, options = {}) {
|
||||
// super.configure(brush, options);
|
||||
configure(brush, options = {}) {
|
||||
super.configure(brush, options);
|
||||
|
||||
// if (options.density !== undefined) {
|
||||
// brush.density = options.density;
|
||||
// }
|
||||
if (options.density !== undefined) {
|
||||
brush.density = options.density;
|
||||
}
|
||||
|
||||
// if (options.randomOpacity !== undefined) {
|
||||
// brush.randomOpacity = options.randomOpacity;
|
||||
// }
|
||||
if (options.randomOpacity !== undefined) {
|
||||
brush.randomOpacity = options.randomOpacity;
|
||||
}
|
||||
|
||||
// if (options.dotWidth !== undefined) {
|
||||
// brush.dotWidth = options.dotWidth;
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// name: "喷枪",
|
||||
// description: "模拟喷枪效果,创建散点效果",
|
||||
// }
|
||||
// );
|
||||
if (options.dotWidth !== undefined) {
|
||||
brush.dotWidth = options.dotWidth;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "喷枪",
|
||||
description: "模拟喷枪效果,创建散点效果",
|
||||
}
|
||||
);
|
||||
// 注册橡皮擦笔刷
|
||||
brushRegistry.register(
|
||||
"eraser",
|
||||
@@ -387,6 +387,13 @@ export class BrushManager {
|
||||
width: this.brushStore.state.size,
|
||||
opacity: this.brushStore.state.opacity,
|
||||
|
||||
// 阴影相关配置
|
||||
shadowEnabled: this.brushStore.state.shadowEnabled,
|
||||
shadowColor: this.brushStore.state.shadowColor,
|
||||
shadowWidth: this.brushStore.state.shadowWidth,
|
||||
shadowOffsetX: this.brushStore.state.shadowOffsetX,
|
||||
shadowOffsetY: this.brushStore.state.shadowOffsetY,
|
||||
|
||||
// 材质笔刷特有配置
|
||||
textureEnabled: this.brushStore.state.textureEnabled,
|
||||
texturePath: this.brushStore.state.texturePath,
|
||||
@@ -522,16 +529,23 @@ export class BrushManager {
|
||||
// 限制透明度范围
|
||||
const brushOpacity = Math.max(0.05, Math.min(1, opacity));
|
||||
|
||||
// 更新笔刷透明度
|
||||
this.canvas.freeDrawingBrush.opacity = brushOpacity;
|
||||
// 不再设置fabric笔刷的opacity属性,而是通过颜色的RGBA值来实现透明度
|
||||
// this.canvas.freeDrawingBrush.opacity = brushOpacity;
|
||||
|
||||
// 更新活动笔刷
|
||||
// 更新活动笔刷配置,使用当前颜色和新的透明度
|
||||
if (this.activeBrush) {
|
||||
const currentColor = this.brushStore.state.color;
|
||||
this.activeBrush.configure(this.canvas.freeDrawingBrush, {
|
||||
color: currentColor,
|
||||
opacity: brushOpacity,
|
||||
});
|
||||
}
|
||||
|
||||
// 确保上下文透明度始终为1,避免全局透明度问题
|
||||
if (this.canvas.contextTop) {
|
||||
this.canvas.contextTop.globalAlpha = 1;
|
||||
}
|
||||
|
||||
// 更新Store
|
||||
this.brushStore.setBrushOpacity(brushOpacity);
|
||||
|
||||
@@ -842,6 +856,16 @@ export class BrushManager {
|
||||
|
||||
return this.canvas.freeDrawingBrush;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新笔刷阴影设置
|
||||
*/
|
||||
updateShadow() {
|
||||
if (this.activeBrush && this.activeBrush.updateShadow) {
|
||||
this.activeBrush.updateShadow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建橡皮擦
|
||||
* @returns {Object} 橡皮擦笔刷
|
||||
|
||||
@@ -427,6 +427,12 @@ import { sprayBrushDataUrl } from "./data/sprayBrushData.js";
|
||||
},
|
||||
|
||||
onMouseDown: function (pointer) {
|
||||
// 添加坐标转换处理画布缩放和偏移
|
||||
pointer.x =
|
||||
pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
|
||||
pointer.y =
|
||||
pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
|
||||
|
||||
this._points = [pointer];
|
||||
this._count = 0;
|
||||
|
||||
@@ -449,6 +455,12 @@ import { sprayBrushDataUrl } from "./data/sprayBrushData.js";
|
||||
},
|
||||
|
||||
onMouseMove: function (pointer) {
|
||||
// 添加坐标转换处理画布缩放和偏移
|
||||
pointer.x =
|
||||
pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
|
||||
pointer.y =
|
||||
pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
|
||||
|
||||
this._points.push(pointer);
|
||||
|
||||
var i,
|
||||
@@ -985,9 +997,9 @@ import { sprayBrushDataUrl } from "./data/sprayBrushData.js";
|
||||
ctx.globalAlpha = 0.8 * this.opacity;
|
||||
ctx.moveTo(this._lastPoint.x, this._lastPoint.y);
|
||||
let x =
|
||||
content > this._lastPoint.x
|
||||
? content - this._lastPoint.x + content
|
||||
: content * 2 - this._lastPoint.x;
|
||||
this.content > this._lastPoint.x
|
||||
? this.content - this._lastPoint.x + this.content
|
||||
: this.content * 2 - this._lastPoint.x;
|
||||
ctx.lineTo(x, this._lastPoint.y);
|
||||
// ctx.lineTo(pointer.y + lineWidthDiff,pointer.x + lineWidthDiff);
|
||||
ctx.stroke();
|
||||
@@ -997,10 +1009,12 @@ import { sprayBrushDataUrl } from "./data/sprayBrushData.js";
|
||||
},
|
||||
|
||||
onMouseDown: function (pointer) {
|
||||
// 添加坐标转换处理画布缩放和偏移
|
||||
pointer.x =
|
||||
pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
|
||||
pointer.y =
|
||||
pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
|
||||
|
||||
this._lastPoint = pointer;
|
||||
this.canvas.contextTop.strokeStyle = this.color;
|
||||
this.canvas.contextTop.lineWidth = this._lineWidth;
|
||||
@@ -1009,10 +1023,12 @@ import { sprayBrushDataUrl } from "./data/sprayBrushData.js";
|
||||
|
||||
onMouseMove: function (pointer) {
|
||||
if (this.canvas._isCurrentlyDrawing) {
|
||||
// 添加坐标转换处理画布缩放和偏移
|
||||
pointer.x =
|
||||
pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
|
||||
pointer.y =
|
||||
pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
|
||||
|
||||
this._render(pointer);
|
||||
}
|
||||
},
|
||||
@@ -1022,7 +1038,7 @@ import { sprayBrushDataUrl } from "./data/sprayBrushData.js";
|
||||
this.canvas.contextTop.globalAlpha = 1;
|
||||
this.convertToImg();
|
||||
},
|
||||
}); // End MarkerBrush
|
||||
}); // End MarkerBrush1
|
||||
/**
|
||||
* PenBrush
|
||||
* Based on code by Tennison Chan.
|
||||
|
||||
@@ -87,8 +87,15 @@ export class PencilBrush extends BaseBrush {
|
||||
|
||||
// 调用 convertToImg 方法将绘制内容转换为图片
|
||||
if (typeof this.convertToImg === "function") {
|
||||
// 确保在转换前设置正确的透明度
|
||||
const currentAlpha = ctx.globalAlpha;
|
||||
ctx.globalAlpha = this.opacity || 1;
|
||||
|
||||
this.convertToImg();
|
||||
console.log("PencilBrush: convertToImg called successfully");
|
||||
|
||||
// 恢复透明度
|
||||
ctx.globalAlpha = currentAlpha;
|
||||
} else {
|
||||
console.warn(
|
||||
"convertToImg method not found, falling back to original behavior"
|
||||
@@ -157,31 +164,96 @@ export class PencilBrush extends BaseBrush {
|
||||
brush.width = options.width;
|
||||
}
|
||||
|
||||
if (options.color !== undefined) {
|
||||
if (options.color !== undefined && options.opacity !== undefined) {
|
||||
// 使用RGBA颜色而不是设置globalAlpha
|
||||
brush.color = this._createRGBAColor(options.color, options.opacity);
|
||||
} else if (options.color !== undefined) {
|
||||
brush.color = options.color;
|
||||
} else if (options.opacity !== undefined) {
|
||||
// 如果只设置了透明度,基于当前颜色创建RGBA
|
||||
const currentColor = brush.color || "#000000";
|
||||
brush.color = this._createRGBAColor(currentColor, options.opacity);
|
||||
}
|
||||
|
||||
if (options.opacity !== undefined) {
|
||||
brush.opacity = options.opacity;
|
||||
// 确保不使用globalAlpha,避免圆圈绘制问题
|
||||
if (brush.canvas && brush.canvas.contextTop) {
|
||||
brush.canvas.contextTop.globalAlpha = 1;
|
||||
brush.canvas.contextTop.lineCap = "round";
|
||||
brush.canvas.contextTop.lineJoin = "round";
|
||||
}
|
||||
|
||||
// 特殊属性配置
|
||||
options.decimate = 0;
|
||||
if (options.decimate !== undefined) {
|
||||
brush.decimate = options.decimate;
|
||||
this.decimate = options.decimate;
|
||||
}
|
||||
|
||||
options.strokeLineCap = "round";
|
||||
if (options.strokeLineCap !== undefined) {
|
||||
brush.strokeLineCap = options.strokeLineCap;
|
||||
this.strokeLineCap = options.strokeLineCap;
|
||||
}
|
||||
|
||||
options.strokeLineJoin = "round";
|
||||
if (options.strokeLineJoin !== undefined) {
|
||||
brush.strokeLineJoin = options.strokeLineJoin;
|
||||
this.strokeLineJoin = options.strokeLineJoin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建RGBA颜色字符串
|
||||
* @private
|
||||
* @param {String} color 十六进制颜色或已有颜色
|
||||
* @param {Number} opacity 透明度 (0-1)
|
||||
* @returns {String} RGBA颜色字符串
|
||||
*/
|
||||
_createRGBAColor(color, opacity) {
|
||||
// 如果已经是rgba颜色,先提取RGB部分
|
||||
if (color.startsWith("rgba")) {
|
||||
const rgbaMatch = color.match(
|
||||
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/
|
||||
);
|
||||
if (rgbaMatch) {
|
||||
const [, r, g, b] = rgbaMatch;
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是rgb颜色,提取RGB部分
|
||||
if (color.startsWith("rgb")) {
|
||||
const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
||||
if (rgbMatch) {
|
||||
const [, r, g, b] = rgbMatch;
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理十六进制颜色
|
||||
if (color.startsWith("#")) {
|
||||
const hex = color.replace("#", "");
|
||||
let r, g, b;
|
||||
|
||||
if (hex.length === 3) {
|
||||
r = parseInt(hex[0] + hex[0], 16);
|
||||
g = parseInt(hex[1] + hex[1], 16);
|
||||
b = parseInt(hex[2] + hex[2], 16);
|
||||
} else if (hex.length === 6) {
|
||||
r = parseInt(hex.substring(0, 2), 16);
|
||||
g = parseInt(hex.substring(2, 4), 16);
|
||||
b = parseInt(hex.substring(4, 6), 16);
|
||||
} else {
|
||||
// 无效的十六进制颜色,使用默认
|
||||
r = g = b = 0;
|
||||
}
|
||||
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
}
|
||||
|
||||
// 如果是其他格式的颜色,尝试转换(例如颜色名称)
|
||||
// 这里简化处理,实际项目中可以使用更复杂的颜色解析
|
||||
return color; // fallback到原颜色
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取笔刷可配置属性
|
||||
* @returns {Array} 可配置属性描述数组
|
||||
@@ -193,69 +265,69 @@ export class PencilBrush extends BaseBrush {
|
||||
|
||||
// 定义铅笔笔刷特有属性
|
||||
const pencilProperties = [
|
||||
{
|
||||
id: "decimate",
|
||||
name: "精细度",
|
||||
type: "slider",
|
||||
defaultValue: this.decimate,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.1,
|
||||
description: "控制笔触路径的简化程度,值越小路径越精细",
|
||||
category: "铅笔设置",
|
||||
order: 100,
|
||||
},
|
||||
{
|
||||
id: "strokeLineCap",
|
||||
name: "线条端点",
|
||||
type: "select",
|
||||
defaultValue: this.strokeLineCap,
|
||||
options: [
|
||||
{ value: "round", label: "圆形" },
|
||||
{ value: "butt", label: "平直" },
|
||||
{ value: "square", label: "方形" },
|
||||
],
|
||||
description: "线条端点的形状",
|
||||
category: "铅笔设置",
|
||||
order: 110,
|
||||
},
|
||||
{
|
||||
id: "strokeLineJoin",
|
||||
name: "线条连接",
|
||||
type: "select",
|
||||
defaultValue: this.strokeLineJoin,
|
||||
options: [
|
||||
{ value: "round", label: "圆角" },
|
||||
{ value: "bevel", label: "斜角" },
|
||||
{ value: "miter", label: "尖角" },
|
||||
],
|
||||
description: "线条拐角的连接方式",
|
||||
category: "铅笔设置",
|
||||
order: 120,
|
||||
},
|
||||
{
|
||||
id: "smoothingEnabled",
|
||||
name: "启用平滑",
|
||||
type: "checkbox",
|
||||
defaultValue: false,
|
||||
description: "是否对线条进行平滑处理",
|
||||
category: "铅笔设置",
|
||||
order: 130,
|
||||
},
|
||||
{
|
||||
id: "smoothingFactor",
|
||||
name: "平滑程度",
|
||||
type: "slider",
|
||||
defaultValue: 0.5,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.05,
|
||||
description: "线条平滑的强度",
|
||||
category: "铅笔设置",
|
||||
order: 140,
|
||||
// 只有当smoothingEnabled为true时才显示
|
||||
visibleWhen: { smoothingEnabled: true },
|
||||
},
|
||||
// {
|
||||
// id: "decimate",
|
||||
// name: "精细度",
|
||||
// type: "slider",
|
||||
// defaultValue: this.decimate,
|
||||
// min: 0,
|
||||
// max: 1,
|
||||
// step: 0.1,
|
||||
// description: "控制笔触路径的简化程度,值越小路径越精细",
|
||||
// category: "铅笔设置",
|
||||
// order: 100,
|
||||
// },
|
||||
// {
|
||||
// id: "strokeLineCap",
|
||||
// name: "线条端点",
|
||||
// type: "select",
|
||||
// defaultValue: this.strokeLineCap,
|
||||
// options: [
|
||||
// { value: "round", label: "圆形" },
|
||||
// { value: "butt", label: "平直" },
|
||||
// { value: "square", label: "方形" },
|
||||
// ],
|
||||
// description: "线条端点的形状",
|
||||
// category: "铅笔设置",
|
||||
// order: 110,
|
||||
// },
|
||||
// {
|
||||
// id: "strokeLineJoin",
|
||||
// name: "线条连接",
|
||||
// type: "select",
|
||||
// defaultValue: this.strokeLineJoin,
|
||||
// options: [
|
||||
// { value: "round", label: "圆角" },
|
||||
// { value: "bevel", label: "斜角" },
|
||||
// { value: "miter", label: "尖角" },
|
||||
// ],
|
||||
// description: "线条拐角的连接方式",
|
||||
// category: "铅笔设置",
|
||||
// order: 120,
|
||||
// },
|
||||
// {
|
||||
// id: "smoothingEnabled",
|
||||
// name: "启用平滑",
|
||||
// type: "checkbox",
|
||||
// defaultValue: false,
|
||||
// description: "是否对线条进行平滑处理",
|
||||
// category: "铅笔设置",
|
||||
// order: 130,
|
||||
// },
|
||||
// {
|
||||
// id: "smoothingFactor",
|
||||
// name: "平滑程度",
|
||||
// type: "slider",
|
||||
// defaultValue: 0.5,
|
||||
// min: 0,
|
||||
// max: 1,
|
||||
// step: 0.05,
|
||||
// description: "线条平滑的强度",
|
||||
// category: "铅笔设置",
|
||||
// order: 140,
|
||||
// // 只有当smoothingEnabled为true时才显示
|
||||
// visibleWhen: { smoothingEnabled: true },
|
||||
// },
|
||||
];
|
||||
|
||||
// 合并并返回所有属性
|
||||
|
||||
@@ -73,6 +73,15 @@ export class RibbonBrush extends BaseBrush {
|
||||
|
||||
if (options.opacity !== undefined) {
|
||||
brush.opacity = options.opacity;
|
||||
// 如果启用渐变,更新渐变的第一个颜色
|
||||
if (this.gradient && this.gradientColors.length > 0) {
|
||||
this.gradientColors[0] = this._createRGBAColor(
|
||||
brush.color,
|
||||
options.opacity
|
||||
);
|
||||
this.updateGradient();
|
||||
}
|
||||
brush.canvas.freeDrawingBrush.opacity = options.opacity;
|
||||
}
|
||||
|
||||
// 丝带笔刷特有属性
|
||||
|
||||
@@ -27,8 +27,7 @@ export class TextureBrush extends BaseBrush {
|
||||
this.textureRepeat = options.textureRepeat || "repeat";
|
||||
this.textureScale = options.textureScale || 1;
|
||||
this.textureAngle = options.textureAngle || 0;
|
||||
this.textureOpacity =
|
||||
options.textureOpacity !== undefined ? options.textureOpacity : 1;
|
||||
this.opacity = options.opacity !== undefined ? options.opacity : 1;
|
||||
|
||||
// 预设材质相关
|
||||
this.selectedTextureId = options.selectedTextureId || null;
|
||||
@@ -201,8 +200,8 @@ export class TextureBrush extends BaseBrush {
|
||||
}
|
||||
}
|
||||
|
||||
if (options.textureOpacity !== undefined) {
|
||||
this.textureOpacity = options.textureOpacity;
|
||||
if (options.opacity !== undefined) {
|
||||
this.opacity = options.opacity;
|
||||
// 需要重新应用纹理以应用透明度
|
||||
if (this.selectedTextureId) {
|
||||
this.setTextureById(this.selectedTextureId);
|
||||
@@ -286,9 +285,9 @@ export class TextureBrush extends BaseBrush {
|
||||
canvasTexture.width = width;
|
||||
canvasTexture.height = height;
|
||||
|
||||
// 应用透明度设置
|
||||
if (this.textureOpacity < 1) {
|
||||
ctx.globalAlpha = this.textureOpacity;
|
||||
// 应用纹理透明度设置 - 仅控制纹理的透明度
|
||||
if (this.opacity < 1) {
|
||||
ctx.globalAlpha = this.opacity;
|
||||
}
|
||||
|
||||
// 绘制前应用旋转
|
||||
@@ -310,6 +309,18 @@ export class TextureBrush extends BaseBrush {
|
||||
// 直接设置source属性,这是PatternBrush的标准用法
|
||||
this.brush.source = canvasTexture;
|
||||
|
||||
this.brush.canvas.opacity = this.opacity; // 确保画布不透明
|
||||
|
||||
// 设置笔刷颜色为完全透明的RGBA,避免重复绘制时的叠加效果
|
||||
// 笔刷的透明度通过RGBA颜色控制,而不是通过globalAlpha
|
||||
const brushOpacity = this.brush.opacity || 1;
|
||||
this.brush.color = `rgba(0, 0, 0, ${brushOpacity})`;
|
||||
|
||||
// 确保画布上下文使用完整的透明度,避免圆圈重叠问题
|
||||
// if (this.canvas && this.canvas.contextTop) {
|
||||
// this.canvas.contextTop.globalAlpha = 1;
|
||||
// }
|
||||
|
||||
// 通知画布重绘
|
||||
if (this.canvas && this.canvas.requestRenderAll) {
|
||||
this.canvas.requestRenderAll();
|
||||
@@ -378,8 +389,8 @@ export class TextureBrush extends BaseBrush {
|
||||
* @param {Number} opacity 透明度
|
||||
* @returns {Number} 设置后的透明度
|
||||
*/
|
||||
setTextureOpacity(opacity) {
|
||||
this.textureOpacity = Math.min(1, Math.max(0, opacity));
|
||||
setopacity(opacity) {
|
||||
this.opacity = Math.min(1, Math.max(0, opacity));
|
||||
|
||||
// 重新应用纹理以更新透明度
|
||||
if (this.selectedTextureId) {
|
||||
@@ -388,7 +399,7 @@ export class TextureBrush extends BaseBrush {
|
||||
this.setTexture(this.textureSource);
|
||||
}
|
||||
|
||||
return this.textureOpacity;
|
||||
return this.opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,10 +529,10 @@ export class TextureBrush extends BaseBrush {
|
||||
// order: 130,
|
||||
// },
|
||||
// {
|
||||
// id: "textureOpacity",
|
||||
// id: "opacity",
|
||||
// name: "纹理透明度",
|
||||
// type: "slider",
|
||||
// defaultValue: this.textureOpacity,
|
||||
// defaultValue: this.opacity,
|
||||
// min: 0,
|
||||
// max: 1,
|
||||
// step: 0.05,
|
||||
@@ -584,8 +595,8 @@ export class TextureBrush extends BaseBrush {
|
||||
} else if (propId === "textureAngle") {
|
||||
this.setTextureAngle(value);
|
||||
return true;
|
||||
} else if (propId === "textureOpacity") {
|
||||
this.setTextureOpacity(value);
|
||||
} else if (propId === "opacity") {
|
||||
this.setopacity(value);
|
||||
return true;
|
||||
} else if (propId === "uploadTexture") {
|
||||
// 触发上传纹理事件
|
||||
@@ -688,7 +699,7 @@ export class TextureBrush extends BaseBrush {
|
||||
case "scale":
|
||||
return this.setTextureScale(value);
|
||||
case "opacity":
|
||||
return this.setTextureOpacity(value);
|
||||
return this.setopacity(value);
|
||||
case "repeat":
|
||||
return this.setTextureRepeat(value);
|
||||
case "angle":
|
||||
@@ -708,7 +719,7 @@ export class TextureBrush extends BaseBrush {
|
||||
case "scale":
|
||||
return this.textureScale;
|
||||
case "opacity":
|
||||
return this.textureOpacity;
|
||||
return this.opacity;
|
||||
case "repeat":
|
||||
return this.textureRepeat;
|
||||
case "angle":
|
||||
@@ -751,7 +762,7 @@ export class TextureBrush extends BaseBrush {
|
||||
}
|
||||
|
||||
if (presetData.opacity !== undefined) {
|
||||
this.setTextureOpacity(presetData.opacity);
|
||||
this.setopacity(presetData.opacity);
|
||||
}
|
||||
|
||||
if (presetData.repeat !== undefined) {
|
||||
@@ -786,7 +797,7 @@ export class TextureBrush extends BaseBrush {
|
||||
return {
|
||||
textureId: this.selectedTextureId,
|
||||
scale: this.textureScale,
|
||||
opacity: this.textureOpacity,
|
||||
opacity: this.opacity,
|
||||
repeat: this.textureRepeat,
|
||||
angle: this.textureAngle,
|
||||
// 包含笔刷状态
|
||||
@@ -815,7 +826,7 @@ export class TextureBrush extends BaseBrush {
|
||||
}
|
||||
|
||||
if (state.opacity !== undefined) {
|
||||
this.setTextureOpacity(state.opacity);
|
||||
this.setopacity(state.opacity);
|
||||
}
|
||||
|
||||
if (state.repeat !== undefined) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { LiquifyWebGLManager } from "./LiquifyWebGLManager";
|
||||
import { LiquifyCPUManager } from "./LiquifyCPUManager";
|
||||
import { LayerType } from "../../utils/layerHelper";
|
||||
import { findInChildLayers, LayerType } from "../../utils/layerHelper";
|
||||
|
||||
export class EnhancedLiquifyManager {
|
||||
/**
|
||||
@@ -160,7 +160,10 @@ export class EnhancedLiquifyManager {
|
||||
} else if (typeof target === "object") {
|
||||
// 传入的是对象
|
||||
targetObject = target;
|
||||
const layer = this.layerManager.findLayerByObject(targetObject);
|
||||
const { layer } = findInChildLayers(
|
||||
this.layerManager.layers.value,
|
||||
targetObject.layerId
|
||||
);
|
||||
if (layer) {
|
||||
targetLayerId = layer.id;
|
||||
} else {
|
||||
@@ -632,16 +635,69 @@ export class EnhancedLiquifyManager {
|
||||
async _getImageData(fabricObject) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
// 创建临时canvas - 关键修复:使用原始图像尺寸,不考虑fabric对象的缩放
|
||||
console.log("开始获取图像数据,对象类型:", fabricObject.type);
|
||||
console.log("对象属性:", {
|
||||
width: fabricObject.width,
|
||||
height: fabricObject.height,
|
||||
hasElement: !!fabricObject._element,
|
||||
hasSrc: !!fabricObject.getSrc,
|
||||
});
|
||||
|
||||
// 检查基本属性
|
||||
if (!fabricObject.width || !fabricObject.height) {
|
||||
reject(new Error("图像对象缺少有效的宽度或高度"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建临时canvas - 使用原始图像尺寸,不考虑fabric对象的缩放
|
||||
const tempCanvas = document.createElement("canvas");
|
||||
// 使用图像的原始尺寸,而不是缩放后的尺寸
|
||||
tempCanvas.width = fabricObject.width;
|
||||
tempCanvas.height = fabricObject.height;
|
||||
const tempCtx = tempCanvas.getContext("2d");
|
||||
|
||||
// 如果对象有图像元素
|
||||
console.log(
|
||||
`创建临时Canvas,尺寸: ${tempCanvas.width}x${tempCanvas.height}`
|
||||
);
|
||||
|
||||
// 处理不同的图像源
|
||||
if (fabricObject._element) {
|
||||
// 绘制原始尺寸的图像
|
||||
console.log("使用 _element 绘制图像");
|
||||
|
||||
// 检查_element是否有效
|
||||
if (
|
||||
!fabricObject._element.complete &&
|
||||
fabricObject._element.tagName === "IMG"
|
||||
) {
|
||||
console.log("图像未加载完成,等待加载...");
|
||||
fabricObject._element.onload = () => {
|
||||
try {
|
||||
tempCtx.drawImage(
|
||||
fabricObject._element,
|
||||
0,
|
||||
0,
|
||||
fabricObject.width,
|
||||
fabricObject.height
|
||||
);
|
||||
const imageData = tempCtx.getImageData(
|
||||
0,
|
||||
0,
|
||||
tempCanvas.width,
|
||||
tempCanvas.height
|
||||
);
|
||||
console.log("✅ 图像加载完成后获取数据成功");
|
||||
resolve(imageData);
|
||||
} catch (error) {
|
||||
console.error("图像加载后绘制失败:", error);
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
fabricObject._element.onerror = () => {
|
||||
reject(new Error("图像加载失败"));
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接绘制已加载的图像
|
||||
tempCtx.drawImage(
|
||||
fabricObject._element,
|
||||
0,
|
||||
@@ -649,49 +705,124 @@ export class EnhancedLiquifyManager {
|
||||
fabricObject.width,
|
||||
fabricObject.height
|
||||
);
|
||||
} else if (fabricObject.getSrc) {
|
||||
} else if (
|
||||
fabricObject.getSrc &&
|
||||
typeof fabricObject.getSrc === "function"
|
||||
) {
|
||||
console.log("使用 getSrc() 方法获取图像源");
|
||||
|
||||
// 通过URL创建图像
|
||||
const img = new Image();
|
||||
img.crossOrigin = "anonymous"; // 避免跨域问题
|
||||
|
||||
img.onload = () => {
|
||||
tempCtx.drawImage(
|
||||
img,
|
||||
0,
|
||||
0,
|
||||
fabricObject.width,
|
||||
fabricObject.height
|
||||
);
|
||||
const imageData = tempCtx.getImageData(
|
||||
0,
|
||||
0,
|
||||
tempCanvas.width,
|
||||
tempCanvas.height
|
||||
);
|
||||
resolve(imageData);
|
||||
try {
|
||||
console.log(
|
||||
`图像加载成功,原始尺寸: ${img.naturalWidth}x${img.naturalHeight}`
|
||||
);
|
||||
|
||||
tempCtx.drawImage(
|
||||
img,
|
||||
0,
|
||||
0,
|
||||
fabricObject.width,
|
||||
fabricObject.height
|
||||
);
|
||||
|
||||
const imageData = tempCtx.getImageData(
|
||||
0,
|
||||
0,
|
||||
tempCanvas.width,
|
||||
tempCanvas.height
|
||||
);
|
||||
|
||||
console.log("✅ 通过URL获取图像数据成功");
|
||||
resolve(imageData);
|
||||
} catch (error) {
|
||||
console.error("绘制图像失败:", error);
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
img.onerror = reject;
|
||||
img.src = fabricObject.getSrc();
|
||||
|
||||
img.onerror = (error) => {
|
||||
console.error("图像加载失败:", error);
|
||||
reject(new Error("无法加载图像URL: " + fabricObject.getSrc()));
|
||||
};
|
||||
|
||||
const srcUrl = fabricObject.getSrc();
|
||||
console.log("加载图像URL:", srcUrl);
|
||||
img.src = srcUrl;
|
||||
return;
|
||||
} else if (fabricObject.src) {
|
||||
console.log("使用 src 属性获取图像源");
|
||||
|
||||
// 通过src属性创建图像
|
||||
const img = new Image();
|
||||
img.crossOrigin = "anonymous";
|
||||
|
||||
img.onload = () => {
|
||||
try {
|
||||
tempCtx.drawImage(
|
||||
img,
|
||||
0,
|
||||
0,
|
||||
fabricObject.width,
|
||||
fabricObject.height
|
||||
);
|
||||
|
||||
const imageData = tempCtx.getImageData(
|
||||
0,
|
||||
0,
|
||||
tempCanvas.width,
|
||||
tempCanvas.height
|
||||
);
|
||||
|
||||
console.log("✅ 通过src属性获取图像数据成功");
|
||||
resolve(imageData);
|
||||
} catch (error) {
|
||||
console.error("通过src绘制图像失败:", error);
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
img.onerror = (error) => {
|
||||
console.error("通过src加载图像失败:", error);
|
||||
reject(new Error("无法加载图像src: " + fabricObject.src));
|
||||
};
|
||||
|
||||
console.log("加载图像src:", fabricObject.src);
|
||||
img.src = fabricObject.src;
|
||||
return;
|
||||
} else {
|
||||
reject(new Error("无法获取图像数据"));
|
||||
console.error("无法找到有效的图像源");
|
||||
reject(
|
||||
new Error("图像对象缺少有效的图像源(_element, getSrc, 或 src)")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取图像数据
|
||||
const imageData = tempCtx.getImageData(
|
||||
0,
|
||||
0,
|
||||
tempCanvas.width,
|
||||
tempCanvas.height
|
||||
);
|
||||
// 如果走到这里,说明使用了_element直接绘制
|
||||
try {
|
||||
const imageData = tempCtx.getImageData(
|
||||
0,
|
||||
0,
|
||||
tempCanvas.width,
|
||||
tempCanvas.height
|
||||
);
|
||||
|
||||
console.log(
|
||||
`获取图像数据: 对象尺寸=${fabricObject.width}x${fabricObject.height}, ` +
|
||||
`对象缩放=(${fabricObject.scaleX}, ${fabricObject.scaleY}), ` +
|
||||
`图像数据尺寸=${imageData.width}x${imageData.height}`
|
||||
);
|
||||
console.log(
|
||||
`✅ 获取图像数据成功: 对象尺寸=${fabricObject.width}x${fabricObject.height}, ` +
|
||||
`对象缩放=(${fabricObject.scaleX}, ${fabricObject.scaleY}), ` +
|
||||
`图像数据尺寸=${imageData.width}x${imageData.height}`
|
||||
);
|
||||
|
||||
resolve(imageData);
|
||||
resolve(imageData);
|
||||
} catch (error) {
|
||||
console.error("获取ImageData失败:", error);
|
||||
reject(new Error("无法从Canvas获取图像数据: " + error.message));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("_getImageData 执行失败:", error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1361,6 +1361,9 @@ export class LiquifyCPUManager {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// 停止持续效果定时器
|
||||
this.stopContinuousEffect();
|
||||
|
||||
this.originalImageData = null;
|
||||
this.currentImageData = null;
|
||||
this.mesh = null;
|
||||
@@ -1389,4 +1392,11 @@ export class LiquifyCPUManager {
|
||||
this.accumulatedScale = 0;
|
||||
this.lastApplyTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放资源 - 别名方法,与其他管理器保持一致
|
||||
*/
|
||||
dispose() {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user