合并画布代码

This commit is contained in:
X1627315083
2025-06-18 11:05:23 +08:00
parent 903c0ebdf5
commit 9c7fae36eb
118 changed files with 23633 additions and 8201 deletions

View File

@@ -577,6 +577,49 @@ export class TexturePresetManager {
cacheSize: this.textureCache.size,
};
}
/**
* 验证纹理文件
* @param {File} file 要验证的文件
* @returns {Boolean} 是否为有效的纹理文件
*/
validateTextureFile(file) {
if (!file) {
console.warn("文件不存在");
return false;
}
// 检查文件类型
if (!file.type.startsWith("image/")) {
console.warn("文件类型无效,必须是图片文件");
return false;
}
// 检查文件大小(限制为 10MB
const maxSize = 10 * 1024 * 1024; // 10MB
if (file.size > maxSize) {
console.warn("文件大小超过限制10MB");
return false;
}
// 检查支持的图片格式
const supportedTypes = [
"image/jpeg",
"image/jpg",
"image/png",
"image/webp",
"image/svg+xml",
"image/bmp",
"image/gif",
];
if (!supportedTypes.includes(file.type)) {
console.warn("不支持的图片格式");
return false;
}
return true;
}
}
// 创建单例实例

View File

@@ -1,4 +1,5 @@
//import { fabric } from "fabric-with-all";
import { fabric } from "fabric-with-all";
import "./fabric.brushes.js";
import { BrushStore } from "../../store/BrushStore";
import { brushRegistry } from "./BrushRegistry";
@@ -16,7 +17,7 @@ import { MarkerBrush } from "./types/MarkerBrush";
import { CustomPenBrush } from "./types/CustomPenBrush";
import { RibbonBrush } from "./types/RibbonBrush";
import { ShadedBrush } from "./types/ShadedBrush";
import { SketchyBrush } from "./types/SketchyBrush";
// import { SketchyBrush } from "./types/SketchyBrush";
import { SpraypaintBrush } from "./types/SpraypaintBrush";
/**
@@ -42,6 +43,11 @@ export class BrushManager {
// 初始化笔刷注册
this._registerDefaultBrushes();
// 初始化橡皮擦状态管理器
this.eraserStateManager = null;
this.isErasingActive = false;
this.currentErasedObjects = []; // 当前擦除会话中被影响的对象
}
/**
@@ -51,64 +57,112 @@ export class BrushManager {
_registerDefaultBrushes() {
// 注册铅笔笔刷
brushRegistry.register("pencil", PencilBrush, {
name: "铅笔",
name: "Pencil",
description: "基础铅笔工具,适合精细线条绘制",
category: "基础笔刷",
});
// 注册材质笔刷
brushRegistry.register("texture", TextureBrush);
brushRegistry.register("texture", TextureBrush, {
name: "Texture",
description: "使用纹理图片作为笔刷,支持缩放和透明度",
category: "基础笔刷",
});
// 注册集成的笔刷类型
brushRegistry.register("crayon", CrayonBrush);
brushRegistry.register("fur", FurBrush);
brushRegistry.register("ink", InkBrush);
brushRegistry.register("longfur", LongfurBrush);
brushRegistry.register("writing", WritingBrush);
brushRegistry.register("marker", MarkerBrush);
brushRegistry.register("pen", CustomPenBrush);
brushRegistry.register("ribbon", RibbonBrush);
brushRegistry.register("shaded", ShadedBrush);
brushRegistry.register("sketchy", SketchyBrush);
brushRegistry.register("spraypaint", SpraypaintBrush);
brushRegistry.register("crayon", CrayonBrush, {
name: "Crayon",
description: "使用纹理图片作为笔刷,支持缩放和透明度",
category: "基础笔刷",
});
brushRegistry.register("fur", FurBrush, {
name: "Texture",
description: "使用纹理图片作为笔刷,支持缩放和透明度",
category: "基础笔刷",
});
brushRegistry.register("ink", InkBrush, {
name: "Ink",
description: "墨水笔刷,适合书写和绘图",
category: "基础笔刷",
});
brushRegistry.register("", LongfurBrush, {
name: "Longfur",
description: "长毛发笔刷,适合绘制动物毛皮、草或头发",
category: "基础笔刷",
});
brushRegistry.register("writing", WritingBrush, {
name: "Writing",
description: "书法笔刷,模拟中国传统书法毛笔效果,具有笔锋和墨色变化",
category: "基础笔刷",
});
brushRegistry.register("marker", MarkerBrush, {
name: "Marker",
description: "马克笔笔刷,适合粗线条和填充",
category: "基础笔刷",
});
brushRegistry.register("pen", CustomPenBrush, {
name: "Pen",
description: "自定义钢笔笔刷,适合书写和绘图",
category: "基础笔刷",
});
brushRegistry.register("ribbon", RibbonBrush, {
name: "Ribbon",
description: "丝带笔刷,适合创建流动的丝带效果",
category: "基础笔刷",
});
brushRegistry.register("shaded", ShadedBrush, {
name: "Shaded",
description: "阴影笔刷,适合创建渐变和阴影效果",
category: "基础笔刷",
});
// brushRegistry.register("sketchy", SketchyBrush);
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: "喷枪",
// 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;
}
}
}
);
// if (options.dotWidth !== undefined) {
// brush.dotWidth = options.dotWidth;
// }
// }
// },
// {
// name: "喷枪",
// description: "模拟喷枪效果,创建散点效果",
// }
// );
// 注册橡皮擦笔刷
brushRegistry.register(
"eraser",
@@ -187,87 +241,87 @@ export class BrushManager {
}
);
// 注册水彩笔刷
brushRegistry.register(
"watercolor",
class WatercolorBrush extends PencilBrush {
constructor(canvas, options = {}) {
super(canvas, {
id: "watercolor",
name: "水彩",
description: "模拟水彩效果,带有流动感和透明感",
category: "特效笔刷",
...options,
});
}
// // 注册水彩笔刷
// brushRegistry.register(
// "watercolor",
// class WatercolorBrush extends PencilBrush {
// constructor(canvas, options = {}) {
// super(canvas, {
// id: "watercolor",
// name: "水彩",
// description: "模拟水彩效果,带有流动感和透明感",
// category: "特效笔刷",
// ...options,
// });
// }
create() {
// 创建一个自定义的PencilBrush来模拟水彩效果
this.brush = new fabric.PencilBrush(this.canvas);
this.configure(this.brush, this.options);
// create() {
// // 创建一个自定义的PencilBrush来模拟水彩效果
// this.brush = new fabric.PencilBrush(this.canvas);
// this.configure(this.brush, this.options);
// 水彩效果特有的属性
this.brush.globalCompositeOperation = "multiply";
this.brush.shadow = new fabric.Shadow({
color: this.options.color || "#000",
blur: 5,
offsetX: 0,
offsetY: 0,
});
// // 水彩效果特有的属性
// this.brush.globalCompositeOperation = "multiply";
// this.brush.shadow = new fabric.Shadow({
// color: this.options.color || "#000",
// blur: 5,
// offsetX: 0,
// offsetY: 0,
// });
return this.brush;
}
// return this.brush;
// }
configure(brush, options = {}) {
super.configure(brush, options);
// configure(brush, options = {}) {
// super.configure(brush, options);
// 水彩笔刷特有的配置
brush.opacity = Math.min(0.5, options.opacity || 0.3); // 默认透明度30%
}
}
);
// // 水彩笔刷特有的配置
// brush.opacity = Math.min(0.5, options.opacity || 0.3); // 默认透明度30%
// }
// }
// );
// 注册粉笔笔刷
brushRegistry.register(
"chalk",
class ChalkBrush extends PencilBrush {
constructor(canvas, options = {}) {
super(canvas, {
id: "chalk",
name: "粉笔",
description: "模拟粉笔效果,有颗粒感和不连续性",
category: "特效笔刷",
...options,
});
}
// // 注册粉笔笔刷
// brushRegistry.register(
// "chalk",
// class ChalkBrush extends PencilBrush {
// constructor(canvas, options = {}) {
// super(canvas, {
// id: "chalk",
// name: "粉笔",
// description: "模拟粉笔效果,有颗粒感和不连续性",
// category: "特效笔刷",
// ...options,
// });
// }
create() {
this.brush = new fabric.PencilBrush(this.canvas);
this.configure(this.brush, this.options);
// create() {
// this.brush = new fabric.PencilBrush(this.canvas);
// this.configure(this.brush, this.options);
// 自定义绘画方法来模拟粉笔效果
const originalOnMouseMove = this.brush.onMouseMove;
this.brush.onMouseMove = function (pointer, options) {
// 随机调整坐标位置,增加粉笔质感
const jitter = 2;
pointer.x += (Math.random() - 0.5) * jitter;
pointer.y += (Math.random() - 0.5) * jitter;
// // 自定义绘画方法来模拟粉笔效果
// const originalOnMouseMove = this.brush.onMouseMove;
// this.brush.onMouseMove = function (pointer, options) {
// // 随机调整坐标位置,增加粉笔质感
// const jitter = 2;
// pointer.x += (Math.random() - 0.5) * jitter;
// pointer.y += (Math.random() - 0.5) * jitter;
// 调用原始方法
originalOnMouseMove.call(this, pointer, options);
};
// // 调用原始方法
// originalOnMouseMove.call(this, pointer, options);
// };
return this.brush;
}
// return this.brush;
// }
configure(brush, options = {}) {
super.configure(brush, options);
// configure(brush, options = {}) {
// super.configure(brush, options);
// 粉笔特有的设置
brush.strokeDashArray = [5, 5]; // 虚线效果
}
}
);
// // 粉笔特有的设置
// brush.strokeDashArray = [5, 5]; // 虚线效果
// }
// }
// );
}
/**
@@ -571,6 +625,148 @@ export class BrushManager {
return success;
}
/**
* 设置依赖管理器
*/
setManagers({ layerManager, commandManager }) {
this.layerManager = layerManager;
this.commandManager = commandManager;
// 初始化橡皮擦状态管理器
if (this.canvas && this.layerManager) {
this.eraserStateManager = new EraserStateManager(
this.canvas,
this.layerManager
);
}
}
/**
* 创建橡皮擦
* @param {Object} options 橡皮擦选项
*/
createEraser(options = {}) {
if (!this.canvas) {
console.error("画布未初始化");
return null;
}
try {
// 直接使用 fabric-with-erasing 库提供的 EraserBrush
this.brush = new fabric.EraserBrush(this.canvas);
// 应用配置
this.configure(this.brush, {
width: this.brushSize.value,
color: this.brushColor.value,
opacity: this.brushOpacity.value,
inverted: options.inverted || false,
...options,
});
// 设置画布为绘图模式
this.canvas.isDrawingMode = true;
this.canvas.freeDrawingBrush = this.brush;
// 绑定橡皮擦事件处理器
// this._bindEraserEvents();
console.log("橡皮擦创建成功");
return this.brush;
} catch (error) {
console.error("创建橡皮擦失败:", error);
return null;
}
}
/**
* 绑定橡皮擦事件处理器
* @private
*/
_bindEraserEvents() {
if (!this.canvas || !this.eraserStateManager) return;
// 监听擦除开始事件
this.canvas.on("erasing:start", this._handleErasingStart.bind(this));
// 监听擦除结束事件
this.canvas.on("erasing:end", this._handleErasingEnd.bind(this));
}
/**
* 解绑橡皮擦事件处理器
* @private
*/
_unbindEraserEvents() {
if (!this.canvas) return;
this.canvas.off("erasing:start", this._handleErasingStart.bind(this));
this.canvas.off("erasing:end", this._handleErasingEnd.bind(this));
}
/**
* 处理擦除开始事件
* @param {Object} e 事件对象
* @private
*/
_handleErasingStart(e) {
console.log("橡皮擦开始擦除");
if (!this.eraserStateManager) return;
// 标记擦除状态
this.isErasingActive = true;
this.currentErasedObjects = [];
// 捕获擦除前的状态
this.eraserStateManager.startErasing();
}
/**
* 处理擦除结束事件
* @param {Object} e 事件对象
* @private
*/
_handleErasingEnd(e) {
console.log("橡皮擦擦除结束", e);
if (!this.eraserStateManager || !this.isErasingActive) return;
// 获取被擦除的对象
const affectedObjects = e.targets || [];
// 创建橡皮擦命令
const eraserCommand = this.eraserStateManager.endErasing(affectedObjects);
// 如果有有效的命令且有命令管理器,执行命令
if (eraserCommand && this.commandManager) {
// 注意:不需要调用 execute(),因为擦除操作已经完成
// 只需要将命令添加到历史记录中以支持撤销
this.commandManager.addToHistory(eraserCommand);
console.log("橡皮擦操作已添加到命令历史");
}
// 重置状态
this.isErasingActive = false;
this.currentErasedObjects = [];
}
/**
* 取消当前擦除操作
*/
cancelErasing() {
if (!this.isErasingActive) return;
if (this.eraserStateManager) {
this.eraserStateManager.cancelErasing();
}
this.isErasingActive = false;
this.currentErasedObjects = [];
console.log("当前擦除操作已取消");
}
/**
* 获取当前笔刷类型
* @returns {String} 当前笔刷类型ID
@@ -646,7 +842,6 @@ export class BrushManager {
return this.canvas.freeDrawingBrush;
}
/**
* 创建橡皮擦
* @returns {Object} 橡皮擦笔刷
@@ -703,9 +898,20 @@ export class BrushManager {
}
/**
* 销毁资源
* 销毁笔刷管理器
*/
dispose() {
// 解绑事件
// this._unbindEraserEvents();
// 取消进行中的擦除操作
this.cancelErasing();
// 清理状态管理器
if (this.eraserStateManager) {
this.eraserStateManager = null;
}
// 销毁当前笔刷
if (this.activeBrush) {
this.activeBrush.destroy();

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
import { BaseBrush } from "../BaseBrush";
//import { fabric } from "fabric-with-all";
import { fabric } from "fabric-with-all";
import texturePresetManager from "../TexturePresetManager";
/**
@@ -71,7 +71,72 @@ export class TextureBrush extends BaseBrush {
// 创建fabric原生纹理笔刷
this.brush = new fabric.PatternBrush(this.canvas);
// 配置笔刷
// 重写 _finalizeAndAddPath 方法,使其调用 convertToImg 而不是创建 Path 对象
const originalFinalizeAndAddPath = this.brush._finalizeAndAddPath.bind(
this.brush
);
const self = this; // 保存外部this引用
this.brush._finalizeAndAddPath = function () {
console.log("TextureBrush: _finalizeAndAddPath called");
const ctx = this.canvas.contextTop;
ctx.closePath();
// 应用点简化如果PatternBrush支持
if (this.decimate && this._points) {
this._points = this.decimatePoints(this._points, this.decimate);
}
console.log(
"TextureBrush: points count =",
this._points ? this._points.length : 0
);
// 检查是否有有效的路径数据
if (!this._points || this._points.length < 2) {
// 如果点数不足,直接请求重新渲染
console.log("TextureBrush: Not enough points, skipping");
this.canvas.requestRenderAll();
return;
}
const pathData = this.convertPointsToSVGPath(this._points);
const isEmpty = self._isEmptySVGPath(pathData);
console.log("TextureBrush: isEmpty =", isEmpty);
if (isEmpty) {
// 如果路径为空,直接请求重新渲染
console.log("TextureBrush: Path is empty, skipping");
this.canvas.requestRenderAll();
return;
}
// 先触发事件,模拟原生行为
const path = this.createPath(pathData);
this.canvas.fire("before:path:created", { path: path });
console.log("TextureBrush: Calling convertToImg");
// 调用 convertToImg 方法将绘制内容转换为图片
if (typeof this.convertToImg === "function") {
this.convertToImg();
console.log("TextureBrush: convertToImg called successfully");
} else {
console.warn(
"convertToImg method not found, falling back to original behavior"
);
// 如果没有convertToImg方法回退到原始行为
this.canvas.add(path);
this.canvas.fire("path:created", { path: path });
this.canvas.clearContext(this.canvas.contextTop);
}
// 重置阴影
this._resetShadow();
};
// 配置笔刷基本属性
this.configure(this.brush, this.options);
// 如果有选中的材质,则设置纹理
@@ -221,6 +286,11 @@ export class TextureBrush extends BaseBrush {
canvasTexture.width = width;
canvasTexture.height = height;
// 应用透明度设置
if (this.textureOpacity < 1) {
ctx.globalAlpha = this.textureOpacity;
}
// 绘制前应用旋转
if (this.textureAngle !== 0) {
ctx.save();
@@ -233,26 +303,16 @@ export class TextureBrush extends BaseBrush {
ctx.drawImage(img, 0, 0, width, height);
}
// 应用透明度
if (this.textureOpacity < 1) {
ctx.globalAlpha = this.textureOpacity;
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
}
// 缓存处理后的纹理图像
this._processedTextureCanvas = canvasTexture;
// 创建Pattern对象
const pattern = new fabric.Pattern({
source: canvasTexture,
repeat: this.textureRepeat,
});
// 使用fabric.js 5.x正确的API方式设置PatternBrush
// 直接设置source属性这是PatternBrush的标准用法
this.brush.source = canvasTexture;
// 设置笔刷源纹理
if (typeof this.brush.setSource === "function") {
this.brush.setSource(pattern);
} else if (typeof this.brush.source === "object") {
this.brush.source = pattern;
} else if (typeof this.brush.pattern === "object") {
this.brush.pattern = pattern;
// 通知画布重绘
if (this.canvas && this.canvas.requestRenderAll) {
this.canvas.requestRenderAll();
}
}
@@ -852,4 +912,37 @@ export class TextureBrush extends BaseBrush {
getTextureStats() {
return texturePresetManager.getStats();
}
/**
* 检查 SVG 路径是否为空
* @private
* @param {Array} pathData SVG 路径数据
* @returns {Boolean} 是否为空路径
*/
_isEmptySVGPath(pathData) {
if (!pathData || pathData.length === 0) {
return true;
}
// 检查路径是否只包含移动命令或者是一个点
let hasDrawing = false;
let moveCount = 0;
for (let i = 0; i < pathData.length; i++) {
const command = pathData[i];
if (command[0] === "M") {
moveCount++;
} else if (
command[0] === "L" ||
command[0] === "Q" ||
command[0] === "C"
) {
hasDrawing = true;
break;
}
}
// 如果只有移动命令且超过1个或者没有绘制命令则认为是空路径
return !hasDrawing || (moveCount > 0 && pathData.length <= moveCount);
}
}