feat: 添加喷枪笔刷类型,支持颗粒感和纹理效果
This commit is contained in:
@@ -18,6 +18,7 @@ import { CustomPenBrush } from "./types/CustomPenBrush";
|
|||||||
import { RibbonBrush } from "./types/RibbonBrush";
|
import { RibbonBrush } from "./types/RibbonBrush";
|
||||||
import { ShadedBrush } from "./types/ShadedBrush";
|
import { ShadedBrush } from "./types/ShadedBrush";
|
||||||
import { EraserStateManager } from "../EraserStateManager";
|
import { EraserStateManager } from "../EraserStateManager";
|
||||||
|
import { SprayBrush } from "./types/SprayBrush";
|
||||||
// import { SketchyBrush } from "./types/SketchyBrush";
|
// import { SketchyBrush } from "./types/SketchyBrush";
|
||||||
// import { SpraypaintBrush } from "./types/SpraypaintBrush";
|
// import { SpraypaintBrush } from "./types/SpraypaintBrush";
|
||||||
|
|
||||||
@@ -116,6 +117,13 @@ export class BrushManager {
|
|||||||
description: "阴影笔刷,适合创建渐变和阴影效果",
|
description: "阴影笔刷,适合创建渐变和阴影效果",
|
||||||
category: "基础笔刷",
|
category: "基础笔刷",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
brushRegistry.register("spray", SprayBrush, {
|
||||||
|
name: "spray",
|
||||||
|
description: "模拟喷枪效果,创建散点效果",
|
||||||
|
category: "基础笔刷",
|
||||||
|
});
|
||||||
|
|
||||||
// brushRegistry.register("sketchy", SketchyBrush);
|
// brushRegistry.register("sketchy", SketchyBrush);
|
||||||
// brushRegistry.register("spraypaint", SpraypaintBrush, {
|
// brushRegistry.register("spraypaint", SpraypaintBrush, {
|
||||||
// name: "Spraypaint",
|
// name: "Spraypaint",
|
||||||
@@ -123,47 +131,47 @@ export class BrushManager {
|
|||||||
// category: "基础笔刷",
|
// category: "基础笔刷",
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// // 注册喷枪笔刷
|
// // // 注册喷枪笔刷
|
||||||
brushRegistry.register(
|
// brushRegistry.register(
|
||||||
"spray",
|
// "spray",
|
||||||
class SprayBrush extends PencilBrush {
|
// class SprayBrush extends PencilBrush {
|
||||||
constructor(canvas, options = {}) {
|
// constructor(canvas, options = {}) {
|
||||||
super(canvas, {
|
// super(canvas, {
|
||||||
id: "spray",
|
// id: "spray",
|
||||||
name: "Spray",
|
// name: "Spray",
|
||||||
description: "模拟喷枪效果,创建散点效果",
|
// description: "模拟喷枪效果,创建散点效果",
|
||||||
category: "基础笔刷",
|
// category: "基础笔刷",
|
||||||
...options,
|
// ...options,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
create() {
|
// create() {
|
||||||
this.brush = new fabric.SprayBrush(this.canvas);
|
// this.brush = new fabric.SprayBrush(this.canvas);
|
||||||
this.configure(this.brush, this.options);
|
// this.configure(this.brush, this.options);
|
||||||
return this.brush;
|
// return this.brush;
|
||||||
}
|
// }
|
||||||
|
|
||||||
configure(brush, options = {}) {
|
// configure(brush, options = {}) {
|
||||||
super.configure(brush, options);
|
// super.configure(brush, options);
|
||||||
|
|
||||||
if (options.density !== undefined) {
|
// if (options.density !== undefined) {
|
||||||
brush.density = options.density;
|
// brush.density = options.density;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (options.randomOpacity !== undefined) {
|
// if (options.randomOpacity !== undefined) {
|
||||||
brush.randomOpacity = options.randomOpacity;
|
// brush.randomOpacity = options.randomOpacity;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (options.dotWidth !== undefined) {
|
// if (options.dotWidth !== undefined) {
|
||||||
brush.dotWidth = options.dotWidth;
|
// brush.dotWidth = options.dotWidth;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
name: "喷枪",
|
// name: "喷枪",
|
||||||
description: "模拟喷枪效果,创建散点效果",
|
// description: "模拟喷枪效果,创建散点效果",
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
// 注册橡皮擦笔刷
|
// 注册橡皮擦笔刷
|
||||||
brushRegistry.register(
|
brushRegistry.register(
|
||||||
"eraser",
|
"eraser",
|
||||||
@@ -380,23 +388,27 @@ export class BrushManager {
|
|||||||
}
|
}
|
||||||
// 创建新笔刷实例
|
// 创建新笔刷实例
|
||||||
try {
|
try {
|
||||||
const brushInstance = brushRegistry.createBrushInstance(brushId, this.canvas, {
|
const brushInstance = brushRegistry.createBrushInstance(
|
||||||
color: brushId === "eraser" ? this.brushStore.state.color : undefined,
|
brushId,
|
||||||
width: this.brushStore.state.size,
|
this.canvas,
|
||||||
opacity: this.brushStore.state.opacity,
|
{
|
||||||
|
color: brushId === "eraser" ? this.brushStore.state.color : undefined,
|
||||||
|
width: this.brushStore.state.size,
|
||||||
|
opacity: this.brushStore.state.opacity,
|
||||||
|
|
||||||
// 阴影相关配置
|
// 阴影相关配置
|
||||||
shadowEnabled: this.brushStore.state.shadowEnabled,
|
shadowEnabled: this.brushStore.state.shadowEnabled,
|
||||||
shadowColor: this.brushStore.state.shadowColor,
|
shadowColor: this.brushStore.state.shadowColor,
|
||||||
shadowWidth: this.brushStore.state.shadowWidth,
|
shadowWidth: this.brushStore.state.shadowWidth,
|
||||||
shadowOffsetX: this.brushStore.state.shadowOffsetX,
|
shadowOffsetX: this.brushStore.state.shadowOffsetX,
|
||||||
shadowOffsetY: this.brushStore.state.shadowOffsetY,
|
shadowOffsetY: this.brushStore.state.shadowOffsetY,
|
||||||
|
|
||||||
// 材质笔刷特有配置
|
// 材质笔刷特有配置
|
||||||
textureEnabled: this.brushStore.state.textureEnabled,
|
textureEnabled: this.brushStore.state.textureEnabled,
|
||||||
texturePath: this.brushStore.state.texturePath,
|
texturePath: this.brushStore.state.texturePath,
|
||||||
textureScale: this.brushStore.state.textureScale,
|
textureScale: this.brushStore.state.textureScale,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (brushInstance) {
|
if (brushInstance) {
|
||||||
// 创建笔刷
|
// 创建笔刷
|
||||||
@@ -645,7 +657,10 @@ export class BrushManager {
|
|||||||
|
|
||||||
// 初始化橡皮擦状态管理器
|
// 初始化橡皮擦状态管理器
|
||||||
if (this.canvas && this.layerManager) {
|
if (this.canvas && this.layerManager) {
|
||||||
this.eraserStateManager = new EraserStateManager(this.canvas, this.layerManager);
|
this.eraserStateManager = new EraserStateManager(
|
||||||
|
this.canvas,
|
||||||
|
this.layerManager
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -885,7 +900,12 @@ export class BrushManager {
|
|||||||
const imageData = ctx.getImageData(pointer.x, pointer.y, 1, 1).data;
|
const imageData = ctx.getImageData(pointer.x, pointer.y, 1, 1).data;
|
||||||
|
|
||||||
// 将RGB转换为十六进制颜色
|
// 将RGB转换为十六进制颜色
|
||||||
const color = `#${((1 << 24) + (imageData[0] << 16) + (imageData[1] << 8) + imageData[2])
|
const color = `#${(
|
||||||
|
(1 << 24) +
|
||||||
|
(imageData[0] << 16) +
|
||||||
|
(imageData[1] << 8) +
|
||||||
|
imageData[2]
|
||||||
|
)
|
||||||
.toString(16)
|
.toString(16)
|
||||||
.slice(1)}`;
|
.slice(1)}`;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,246 @@
|
|||||||
|
import { BaseBrush } from "../BaseBrush";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 喷枪笔刷
|
||||||
|
* 模拟喷枪效果,具有颗粒感和纹理
|
||||||
|
*/
|
||||||
|
export class SprayBrush extends BaseBrush {
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param {Object} canvas fabric画布实例
|
||||||
|
* @param {Object} options 配置选项
|
||||||
|
*/
|
||||||
|
constructor(canvas, options = {}) {
|
||||||
|
super(canvas, {
|
||||||
|
id: "crayon",
|
||||||
|
name: "喷枪",
|
||||||
|
description: "模拟喷枪效果,具有颗粒感和纹理",
|
||||||
|
category: "特效笔刷",
|
||||||
|
icon: "crayon",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 喷枪笔刷特有属性
|
||||||
|
this._baseWidth = options._baseWidth || 15;
|
||||||
|
this._size = options._size || 0;
|
||||||
|
this._sep = options._sep || options._sep === 0 ? options._sep : 1;
|
||||||
|
this._inkAmount = options._inkAmount || 10;
|
||||||
|
this.randomness = options.randomness || 0.8; // 随机性
|
||||||
|
this.texture = options.texture || "default"; // 纹理类型
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建笔刷实例
|
||||||
|
* @returns {Object} fabric笔刷实例
|
||||||
|
*/
|
||||||
|
create() {
|
||||||
|
if (!this.canvas) {
|
||||||
|
throw new Error("画布实例不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建fabric原生喷枪笔刷
|
||||||
|
this.brush = new fabric.CrayonBrush(this.canvas);
|
||||||
|
|
||||||
|
// 配置笔刷
|
||||||
|
this.configure(this.brush, this.options);
|
||||||
|
|
||||||
|
return this.brush;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置笔刷
|
||||||
|
* @param {Object} brush fabric笔刷实例
|
||||||
|
* @param {Object} options 配置选项
|
||||||
|
*/
|
||||||
|
configure(brush, options = {}) {
|
||||||
|
if (!brush) return;
|
||||||
|
|
||||||
|
options._sep = options._sep || this._sep;
|
||||||
|
options._inkAmount = options._inkAmount || this._inkAmount;
|
||||||
|
|
||||||
|
// 基础属性配置
|
||||||
|
if (options.width !== undefined) {
|
||||||
|
brush.width = options.width;
|
||||||
|
// 更新笔刷相关属性
|
||||||
|
this._baseWidth = options.width / 2;
|
||||||
|
this._size = options.width / 2 + this._baseWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.color !== undefined) {
|
||||||
|
brush.color = options.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.opacity !== undefined) {
|
||||||
|
brush.opacity = options.opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 喷枪笔刷特有属性
|
||||||
|
if (options._baseWidth !== undefined) {
|
||||||
|
brush._baseWidth = options._baseWidth;
|
||||||
|
this._baseWidth = options._baseWidth;
|
||||||
|
this._size = this.width / 2 + this._baseWidth;
|
||||||
|
}
|
||||||
|
if (options._sep !== undefined) {
|
||||||
|
brush._sep = options._sep;
|
||||||
|
this._sep = options._sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options._inkAmount !== undefined) {
|
||||||
|
brush._inkAmount = options._inkAmount;
|
||||||
|
this._inkAmount = options._inkAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置颗粒分离度
|
||||||
|
* @param {Number} sep 分离度值
|
||||||
|
*/
|
||||||
|
setSeparation(sep) {
|
||||||
|
this._sep = Math.max(0.5, Math.min(10, sep));
|
||||||
|
|
||||||
|
if (this.brush) {
|
||||||
|
this.brush._sep = this._sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置墨量
|
||||||
|
* @param {Number} amount 墨量值
|
||||||
|
*/
|
||||||
|
setInkAmount(amount) {
|
||||||
|
this._inkAmount = Math.max(1, Math.min(50, amount));
|
||||||
|
|
||||||
|
if (this.brush) {
|
||||||
|
this.brush._inkAmount = this._inkAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._inkAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置随机性
|
||||||
|
* @param {Number} value 随机性值(0-1)
|
||||||
|
*/
|
||||||
|
setRandomness(value) {
|
||||||
|
this.randomness = Math.max(0, Math.min(1, value));
|
||||||
|
return this.randomness;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置纹理类型
|
||||||
|
* @param {String} type 纹理类型
|
||||||
|
*/
|
||||||
|
setTexture(type) {
|
||||||
|
this.texture = type;
|
||||||
|
// 实际应用可能需要更多的实现逻辑
|
||||||
|
return this.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取笔刷可配置属性
|
||||||
|
* @returns {Array} 可配置属性描述数组
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
getConfigurableProperties() {
|
||||||
|
// 获取基础属性
|
||||||
|
const baseProperties = super.getConfigurableProperties();
|
||||||
|
|
||||||
|
// 定义喷枪笔刷特有属性
|
||||||
|
const crayonProperties = [
|
||||||
|
{
|
||||||
|
id: "separation",
|
||||||
|
name: "颗粒分离度",
|
||||||
|
type: "slider",
|
||||||
|
defaultValue: this._sep,
|
||||||
|
min: 0.5,
|
||||||
|
max: 10,
|
||||||
|
step: 0.5,
|
||||||
|
description: "控制喷枪颗粒的分离程度",
|
||||||
|
category: "喷枪设置",
|
||||||
|
order: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "inkAmount",
|
||||||
|
name: "墨量",
|
||||||
|
type: "slider",
|
||||||
|
defaultValue: this._inkAmount,
|
||||||
|
min: 1,
|
||||||
|
max: 50,
|
||||||
|
step: 1,
|
||||||
|
description: "控制喷枪的颜料量",
|
||||||
|
category: "喷枪设置",
|
||||||
|
order: 110,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "randomness",
|
||||||
|
name: "随机性",
|
||||||
|
type: "slider",
|
||||||
|
defaultValue: this.randomness,
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
step: 0.05,
|
||||||
|
description: "控制喷枪纹理的随机程度",
|
||||||
|
category: "喷枪设置",
|
||||||
|
order: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "texture",
|
||||||
|
name: "纹理类型",
|
||||||
|
type: "select",
|
||||||
|
defaultValue: this.texture,
|
||||||
|
options: [
|
||||||
|
{ value: "default", label: "默认" },
|
||||||
|
{ value: "rough", label: "粗糙" },
|
||||||
|
{ value: "smooth", label: "平滑" },
|
||||||
|
],
|
||||||
|
description: "设置喷枪的纹理类型",
|
||||||
|
category: "喷枪设置",
|
||||||
|
order: 130,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 合并并返回所有属性
|
||||||
|
return [...baseProperties, ...crayonProperties];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新笔刷属性
|
||||||
|
* @param {String} propId 属性ID
|
||||||
|
* @param {any} value 属性值
|
||||||
|
* @returns {Boolean} 是否更新成功
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
updateProperty(propId, value) {
|
||||||
|
// 先检查基类能否处理此属性
|
||||||
|
if (super.updateProperty(propId, value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理喷枪笔刷特有属性
|
||||||
|
if (propId === "separation") {
|
||||||
|
this.setSeparation(value);
|
||||||
|
return true;
|
||||||
|
} else if (propId === "inkAmount") {
|
||||||
|
this.setInkAmount(value);
|
||||||
|
return true;
|
||||||
|
} else if (propId === "randomness") {
|
||||||
|
this.setRandomness(value);
|
||||||
|
return true;
|
||||||
|
} else if (propId === "texture") {
|
||||||
|
this.setTexture(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取预览图
|
||||||
|
* @returns {String} 预览图URL
|
||||||
|
*/
|
||||||
|
getPreview() {
|
||||||
|
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cmVjdCB4PSIxMCIgeT0iMTAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiLz48cmVjdCB4PSIyMCIgeT0iMjAiIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiLz48cmVjdCB4PSIzMCIgeT0iMzAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4=";
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user