接入画布

This commit is contained in:
X1627315083
2025-06-09 10:25:54 +08:00
parent 87a08f5f8f
commit c266967f16
157 changed files with 43833 additions and 1571 deletions

View File

@@ -0,0 +1,234 @@
/**
* 笔刷基类
* 所有笔刷类型应继承此基类并实现必要的方法
*/
export class BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 笔刷配置选项
*/
constructor(canvas, options = {}) {
this.canvas = canvas;
this.options = options;
// 基本属性
this.id = options.id || this.constructor.name;
this.name = options.name || "未命名笔刷";
this.description = options.description || "";
this.icon = options.icon || null;
this.category = options.category || "默认";
// 笔刷实例
this.brush = null;
}
/**
* 创建笔刷实例(必须由子类实现)
* @returns {Object} fabric笔刷实例
*/
create() {
throw new Error("必须由子类实现create方法");
}
/**
* 配置笔刷(必须由子类实现)
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options) {
throw new Error("必须由子类实现configure方法");
}
/**
* 获取笔刷的元数据
* @returns {Object} 笔刷元数据
*/
getMetadata() {
return {
id: this.id,
name: this.name,
description: this.description,
icon: this.icon,
category: this.category,
};
}
/**
* 获取笔刷预览
* @returns {String|null} 预览图URL或null
*/
getPreview() {
return null;
}
/**
* 获取笔刷可配置属性
* 这个方法返回一个对象数组,每个对象描述一个可配置属性
* 每个属性对象包含:
* - id: 属性标识符
* - name: 属性显示名称
* - type: 属性类型(例如:'slider', 'color', 'checkbox', 'select'
* - defaultValue: 默认值
* - min/max/step: 对于slider类型的限制值
* - options: 对于select类型的选项
* - description: 属性描述
* - category: 属性分类
* - order: 显示顺序(越小越靠前)
* - visibleWhen: 函数或对象,定义何时显示该属性
* - dynamicOptions: 函数,返回动态的选项列表
* @returns {Array} 可配置属性描述数组
*/
getConfigurableProperties() {
// 返回基础属性,所有笔刷都有这些属性
return [
{
id: "size",
name: "笔刷大小",
type: "slider",
defaultValue: 5,
min: 0.5,
max: 100,
step: 0.5,
description: "笔刷的大小(像素)",
category: "基本",
order: 10,
},
{
id: "color",
name: "笔刷颜色",
type: "color",
defaultValue: "#000000",
description: "笔刷的颜色",
category: "基本",
order: 20,
},
{
id: "opacity",
name: "不透明度",
type: "slider",
defaultValue: 1,
min: 0.05,
max: 1,
step: 0.01,
description: "笔刷的不透明度",
category: "基本",
order: 30,
},
];
}
/**
* 合并特有属性与基本属性
* 子类应该调用此方法来合并自身特有属性与基类提供的基本属性
* @param {Array} specificProperties 特有属性数组
* @returns {Array} 合并后的属性数组
*/
mergeWithBaseProperties(specificProperties) {
const baseProperties = super.getConfigurableProperties();
// 过滤掉同名属性(子类优先)
const basePropsFiltered = baseProperties.filter(
(baseProp) =>
!specificProperties.some(
(specificProp) => specificProp.id === baseProp.id
)
);
return [...basePropsFiltered, ...specificProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
*/
updateProperty(propId, value) {
// 基础实现,可以被子类覆盖以处理特殊属性
if (propId === "size") {
if (this.brush) {
this.brush.width = value;
return true;
}
} else if (propId === "color") {
if (this.brush) {
this.brush.color = value;
return true;
}
} else if (propId === "opacity") {
if (this.brush) {
this.brush.opacity = value;
return true;
}
}
return false;
}
/**
* 检查属性是否可见
* @param {Object} property 属性对象
* @param {Object} currentValues 当前所有属性的值
* @returns {Boolean} 是否可见
*/
isPropertyVisible(property, currentValues) {
// 如果没有visibleWhen条件则始终显示
if (!property.visibleWhen) {
return true;
}
// 如果visibleWhen是函数则调用函数判断
if (typeof property.visibleWhen === "function") {
return property.visibleWhen(currentValues);
}
// 如果visibleWhen是对象检查条件是否满足
if (typeof property.visibleWhen === "object") {
for (const [key, value] of Object.entries(property.visibleWhen)) {
if (currentValues[key] !== value) {
return false;
}
}
return true;
}
return true;
}
/**
* 获取动态选项
* @param {Object} property 属性对象
* @param {Object} currentValues 当前所有属性的值
* @returns {Array} 选项数组
*/
getDynamicOptions(property, currentValues) {
if (
property.dynamicOptions &&
typeof property.dynamicOptions === "function"
) {
return property.dynamicOptions(currentValues);
}
return property.options || [];
}
/**
* 生命周期方法:笔刷被选中
*/
onSelected() {
// 可由子类覆盖
}
/**
* 生命周期方法:笔刷被取消选中
*/
onDeselected() {
// 可由子类覆盖
}
/**
* 销毁笔刷实例并清理资源
*/
destroy() {
this.brush = null;
}
}

View File

@@ -0,0 +1,201 @@
/**
* 笔刷注册表
* 用于注册、获取和管理所有笔刷
*/
export class BrushRegistry {
constructor() {
// 存储所有注册的笔刷类
this.brushes = new Map();
// 按类别组织的笔刷
this.brushesByCategory = new Map();
// 事件监听器
this.listeners = {
register: [],
unregister: [],
};
}
/**
* 注册一个笔刷
* @param {String} id 笔刷唯一标识
* @param {Class} brushClass 笔刷类需要继承BaseBrush
* @param {Object} metadata 笔刷元数据(可选)
* @returns {Boolean} 是否注册成功
*/
register(id, brushClass, metadata = {}) {
if (this.brushes.has(id)) {
console.warn(`笔刷 ${id} 已存在请使用其他ID`);
return false;
}
// 存储笔刷信息
const brushInfo = {
id,
class: brushClass,
metadata: {
...metadata,
id,
},
};
this.brushes.set(id, brushInfo);
// 添加到分类
const category = metadata.category || "默认";
if (!this.brushesByCategory.has(category)) {
this.brushesByCategory.set(category, []);
}
this.brushesByCategory.get(category).push(brushInfo);
// 触发事件
this._triggerEvent("register", brushInfo);
return true;
}
/**
* 取消注册笔刷
* @param {String} id 笔刷ID
* @returns {Boolean} 是否成功
*/
unregister(id) {
if (!this.brushes.has(id)) {
return false;
}
const brushInfo = this.brushes.get(id);
this.brushes.delete(id);
// 从分类中移除
const category = brushInfo.metadata.category || "默认";
if (this.brushesByCategory.has(category)) {
const brushes = this.brushesByCategory.get(category);
const index = brushes.findIndex((b) => b.id === id);
if (index !== -1) {
brushes.splice(index, 1);
}
// 如果分类为空,删除该分类
if (brushes.length === 0) {
this.brushesByCategory.delete(category);
}
}
// 触发事件
this._triggerEvent("unregister", brushInfo);
return true;
}
/**
* 获取笔刷信息
* @param {String} id 笔刷ID
* @returns {Object|null} 笔刷信息或null
*/
getBrush(id) {
return this.brushes.get(id) || null;
}
/**
* 获取所有笔刷
* @returns {Array} 笔刷信息数组
*/
getAllBrushes() {
return Array.from(this.brushes.values());
}
/**
* 获取指定分类的笔刷
* @param {String} category 分类名称
* @returns {Array} 笔刷信息数组
*/
getBrushesByCategory(category) {
return this.brushesByCategory.get(category) || [];
}
/**
* 获取所有分类
* @returns {Array} 分类名称数组
*/
getCategories() {
return Array.from(this.brushesByCategory.keys());
}
/**
* 创建一个笔刷实例
* @param {String} id 笔刷ID
* @param {Object} canvas 画布实例
* @param {Object} options 配置选项
* @returns {Object|null} 笔刷实例或null
*/
createBrushInstance(id, canvas, options = {}) {
const brushInfo = this.getBrush(id);
if (!brushInfo) {
console.error(`笔刷 ${id} 不存在`);
return null;
}
try {
// 创建笔刷实例
return new brushInfo.class(canvas, {
...options,
id: brushInfo.id,
...brushInfo.metadata,
});
} catch (error) {
console.error(`创建笔刷 ${id} 失败:`, error);
return null;
}
}
/**
* 添加事件监听器
* @param {String} event 事件名称 ('register'|'unregister')
* @param {Function} callback 回调函数
*/
addEventListener(event, callback) {
if (this.listeners[event]) {
this.listeners[event].push(callback);
}
}
/**
* 移除事件监听器
* @param {String} event 事件名称
* @param {Function} callback 回调函数
*/
removeEventListener(event, callback) {
if (this.listeners[event]) {
const index = this.listeners[event].indexOf(callback);
if (index !== -1) {
this.listeners[event].splice(index, 1);
}
}
}
/**
* 触发事件
* @param {String} event 事件名称
* @param {*} data 事件数据
* @private
*/
_triggerEvent(event, data) {
if (this.listeners[event]) {
this.listeners[event].forEach((callback) => {
try {
callback(data);
} catch (error) {
console.error(`执行 ${event} 事件监听器出错:`, error);
}
});
}
}
}
// 导出单例实例
export const brushRegistry = new BrushRegistry();
// 默认导出单例
export default brushRegistry;

View File

@@ -0,0 +1,586 @@
/**
* 材质预设管理器
* 负责管理所有材质预设,包括内置预设和用户自定义预设
*/
export class TexturePresetManager {
constructor() {
// 内置材质预设
this.builtInTextures = [];
// 用户自定义材质
this.customTextures = [];
// 材质分类
this.categories = new Map();
// 材质缓存
this.textureCache = new Map();
// 事件监听器
this.listeners = {
textureAdded: [],
textureRemoved: [],
textureUpdated: [],
};
// 初始化内置材质
this._initBuiltInTextures();
}
/**
* 初始化内置材质预设
* @private
*/
_initBuiltInTextures() {
// 基于项目中的texture文件夹内容创建预设
const textureList = [
// 基础纹理
{
id: "texture0",
name: "纸质纹理",
category: "基础纹理",
path: "/src/assets/texture/texture0.webp",
},
{
id: "texture1",
name: "粗糙表面",
category: "基础纹理",
path: "/src/assets/texture/texture1.webp",
},
{
id: "texture2",
name: "细腻纹理",
category: "基础纹理",
path: "/src/assets/texture/texture2.webp",
},
{
id: "texture3",
name: "颗粒质感",
category: "基础纹理",
path: "/src/assets/texture/texture3.webp",
},
{
id: "texture4",
name: "布料纹理",
category: "基础纹理",
path: "/src/assets/texture/texture4.webp",
},
{
id: "texture5",
name: "木质纹理",
category: "自然纹理",
path: "/src/assets/texture/texture5.webp",
},
{
id: "texture6",
name: "石材纹理",
category: "自然纹理",
path: "/src/assets/texture/texture6.webp",
},
{
id: "texture7",
name: "金属质感",
category: "金属纹理",
path: "/src/assets/texture/texture7.webp",
},
{
id: "texture8",
name: "皮革纹理",
category: "自然纹理",
path: "/src/assets/texture/texture8.webp",
},
{
id: "texture9",
name: "水彩纸质",
category: "艺术纹理",
path: "/src/assets/texture/texture9.webp",
},
{
id: "texture10",
name: "画布纹理",
category: "艺术纹理",
path: "/src/assets/texture/texture10.webp",
},
{
id: "texture11",
name: "沙砾质感",
category: "自然纹理",
path: "/src/assets/texture/texture11.webp",
},
{
id: "texture12",
name: "水波纹理",
category: "自然纹理",
path: "/src/assets/texture/texture12.webp",
},
{
id: "texture13",
name: "云朵纹理",
category: "自然纹理",
path: "/src/assets/texture/texture13.webp",
},
{
id: "texture14",
name: "火焰纹理",
category: "特效纹理",
path: "/src/assets/texture/texture14.webp",
},
{
id: "texture15",
name: "烟雾效果",
category: "特效纹理",
path: "/src/assets/texture/texture15.webp",
},
{
id: "texture16",
name: "星空纹理",
category: "特效纹理",
path: "/src/assets/texture/texture16.webp",
},
{
id: "texture17",
name: "大理石纹",
category: "石材纹理",
path: "/src/assets/texture/texture17.webp",
},
{
id: "texture18",
name: "花岗岩纹",
category: "石材纹理",
path: "/src/assets/texture/texture18.webp",
},
{
id: "texture19",
name: "竹纹理",
category: "自然纹理",
path: "/src/assets/texture/texture19.webp",
},
{
id: "texture20",
name: "抽象图案",
category: "艺术纹理",
path: "/src/assets/texture/texture20.webp",
},
];
// 添加内置材质
textureList.forEach((texture) => {
this.builtInTextures.push({
id: texture.id,
name: texture.name,
category: texture.category,
path: texture.path,
type: "builtin",
preview: texture.path, // 使用原图作为预览
description: `内置${texture.category} - ${texture.name}`,
tags: [texture.category.replace("纹理", ""), "内置"],
created: new Date().toISOString(),
// 默认属性
defaultSettings: {
scale: 1,
opacity: 1,
repeat: "repeat",
angle: 0,
},
});
// 添加到分类
if (!this.categories.has(texture.category)) {
this.categories.set(texture.category, []);
}
this.categories.get(texture.category).push(texture.id);
});
}
/**
* 获取所有材质(内置 + 自定义)
* @returns {Array} 材质数组
*/
getAllTextures() {
return [...this.builtInTextures, ...this.customTextures];
}
/**
* 根据ID获取材质
* @param {String} textureId 材质ID
* @returns {Object|null} 材质对象
*/
getTextureById(textureId) {
return (
this.getAllTextures().find((texture) => texture.id === textureId) || null
);
}
/**
* 根据分类获取材质
* @param {String} category 分类名称
* @returns {Array} 材质数组
*/
getTexturesByCategory(category) {
return this.getAllTextures().filter(
(texture) => texture.category === category
);
}
/**
* 获取所有分类
* @returns {Array} 分类名称数组
*/
getCategories() {
const categories = new Set();
this.getAllTextures().forEach((texture) => {
categories.add(texture.category);
});
return Array.from(categories);
}
/**
* 添加自定义材质
* @param {Object} textureData 材质数据
* @returns {String} 材质ID
*/
addCustomTexture(textureData) {
const textureId =
textureData.id ||
`custom_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const texture = {
id: textureId,
name: textureData.name || "自定义材质",
category: textureData.category || "自定义材质",
path: textureData.path || textureData.dataUrl,
type: "custom",
preview: textureData.preview || textureData.path || textureData.dataUrl,
description: textureData.description || "用户自定义材质",
tags: textureData.tags || ["自定义"],
created: new Date().toISOString(),
defaultSettings: {
scale: textureData.scale || 1,
opacity: textureData.opacity || 1,
repeat: textureData.repeat || "repeat",
angle: textureData.angle || 0,
...textureData.defaultSettings,
},
// 保存原始文件信息
file: textureData.file || null,
dataUrl: textureData.dataUrl || null,
};
this.customTextures.push(texture);
// 添加到分类
if (!this.categories.has(texture.category)) {
this.categories.set(texture.category, []);
}
this.categories.get(texture.category).push(textureId);
// 触发事件
this._triggerEvent("textureAdded", texture);
return textureId;
}
/**
* 删除自定义材质
* @param {String} textureId 材质ID
* @returns {Boolean} 是否删除成功
*/
removeCustomTexture(textureId) {
const index = this.customTextures.findIndex(
(texture) => texture.id === textureId
);
if (index === -1) {
return false;
}
const texture = this.customTextures[index];
// 只能删除自定义材质
if (texture.type !== "custom") {
console.warn("不能删除内置材质");
return false;
}
this.customTextures.splice(index, 1);
// 从分类中移除
if (this.categories.has(texture.category)) {
const categoryTextures = this.categories.get(texture.category);
const categoryIndex = categoryTextures.indexOf(textureId);
if (categoryIndex !== -1) {
categoryTextures.splice(categoryIndex, 1);
}
// 如果分类为空且不是内置分类,删除分类
if (categoryTextures.length === 0 && texture.category === "自定义材质") {
this.categories.delete(texture.category);
}
}
// 清除缓存
this.textureCache.delete(textureId);
// 触发事件
this._triggerEvent("textureRemoved", texture);
return true;
}
/**
* 更新材质信息
* @param {String} textureId 材质ID
* @param {Object} updates 更新数据
* @returns {Boolean} 是否更新成功
*/
updateTexture(textureId, updates) {
const texture = this.getTextureById(textureId);
if (!texture || texture.type === "builtin") {
return false;
}
// 更新材质属性
Object.assign(texture, updates);
// 触发事件
this._triggerEvent("textureUpdated", texture);
return true;
}
/**
* 获取材质预览URL
* @param {Object} texture 材质对象
* @returns {String} 预览URL
*/
getTexturePreviewUrl(texture) {
if (!texture) return null;
// 如果有预览图,使用预览图
if (texture.preview) {
return texture.preview;
}
// 否则使用原图
return texture.path || texture.dataUrl;
}
/**
* 加载材质图像
* @param {String} textureId 材质ID
* @returns {Promise<HTMLImageElement>} 图像对象
*/
loadTextureImage(textureId) {
return new Promise((resolve, reject) => {
// 检查缓存
if (this.textureCache.has(textureId)) {
resolve(this.textureCache.get(textureId));
return;
}
const texture = this.getTextureById(textureId);
if (!texture) {
reject(new Error(`材质 ${textureId} 不存在`));
return;
}
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = () => {
// 缓存图像
this.textureCache.set(textureId, img);
resolve(img);
};
img.onerror = () => {
reject(new Error(`材质 ${textureId} 加载失败`));
};
img.src = texture.path || texture.dataUrl;
});
}
/**
* 搜索材质
* @param {String} query 搜索关键词
* @returns {Array} 匹配的材质数组
*/
searchTextures(query) {
if (!query) return this.getAllTextures();
const searchTerm = query.toLowerCase();
return this.getAllTextures().filter((texture) => {
return (
texture.name.toLowerCase().includes(searchTerm) ||
texture.category.toLowerCase().includes(searchTerm) ||
texture.description.toLowerCase().includes(searchTerm) ||
texture.tags.some((tag) => tag.toLowerCase().includes(searchTerm))
);
});
}
/**
* 保存自定义材质到本地存储
*/
saveCustomTexturesToStorage() {
try {
const customTexturesData = this.customTextures.map((texture) => ({
...texture,
// 不保存file对象到localStorage
file: null,
}));
localStorage.setItem(
"canvasEditor_customTextures",
JSON.stringify(customTexturesData)
);
} catch (error) {
console.error("保存自定义材质失败:", error);
}
}
/**
* 从本地存储加载自定义材质
*/
loadCustomTexturesFromStorage() {
try {
const stored = localStorage.getItem("canvasEditor_customTextures");
if (stored) {
const customTexturesData = JSON.parse(stored);
this.customTextures = customTexturesData;
// 重建分类索引
this.customTextures.forEach((texture) => {
if (!this.categories.has(texture.category)) {
this.categories.set(texture.category, []);
}
if (!this.categories.get(texture.category).includes(texture.id)) {
this.categories.get(texture.category).push(texture.id);
}
});
}
} catch (error) {
console.error("加载自定义材质失败:", error);
this.customTextures = [];
}
}
/**
* 创建材质预设
* @param {String} name 预设名称
* @param {Object} settings 材质设置
* @returns {String} 预设ID
*/
createTexturePreset(name, settings) {
const presetId = `preset_${Date.now()}_${Math.random()
.toString(36)
.substr(2, 9)}`;
const preset = {
id: presetId,
name: name,
type: "preset",
category: "材质预设",
created: new Date().toISOString(),
settings: {
textureId: settings.textureId,
scale: settings.scale || 1,
opacity: settings.opacity || 1,
repeat: settings.repeat || "repeat",
angle: settings.angle || 0,
brushSize: settings.brushSize || 5,
brushOpacity: settings.brushOpacity || 1,
brushColor: settings.brushColor || "#000000",
},
};
this.customTextures.push(preset);
this._triggerEvent("textureAdded", preset);
return presetId;
}
/**
* 应用材质预设
* @param {String} presetId 预设ID
* @returns {Object|null} 预设设置
*/
applyTexturePreset(presetId) {
const preset = this.getTextureById(presetId);
if (!preset || preset.type !== "preset") {
return null;
}
return preset.settings;
}
/**
* 添加事件监听器
* @param {String} event 事件名称
* @param {Function} callback 回调函数
*/
addEventListener(event, callback) {
if (this.listeners[event]) {
this.listeners[event].push(callback);
}
}
/**
* 移除事件监听器
* @param {String} event 事件名称
* @param {Function} callback 回调函数
*/
removeEventListener(event, callback) {
if (this.listeners[event]) {
const index = this.listeners[event].indexOf(callback);
if (index !== -1) {
this.listeners[event].splice(index, 1);
}
}
}
/**
* 触发事件
* @param {String} event 事件名称
* @param {*} data 事件数据
* @private
*/
_triggerEvent(event, data) {
if (this.listeners[event]) {
this.listeners[event].forEach((callback) => {
try {
callback(data);
} catch (error) {
console.error(`执行 ${event} 事件监听器出错:`, error);
}
});
}
}
/**
* 清除所有缓存
*/
clearCache() {
this.textureCache.clear();
}
/**
* 获取统计信息
* @returns {Object} 统计信息
*/
getStats() {
return {
builtInCount: this.builtInTextures.length,
customCount: this.customTextures.length,
totalCount: this.getAllTextures().length,
categoriesCount: this.getCategories().length,
cacheSize: this.textureCache.size,
};
}
}
// 创建单例实例
const texturePresetManager = new TexturePresetManager();
// 导出单例
export default texturePresetManager;

View File

@@ -0,0 +1,720 @@
//import { fabric } from "fabric-with-all";
import { BrushStore } from "../../store/BrushStore";
import { brushRegistry } from "./BrushRegistry";
// 导入基础笔刷类型
import { PencilBrush } from "./types/PencilBrush";
import { TextureBrush } from "./types/TextureBrush";
// 导入集成的笔刷类型
import { CrayonBrush } from "./types/CrayonBrush";
import { FurBrush } from "./types/FurBrush";
import { InkBrush } from "./types/InkBrush";
import { LongfurBrush } from "./types/LongfurBrush";
import { WritingBrush } from "./types/WritingBrush";
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 { SpraypaintBrush } from "./types/SpraypaintBrush";
/**
* 笔刷管理器
* 负责管理和切换不同的笔刷类型
*/
export class BrushManager {
/**
* 构造函数
* @param {Object} options 配置选项
* @param {Object} options.canvas fabric.js画布实例
* @param {Object} options.brushStore 笔刷数据存储(可选)
* @param {Object} options.layerManager 图层管理器实例(可选)
*/
constructor(options = {}) {
this.canvas = options.canvas;
this.brushStore = options.brushStore || BrushStore;
this.layerManager = options.layerManager; // 添加图层管理器引用
// 当前活动笔刷
this.activeBrush = null;
this.activeBrushId = null;
// 初始化笔刷注册
this._registerDefaultBrushes();
}
/**
* 注册默认笔刷
* @private
*/
_registerDefaultBrushes() {
// 注册铅笔笔刷
brushRegistry.register("pencil", PencilBrush, {
name: "铅笔",
description: "基础铅笔工具,适合精细线条绘制",
category: "基础笔刷",
});
// 注册材质笔刷
brushRegistry.register("texture", TextureBrush);
// 注册集成的笔刷类型
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(
"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;
}
configure(brush, options = {}) {
super.configure(brush, options);
if (options.density !== undefined) {
brush.density = options.density;
}
if (options.randomOpacity !== undefined) {
brush.randomOpacity = options.randomOpacity;
}
if (options.dotWidth !== undefined) {
brush.dotWidth = options.dotWidth;
}
}
}
);
// 注册橡皮擦笔刷
brushRegistry.register(
"eraser",
class EraserBrush extends PencilBrush {
constructor(canvas, options = {}) {
super(canvas, {
id: "eraser",
name: "橡皮擦",
description: "擦除已绘制的内容",
category: "工具",
...options,
});
}
create() {
// 直接使用 fabric-with-erasing 库提供的 EraserBrush
this.brush = new fabric.EraserBrush(this.canvas);
this.configure(this.brush, this.options);
// 配置橡皮擦特有属性
this.brush.inverted = this.options.inverted || false; // 是否反向擦除(恢复擦除)
return this.brush;
}
configure(brush, options = {}) {
super.configure(brush, options);
// 橡皮擦特有配置
if (options.inverted !== undefined) {
brush.inverted = options.inverted;
}
}
/**
* 设置反向擦除模式
* @param {Boolean} inverted 是否启用反向擦除(撤销擦除效果)
*/
setInverted(inverted) {
if (this.brush) {
this.brush.inverted = inverted;
}
}
/**
* 获取橡皮擦配置属性
* @returns {Array} 可配置属性数组
*/
getConfigurableProperties() {
return [
...super.getConfigurableProperties(),
{
id: "inverted",
name: "反向擦除",
type: "boolean",
description: "启用时可以恢复已擦除的内容",
defaultValue: false,
min: null,
max: null,
},
];
}
/**
* 更新橡皮擦属性
* @param {String} propId 属性ID
* @param {any} value 属性值
*/
updateProperty(propId, value) {
if (propId === "inverted") {
this.setInverted(value);
} else {
super.updateProperty(propId, value);
}
}
}
);
// 注册水彩笔刷
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);
// 水彩效果特有的属性
this.brush.globalCompositeOperation = "multiply";
this.brush.shadow = new fabric.Shadow({
color: this.options.color || "#000",
blur: 5,
offsetX: 0,
offsetY: 0,
});
return this.brush;
}
configure(brush, options = {}) {
super.configure(brush, options);
// 水彩笔刷特有的配置
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,
});
}
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;
// 调用原始方法
originalOnMouseMove.call(this, pointer, options);
};
return this.brush;
}
configure(brush, options = {}) {
super.configure(brush, options);
// 粉笔特有的设置
brush.strokeDashArray = [5, 5]; // 虚线效果
}
}
);
}
/**
* 获取所有可用笔刷类型
* @returns {Array} 笔刷类型数组包含id、name和description
*/
getBrushTypes() {
// 从注册表获取所有笔刷信息
const brushes = brushRegistry.getAllBrushes();
// 将笔刷信息转换为期望的格式
return brushes.map((brushInfo) => ({
id: brushInfo.id,
name: brushInfo.metadata.name || brushInfo.id,
description: brushInfo.metadata.description || "",
category: brushInfo.metadata.category || "默认",
icon: brushInfo.metadata.icon || null,
}));
}
/**
* 初始化笔刷列表并更新BrushStore
*/
initializeBrushes() {
// 获取所有笔刷
const allBrushes = this.getBrushTypes();
// 更新BrushStore中的可用笔刷列表
this.brushStore.setAvailableBrushes(allBrushes);
// 设置默认笔刷
if (!this.activeBrushId && allBrushes.length > 0) {
this.setBrushType(allBrushes[0].id);
}
}
/**
* 设置笔刷类型
* @param {String} brushId 笔刷ID
* @returns {Object|null} 设置的笔刷实例
*/
setBrushType(brushId) {
// 如果相同笔刷,不做处理
if (this.activeBrushId === brushId) {
return this.activeBrush;
}
// 销毁当前笔刷
if (this.activeBrush) {
// 调用生命周期方法
if (this.activeBrush.onDeselected) {
this.activeBrush.onDeselected();
}
this.activeBrush.destroy();
}
// 创建新笔刷实例
try {
const brushInstance = brushRegistry.createBrushInstance(
brushId,
this.canvas,
{
color: brushId === "eraser" ? this.brushStore.state.color : undefined,
width: this.brushStore.state.size,
opacity: this.brushStore.state.opacity,
// 材质笔刷特有配置
textureEnabled: this.brushStore.state.textureEnabled,
texturePath: this.brushStore.state.texturePath,
textureScale: this.brushStore.state.textureScale,
}
);
if (brushInstance) {
// 创建笔刷
const fabricBrush = brushInstance.create();
// 更新画布的当前笔刷
if (fabricBrush) {
this.canvas.freeDrawingBrush = fabricBrush;
}
// 更新当前笔刷引用
this.activeBrush = brushInstance;
this.activeBrushId = brushId;
// 调用生命周期方法
if (this.activeBrush.onSelected) {
this.activeBrush.onSelected();
}
// 更新Store的笔刷类型
this.brushStore.setBrushType(brushId);
// 更新Store的当前笔刷实例用于动态属性系统
this.brushStore.setCurrentBrushInstance(brushInstance);
return brushInstance;
}
} catch (error) {
console.error(`创建笔刷 ${brushId} 失败:`, error);
}
return null;
}
/**
* 设置笔刷颜色
* @param {String} color 十六进制颜色值
*/
setBrushColor(color) {
if (!this.canvas.freeDrawingBrush) return;
// 更新笔刷颜色
this.canvas.freeDrawingBrush.color = color;
// 更新活动笔刷
if (this.activeBrush) {
this.activeBrush.configure(this.canvas.freeDrawingBrush, { color });
}
// 更新Store
this.brushStore.setBrushColor(color);
}
/**
* 设置笔刷大小
* @param {Number} size 笔刷大小
*/
setBrushSize(size) {
if (!this.canvas.freeDrawingBrush) return;
// 限制大小范围
const brushSize = Math.max(0.1, Math.min(100, size));
// 更新笔刷大小
this.canvas.freeDrawingBrush.width = brushSize;
// 更新活动笔刷
if (this.activeBrush) {
this.activeBrush.configure(this.canvas.freeDrawingBrush, {
width: brushSize,
});
}
// 更新Store
this.brushStore.setBrushSize(brushSize);
return brushSize;
}
/**
* 增加笔刷大小
* @param {Number} amount 增加量
* @returns {Number} 新的笔刷大小
*/
increaseBrushSize(amount = 1) {
const currentSize = this.brushStore.state.size;
return this.setBrushSize(currentSize + amount);
}
/**
* 减少笔刷大小
* @param {Number} amount 减少量
* @returns {Number} 新的笔刷大小
*/
decreaseBrushSize(amount = 1) {
const currentSize = this.brushStore.state.size;
return this.setBrushSize(currentSize - amount);
}
/**
* 增加笔刷透明度
* @param {Number} amount 增加量
* @returns {Number} 新的笔刷大小
*/
increaseBrushOpacity(amount = 0.01) {
const currentSize = this.brushStore.state.opacity;
return this.setBrushOpacity(currentSize + amount);
}
/**
* 减少笔刷大小
* @param {Number} amount 减少量
* @returns {Number} 新的笔刷大小
*/
decreaseBrushOpacity(amount = 0.01) {
const currentSize = this.brushStore.state.opacity;
return this.setBrushOpacity(currentSize - amount);
}
/**
* 设置笔刷透明度
* @param {Number} opacity 透明度 (0-1)
*/
setBrushOpacity(opacity) {
if (!this.canvas.freeDrawingBrush) return;
// 限制透明度范围
const brushOpacity = Math.max(0.05, Math.min(1, opacity));
// 更新笔刷透明度
this.canvas.freeDrawingBrush.opacity = brushOpacity;
// 更新活动笔刷
if (this.activeBrush) {
this.activeBrush.configure(this.canvas.freeDrawingBrush, {
opacity: brushOpacity,
});
}
// 更新Store
this.brushStore.setBrushOpacity(brushOpacity);
return brushOpacity;
}
/**
* 设置材质缩放
* @param {Number} scale 缩放比例
*/
setTextureScale(scale) {
// 限制缩放范围
const textureScale = Math.max(0.1, Math.min(10, scale));
// 更新活动笔刷
if (this.activeBrush && this.activeBrush.setTextureScale) {
this.activeBrush.setTextureScale(textureScale);
}
// 更新Store
this.brushStore.setTextureScale(textureScale);
return textureScale;
}
/**
* 增加材质缩放
* @param {Number} amount 增加量
*/
increaseTextureScale(amount = 0.1) {
const currentScale = this.brushStore.state.textureScale;
return this.setTextureScale(currentScale + amount);
}
/**
* 减少材质缩放
* @param {Number} amount 减少量
*/
decreaseTextureScale(amount = 0.1) {
const currentScale = this.brushStore.state.textureScale;
return this.setTextureScale(currentScale - amount);
}
/**
* 设置材质路径
* @param {String} path 材质图片路径
*/
setTexturePath(path) {
// 更新活动笔刷
if (this.activeBrush && this.activeBrush.setTexturePath) {
this.activeBrush.setTexturePath(path);
}
// 更新Store
this.brushStore.setTexturePath(path);
return path;
}
/**
* 启用/禁用材质
* @param {Boolean} enabled 是否启用
*/
setTextureEnabled(enabled) {
// 更新Store
this.brushStore.setTextureEnabled(enabled);
// 如果启用材质,且当前不是材质笔刷,需要切换
if (enabled && this.activeBrushId !== "texture") {
this.setBrushType("texture");
}
return enabled;
}
/**
* 注册新笔刷
* @param {String} id 笔刷ID
* @param {Class} brushClass 笔刷类
* @param {Object} metadata 笔刷元数据
* @returns {Boolean} 是否注册成功
*/
registerBrush(id, brushClass, metadata = {}) {
const success = brushRegistry.register(id, brushClass, metadata);
if (success) {
// 更新可用笔刷列表
this.initializeBrushes();
}
return success;
}
/**
* 获取当前笔刷类型
* @returns {String} 当前笔刷类型ID
*/
getCurrentBrushType() {
return this.activeBrushId;
}
/**
* 获取当前笔刷大小
* @returns {Number} 当前笔刷大小
*/
getBrushSize() {
return this.brushStore.state.size;
}
/**
* 获取当前笔刷颜色
* @returns {String} 当前笔刷颜色
*/
getBrushColor() {
return this.brushStore.state.color;
}
/**
* 获取当前笔刷透明度
* @returns {Number} 当前笔刷透明度
*/
getBrushOpacity() {
return this.brushStore.state.opacity;
}
/**
* 获取材质缩放
* @returns {Number} 材质缩放比例
*/
getTextureScale() {
return this.brushStore.state.textureScale;
}
/**
* 创建材质笔刷
* 这个方法保留用于向下兼容
* @deprecated 请使用setBrushType('texture')代替
* @returns {Object} 材质笔刷实例
*/
createTextureBrush() {
console.warn('createTextureBrush方法已废弃请使用setBrushType("texture")');
return this.setBrushType("texture");
}
/**
* 更新笔刷
* 根据当前设置应用笔刷属性到画布
*/
updateBrush() {
if (!this.canvas) return;
// 如果有活动的笔刷实例,重新配置它
if (this.activeBrush && this.canvas.freeDrawingBrush) {
this.activeBrush.configure(this.canvas.freeDrawingBrush, {
color: this.brushStore.state.color,
width: this.brushStore.state.size,
opacity: this.brushStore.state.opacity,
});
} else {
// 如果没有活动笔刷,创建一个默认的
this.setBrushType(this.activeBrushId || "pencil");
}
// 更新画布状态
this?.canvas?.renderAll?.();
return this.canvas.freeDrawingBrush;
}
/**
* 创建橡皮擦
* @returns {Object} 橡皮擦笔刷
*/
createEraser() {
return this.setBrushType("eraser");
}
/**
* 创建吸色工具
* @param {Function} callback 选择颜色后的回调函数
*/
createEyedropper(callback) {
// 保存当前状态
const previousBrushId = this.activeBrushId;
// 一次性事件处理程序
const handleMouseDown = (event) => {
const pointer = this.canvas.getPointer(event.e);
const ctx = this.canvas.getContext();
// 获取点击位置的像素
const imageData = ctx.getImageData(pointer.x, pointer.y, 1, 1).data;
// 将RGB转换为十六进制颜色
const color = `#${(
(1 << 24) +
(imageData[0] << 16) +
(imageData[1] << 8) +
imageData[2]
)
.toString(16)
.slice(1)}`;
// 调用回调函数
if (typeof callback === "function") {
callback(color);
}
// 恢复之前的笔刷
this.setBrushType(previousBrushId);
// 移除事件监听器
this.canvas.off("mouse:down", handleMouseDown);
};
// 添加事件监听器
this.canvas.on("mouse:down", handleMouseDown);
// 设置吸色光标
this.canvas.defaultCursor = "crosshair";
console.log("吸色工具已激活,点击画布选择颜色");
}
/**
* 销毁资源
*/
dispose() {
// 销毁当前笔刷
if (this.activeBrush) {
this.activeBrush.destroy();
this.activeBrush = null;
}
this.canvas = null;
}
}
// 导出单例
export default BrushManager;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,244 @@
import { BaseBrush } from "../BaseBrush";
/**
* 蜡笔笔刷
* 模拟蜡笔效果,具有颗粒感和纹理
*/
export class CrayonBrush 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 : 3;
this._inkAmount = options._inkAmount || 10;
this.randomness = options.randomness || 0.5; // 随机性
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;
// 基础属性配置
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=";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,196 @@
import { BaseBrush } from "../BaseBrush";
/**
* 钢笔笔刷
* 模拟钢笔效果,具有变化的透明度
*/
export class CustomPenBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "pen",
name: "钢笔",
description: "模拟钢笔效果,具有变化的透明度",
category: "基础笔刷",
icon: "pen",
...options,
});
// 钢笔笔刷特有属性
this._baseWidth = options._baseWidth || 15;
this._lineWidth = options._lineWidth || 2;
this.inkOpacityMin = options.inkOpacityMin || 0.2;
this.inkOpacityMax = options.inkOpacityMax || 0.6;
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生钢笔笔刷
this.brush = new fabric.PenBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
// 更新笔刷相关属性
this._baseWidth = options.width / 2;
}
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;
}
if (options._lineWidth !== undefined) {
brush._lineWidth = options._lineWidth;
this._lineWidth = options._lineWidth;
}
// 确保线条连接设置正确
brush.canvas.contextTop.lineJoin = "round";
brush.canvas.contextTop.lineCap = "round";
}
/**
* 设置最小墨水透明度
* @param {Number} opacity 透明度值(0-1)
*/
setInkOpacityMin(opacity) {
this.inkOpacityMin = Math.max(0.1, Math.min(0.5, opacity));
return this.inkOpacityMin;
}
/**
* 设置最大墨水透明度
* @param {Number} opacity 透明度值(0-1)
*/
setInkOpacityMax(opacity) {
this.inkOpacityMax = Math.max(0.3, Math.min(1, opacity));
return this.inkOpacityMax;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义钢笔笔刷特有属性
const penProperties = [
{
id: "lineWidth",
name: "线条宽度",
type: "slider",
defaultValue: this._lineWidth,
min: 1,
max: 10,
step: 0.5,
description: "控制钢笔线条的宽度",
category: "钢笔设置",
order: 100,
},
{
id: "inkOpacityMin",
name: "最小墨水透明度",
type: "slider",
defaultValue: this.inkOpacityMin,
min: 0.1,
max: 0.5,
step: 0.05,
description: "控制钢笔墨水最小透明度",
category: "钢笔设置",
order: 110,
},
{
id: "inkOpacityMax",
name: "最大墨水透明度",
type: "slider",
defaultValue: this.inkOpacityMax,
min: 0.3,
max: 1,
step: 0.05,
description: "控制钢笔墨水最大透明度",
category: "钢笔设置",
order: 120,
},
];
// 合并并返回所有属性
return [...baseProperties, ...penProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理钢笔笔刷特有属性
if (propId === "lineWidth") {
this._lineWidth = value;
if (this.brush) {
this.brush._lineWidth = value;
}
return true;
} else if (propId === "inkOpacityMin") {
this.setInkOpacityMin(value);
return true;
} else if (propId === "inkOpacityMax") {
this.setInkOpacityMax(value);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNMjAgMjBMODAgODBNMjAgODBMODAgMjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=";
}
}

View File

@@ -0,0 +1,201 @@
import { BaseBrush } from "../BaseBrush";
/**
* 毛发笔刷
* 创建类似于毛发或草的效果
*/
export class FurBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "fur",
name: "毛发笔刷",
description: "创建类似于毛发或草的纹理效果",
category: "特效笔刷",
icon: "fur",
...options,
});
// 毛发笔刷特有属性
this.furLength = options.furLength || 10;
this.furDensity = options.furDensity || 0.7;
this.furRandomness = options.furRandomness || 0.5;
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生毛发笔刷
this.brush = new fabric.FurBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 这里可以添加对毛发笔刷特有属性的配置
// 由于fabric.FurBrush的原始实现可能没有直接暴露这些属性
// 我们可能需要在onMouseMove等事件中动态调整行为
// 存储特有属性,供后续使用
if (options.furLength !== undefined) {
this.furLength = options.furLength;
}
if (options.furDensity !== undefined) {
this.furDensity = options.furDensity;
}
if (options.furRandomness !== undefined) {
this.furRandomness = options.furRandomness;
}
}
/**
* 设置毛发长度
* @param {Number} length 长度值
*/
setFurLength(length) {
this.furLength = Math.max(1, Math.min(50, length));
return this.furLength;
}
/**
* 设置毛发密度
* @param {Number} density 密度值(0-1)
*/
setFurDensity(density) {
this.furDensity = Math.max(0.1, Math.min(1, density));
return this.furDensity;
}
/**
* 设置毛发随机性
* @param {Number} randomness 随机性值(0-1)
*/
setFurRandomness(randomness) {
this.furRandomness = Math.max(0, Math.min(1, randomness));
return this.furRandomness;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义毛发笔刷特有属性
const furProperties = [
{
id: "furLength",
name: "毛发长度",
type: "slider",
defaultValue: this.furLength,
min: 1,
max: 50,
step: 1,
description: "控制毛发的长度",
category: "毛发设置",
order: 100,
},
{
id: "furDensity",
name: "毛发密度",
type: "slider",
defaultValue: this.furDensity,
min: 0.1,
max: 1,
step: 0.05,
description: "控制毛发的密度",
category: "毛发设置",
order: 110,
},
{
id: "furRandomness",
name: "随机性",
type: "slider",
defaultValue: this.furRandomness,
min: 0,
max: 1,
step: 0.05,
description: "控制毛发的随机分布程度",
category: "毛发设置",
order: 120,
},
];
// 合并并返回所有属性
return [...baseProperties, ...furProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理毛发笔刷特有属性
if (propId === "furLength") {
this.setFurLength(value);
return true;
} else if (propId === "furDensity") {
this.setFurDensity(value);
return true;
} else if (propId === "furRandomness") {
this.setFurRandomness(value);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNMTAgODBMNTAgMjBNMjAgODBMNjAgMjBNMzAgODBMNzAgMjBNNDAgODBMODAgMjBNNTAgODBMOTAgMjAiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIi8+PC9zdmc+";
}
}

View File

@@ -0,0 +1,267 @@
import { BaseBrush } from "../BaseBrush";
/**
* 水墨笔刷
* 模拟中国传统水墨画效果
*/
export class InkBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "ink",
name: "水墨笔刷",
description: "模拟中国传统水墨画效果,墨色深浅不一",
category: "特效笔刷",
icon: "ink",
...options,
});
// 水墨笔刷特有属性
this._baseWidth = options._baseWidth || 15;
this._inkAmount = options._inkAmount || 7;
this._range = options._range || 10;
this.splashEnabled =
options.splashEnabled !== undefined ? options.splashEnabled : true;
this.splashSize = options.splashSize || 5;
this.splashDistance = options.splashDistance || 30;
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生水墨笔刷
this.brush = new fabric.InkBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
this._baseWidth = options.width / 2;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 水墨笔刷特有属性
if (options._inkAmount !== undefined) {
brush._inkAmount = options._inkAmount;
this._inkAmount = options._inkAmount;
}
if (options._range !== undefined) {
brush._range = options._range;
this._range = options._range;
}
// 更新溅墨相关配置这需要修改原始InkBrush的drawSplash方法
if (this.splashEnabled !== undefined) {
// 由于原始InkBrush没有直接暴露这个配置我们可能需要覆盖方法
// 这里仅保存配置实际逻辑需要在brush创建后处理
}
}
/**
* 设置墨量
* @param {Number} amount 墨量值
*/
setInkAmount(amount) {
this._inkAmount = Math.max(1, Math.min(20, amount));
if (this.brush) {
this.brush._inkAmount = this._inkAmount;
}
return this._inkAmount;
}
/**
* 设置笔触范围
* @param {Number} range 范围值
*/
setRange(range) {
this._range = Math.max(5, Math.min(50, range));
if (this.brush) {
this.brush._range = this._range;
}
return this._range;
}
/**
* 启用/禁用溅墨效果
* @param {Boolean} enabled 是否启用
*/
setSplashEnabled(enabled) {
this.splashEnabled = enabled;
// 实际应用需要更多的逻辑来支持这个功能
// 由于需要修改fabric.InkBrush的内部行为
return this.splashEnabled;
}
/**
* 设置溅墨大小
* @param {Number} size 大小值
*/
setSplashSize(size) {
this.splashSize = Math.max(1, Math.min(20, size));
return this.splashSize;
}
/**
* 设置溅墨距离
* @param {Number} distance 距离值
*/
setSplashDistance(distance) {
this.splashDistance = Math.max(10, Math.min(100, distance));
return this.splashDistance;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义水墨笔刷特有属性
const inkProperties = [
{
id: "inkAmount",
name: "墨量",
type: "slider",
defaultValue: this._inkAmount,
min: 1,
max: 20,
step: 1,
description: "控制水墨的浓度",
category: "水墨设置",
order: 100,
},
{
id: "range",
name: "笔触范围",
type: "slider",
defaultValue: this._range,
min: 5,
max: 50,
step: 1,
description: "控制水墨扩散的范围",
category: "水墨设置",
order: 110,
},
{
id: "splashEnabled",
name: "溅墨效果",
type: "checkbox",
defaultValue: this.splashEnabled,
description: "是否启用溅墨效果",
category: "水墨设置",
order: 120,
},
{
id: "splashSize",
name: "溅墨大小",
type: "slider",
defaultValue: this.splashSize,
min: 1,
max: 20,
step: 1,
description: "溅墨点的大小",
category: "水墨设置",
order: 130,
visibleWhen: { splashEnabled: true },
},
{
id: "splashDistance",
name: "溅墨距离",
type: "slider",
defaultValue: this.splashDistance,
min: 10,
max: 100,
step: 5,
description: "溅墨可扩散的最大距离",
category: "水墨设置",
order: 140,
visibleWhen: { splashEnabled: true },
},
];
// 合并并返回所有属性
return [...baseProperties, ...inkProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理水墨笔刷特有属性
if (propId === "inkAmount") {
this.setInkAmount(value);
return true;
} else if (propId === "range") {
this.setRange(value);
return true;
} else if (propId === "splashEnabled") {
this.setSplashEnabled(value);
return true;
} else if (propId === "splashSize") {
this.setSplashSize(value);
return true;
} else if (propId === "splashDistance") {
this.setSplashDistance(value);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNMjAgODBDNDAgNjAgNjAgNDAgODAgMjAiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSI1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiLz48Y2lyY2xlIGN4PSI3MCIgY3k9IjMwIiByPSI1IiBmaWxsPSIjMDAwIi8+PGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iMyIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjMwIiBjeT0iNzAiIHI9IjYiIGZpbGw9IiMwMDAiLz48L3N2Zz4=";
}
}

View File

@@ -0,0 +1,305 @@
import { BaseBrush } from "../BaseBrush";
/**
* 长毛发笔刷
* 创建类似于长毛、毛皮、草或头发的效果
*/
export class LongfurBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "longfur",
name: "长毛发",
description: "创建流动的长毛发效果,适合绘制动物毛皮、草或头发",
category: "特效笔刷",
icon: "longfur",
...options,
});
// 长毛发笔刷特有属性
this.furLength = options.furLength || 20;
this.furDensity = options.furDensity || 0.7;
this.furFlowFactor = options.furFlowFactor || 0.5;
this.furCurvature = options.furCurvature || 0.3;
this.randomizeDirection =
options.randomizeDirection !== undefined
? options.randomizeDirection
: true;
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生长毛发笔刷
this.brush = new fabric.LongfurBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 长毛发笔刷特有属性
if (options.furLength !== undefined) {
this.furLength = options.furLength;
// 如果原生笔刷支持此属性,则设置
if (brush.furLength !== undefined) {
brush.furLength = this.furLength;
}
}
if (options.furDensity !== undefined) {
this.furDensity = options.furDensity;
// 如果原生笔刷支持此属性,则设置
if (brush.furDensity !== undefined) {
brush.furDensity = this.furDensity;
}
}
if (options.furFlowFactor !== undefined) {
this.furFlowFactor = options.furFlowFactor;
// 如果原生笔刷支持此属性,则设置
if (brush.furFlowFactor !== undefined) {
brush.furFlowFactor = this.furFlowFactor;
}
}
if (options.furCurvature !== undefined) {
this.furCurvature = options.furCurvature;
// 如果原生笔刷支持此属性,则设置
if (brush.furCurvature !== undefined) {
brush.furCurvature = this.furCurvature;
}
}
if (options.randomizeDirection !== undefined) {
this.randomizeDirection = options.randomizeDirection;
// 如果原生笔刷支持此属性,则设置
if (brush.randomizeDirection !== undefined) {
brush.randomizeDirection = this.randomizeDirection;
}
}
}
/**
* 设置毛发长度
* @param {Number} length 长度值
*/
setFurLength(length) {
this.furLength = Math.max(5, Math.min(100, length));
if (this.brush && this.brush.furLength !== undefined) {
this.brush.furLength = this.furLength;
}
return this.furLength;
}
/**
* 设置毛发密度
* @param {Number} density 密度值(0-1)
*/
setFurDensity(density) {
this.furDensity = Math.max(0.1, Math.min(1, density));
if (this.brush && this.brush.furDensity !== undefined) {
this.brush.furDensity = this.furDensity;
}
return this.furDensity;
}
/**
* 设置毛发流动系数
* @param {Number} factor 流动系数(0-1)
*/
setFurFlowFactor(factor) {
this.furFlowFactor = Math.max(0, Math.min(1, factor));
if (this.brush && this.brush.furFlowFactor !== undefined) {
this.brush.furFlowFactor = this.furFlowFactor;
}
return this.furFlowFactor;
}
/**
* 设置毛发弯曲度
* @param {Number} curvature 弯曲度(0-1)
*/
setFurCurvature(curvature) {
this.furCurvature = Math.max(0, Math.min(1, curvature));
if (this.brush && this.brush.furCurvature !== undefined) {
this.brush.furCurvature = this.furCurvature;
}
return this.furCurvature;
}
/**
* 设置是否随机化方向
* @param {Boolean} randomize 是否随机化
*/
setRandomizeDirection(randomize) {
this.randomizeDirection = randomize;
if (this.brush && this.brush.randomizeDirection !== undefined) {
this.brush.randomizeDirection = this.randomizeDirection;
}
return this.randomizeDirection;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义长毛发笔刷特有属性
const longfurProperties = [
{
id: "furLength",
name: "毛发长度",
type: "slider",
defaultValue: this.furLength,
min: 5,
max: 100,
step: 1,
description: "控制毛发的长度",
category: "长毛发设置",
order: 100,
},
{
id: "furDensity",
name: "毛发密度",
type: "slider",
defaultValue: this.furDensity,
min: 0.1,
max: 1,
step: 0.05,
description: "控制毛发的密度",
category: "长毛发设置",
order: 110,
},
{
id: "furFlowFactor",
name: "流动系数",
type: "slider",
defaultValue: this.furFlowFactor,
min: 0,
max: 1,
step: 0.05,
description: "控制毛发的流动感",
category: "长毛发设置",
order: 120,
},
{
id: "furCurvature",
name: "弯曲度",
type: "slider",
defaultValue: this.furCurvature,
min: 0,
max: 1,
step: 0.05,
description: "控制毛发的弯曲程度",
category: "长毛发设置",
order: 130,
},
{
id: "randomizeDirection",
name: "随机方向",
type: "checkbox",
defaultValue: this.randomizeDirection,
description: "是否随机化毛发方向",
category: "长毛发设置",
order: 140,
},
];
// 合并并返回所有属性
return [...baseProperties, ...longfurProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理长毛发笔刷特有属性
if (propId === "furLength") {
this.setFurLength(value);
return true;
} else if (propId === "furDensity") {
this.setFurDensity(value);
return true;
} else if (propId === "furFlowFactor") {
this.setFurFlowFactor(value);
return true;
} else if (propId === "furCurvature") {
this.setFurCurvature(value);
return true;
} else if (propId === "randomizeDirection") {
this.setRandomizeDirection(value);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNMjUgNTBDMjUgNTAgNTAgMTAgNTAgNTBDNTAgNTAgNTAgOTAgNzUgNTAiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIyIiBmaWxsPSJub25lIi8+PGxpbmUgeDE9IjMwIiB5MT0iNDUiIHgyPSIzMCIgeTI9IjEwIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMSIvPjxsaW5lIHgxPSI0MCIgeTE9IjQwIiB4Mj0iNDAiIHkyPSI1IiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMSIvPjxsaW5lIHgxPSI1MCIgeTE9IjQwIiB4Mj0iNTAiIHkyPSIxMCIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjEiLz48bGluZSB4MT0iNjAiIHkxPSI0MCIgeDI9IjYwIiB5Mj0iNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjEiLz48bGluZSB4MT0iNzAiIHkxPSI0NSIgeDI9IjcwIiB5Mj0iMTAiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9zdmc+";
}
}

View File

@@ -0,0 +1,230 @@
import { BaseBrush } from "../BaseBrush";
/**
* 马克笔笔刷
* 模拟马克笔效果,具有半透明平头笔触
*/
export class MarkerBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "marker",
name: "马克笔",
description: "模拟马克笔效果,具有半透明平头笔触",
category: "基础笔刷",
icon: "marker",
...options,
});
// 马克笔特有属性
this._baseWidth = options._baseWidth || 15;
this._lineWidth = options._lineWidth || 2;
this.capStyle = options.capStyle || "round"; // "round" 或 "square"
this.blendMode = options.blendMode || "multiply"; // 混合模式
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生马克笔笔刷
this.brush = new fabric.MarkerBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
// 更新笔刷相关属性
this._baseWidth = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
// 马克笔的透明度默认不要太高
brush.opacity = Math.min(0.8, options.opacity || 0.6);
}
// 马克笔笔刷特有属性
if (options._baseWidth !== undefined) {
brush._baseWidth = options._baseWidth;
this._baseWidth = options._baseWidth;
}
if (options._lineWidth !== undefined) {
brush._lineWidth = options._lineWidth;
this._lineWidth = options._lineWidth;
}
// 笔触样式设置
brush.canvas.contextTop.lineJoin = "round";
brush.canvas.contextTop.lineCap = this.capStyle || "round";
// 马克笔的混合模式设置
if (this.blendMode === "multiply") {
brush.canvas.contextTop.globalCompositeOperation = "multiply";
} else {
brush.canvas.contextTop.globalCompositeOperation = "source-over";
}
}
/**
* 设置笔触线宽
* @param {Number} width 线宽值
*/
setLineWidth(width) {
this._lineWidth = Math.max(1, Math.min(10, width));
if (this.brush) {
this.brush._lineWidth = this._lineWidth;
}
return this._lineWidth;
}
/**
* 设置笔头样式
* @param {String} style 笔头样式 ('round' 或 'square')
*/
setCapStyle(style) {
if (style === "round" || style === "square") {
this.capStyle = style;
if (this.brush && this.brush.canvas) {
this.brush.canvas.contextTop.lineCap = style;
}
}
return this.capStyle;
}
/**
* 设置混合模式
* @param {String} mode 混合模式 ('multiply' 或 'normal')
*/
setBlendMode(mode) {
this.blendMode = mode;
if (this.brush && this.brush.canvas) {
this.brush.canvas.contextTop.globalCompositeOperation =
mode === "multiply" ? "multiply" : "source-over";
}
return this.blendMode;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义马克笔笔刷特有属性
const markerProperties = [
{
id: "lineWidth",
name: "笔触宽度",
type: "slider",
defaultValue: this._lineWidth,
min: 1,
max: 10,
step: 0.5,
description: "控制马克笔笔触的宽度",
category: "马克笔设置",
order: 100,
},
{
id: "capStyle",
name: "笔头样式",
type: "select",
defaultValue: this.capStyle,
options: [
{ value: "round", label: "圆形" },
{ value: "square", label: "方形" },
],
description: "设置马克笔笔头的形状",
category: "马克笔设置",
order: 110,
},
{
id: "blendMode",
name: "混合模式",
type: "select",
defaultValue: this.blendMode,
options: [
{ value: "multiply", label: "正片叠底" },
{ value: "normal", label: "正常" },
],
description: "设置马克笔的颜色混合方式",
category: "马克笔设置",
order: 120,
},
];
// 合并并返回所有属性
return [...baseProperties, ...markerProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理马克笔笔刷特有属性
if (propId === "lineWidth") {
this.setLineWidth(value);
return true;
} else if (propId === "capStyle") {
this.setCapStyle(value);
return true;
} else if (propId === "blendMode") {
this.setBlendMode(value);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNMTAgNTBIOTAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIyMCIgc3Ryb2tlLW9wYWNpdHk9IjAuNiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9zdmc+";
}
}

View File

@@ -0,0 +1,318 @@
import { BaseBrush } from "../BaseBrush";
/**
* 铅笔笔刷
* fabric原生铅笔笔刷的包装类
*/
export class PencilBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "pencil",
name: "铅笔",
description: "基础铅笔工具,适合精细线条绘制",
category: "基础笔刷",
icon: "pencil",
...options,
});
// 铅笔笔刷特有属性
this.decimate = options.decimate || 0.4;
this.strokeLineCap = options.strokeLineCap || "round";
this.strokeLineJoin = options.strokeLineJoin || "round";
}
/**
* 创建笔刷实例
* @returns {Object} fabric.PencilBrush实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生铅笔笔刷
this.brush = new fabric.PencilBrush(this.canvas);
// 重写 _finalizeAndAddPath 方法,使其调用 convertToImg 而不是创建 Path 对象
const originalFinalizeAndAddPath = this.brush._finalizeAndAddPath.bind(
this.brush
);
const self = this; // 保存外部this引用
this.brush._finalizeAndAddPath = function () {
console.log("PencilBrush: _finalizeAndAddPath called");
const ctx = this.canvas.contextTop;
ctx.closePath();
// 应用点简化
if (this.decimate) {
this._points = this.decimatePoints(this._points, this.decimate);
}
console.log(
"PencilBrush: points count =",
this._points ? this._points.length : 0
);
// 检查是否有有效的路径数据
if (!this._points || this._points.length < 2) {
// 如果点数不足,直接请求重新渲染
console.log("PencilBrush: Not enough points, skipping");
this.canvas.requestRenderAll();
return;
}
const pathData = this.convertPointsToSVGPath(this._points);
const isEmpty = self._isEmptySVGPath(pathData);
console.log("PencilBrush: isEmpty =", isEmpty);
if (isEmpty) {
// 如果路径为空,直接请求重新渲染
console.log("PencilBrush: Path is empty, skipping");
this.canvas.requestRenderAll();
return;
}
// 先触发事件,模拟原生行为
const path = this.createPath(pathData);
this.canvas.fire("before:path:created", { path: path });
console.log("PencilBrush: Calling convertToImg");
// 调用 convertToImg 方法将绘制内容转换为图片
if (typeof this.convertToImg === "function") {
this.convertToImg();
console.log("PencilBrush: 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);
return this.brush;
}
/**
* 检查 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);
}
/**
* 配置笔刷
* @param {Object} brush fabric.PencilBrush实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) {
return;
}
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 特殊属性配置
if (options.decimate !== undefined) {
brush.decimate = options.decimate;
this.decimate = options.decimate;
}
if (options.strokeLineCap !== undefined) {
brush.strokeLineCap = options.strokeLineCap;
this.strokeLineCap = options.strokeLineCap;
}
if (options.strokeLineJoin !== undefined) {
brush.strokeLineJoin = options.strokeLineJoin;
this.strokeLineJoin = options.strokeLineJoin;
}
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义铅笔笔刷特有属性
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 },
},
];
// 合并并返回所有属性
return [...baseProperties, ...pencilProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理铅笔特有属性
if (propId === "decimate") {
this.decimate = value;
if (this.brush) {
this.brush.decimate = value;
return true;
}
} else if (propId === "strokeLineCap") {
this.strokeLineCap = value;
if (this.brush) {
this.brush.strokeLineCap = value;
return true;
}
} else if (propId === "strokeLineJoin") {
this.strokeLineJoin = value;
if (this.brush) {
this.brush.strokeLineJoin = value;
return true;
}
} else if (propId === "smoothingEnabled") {
this.smoothingEnabled = value;
// 实现平滑逻辑...
return true;
} else if (propId === "smoothingFactor") {
this.smoothingFactor = value;
// 实现平滑度调整...
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
// 实际项目中可以返回一个实际的预览图URL
return "data:image/svg+xml;base64,..."; // 示例SVG
}
}

View File

@@ -0,0 +1,351 @@
import { BaseBrush } from "../BaseBrush";
/**
* 丝带笔刷
* 创建流畅的飘带状线条
*/
export class RibbonBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "ribbon",
name: "飘带",
description: "创建流畅的飘带状线条,具有动态宽度变化和曲线美感",
category: "特效笔刷",
icon: "ribbon",
...options,
});
// 丝带笔刷特有属性
this.ribbonWidth = options.ribbonWidth || 20;
this.widthVariation = options.widthVariation || 0.5;
this.ribbonSmoothness = options.ribbonSmoothness || 0.7;
this.gradient = options.gradient !== undefined ? options.gradient : true;
this.gradientColors = options.gradientColors || ["#000000", "#555555"];
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生丝带笔刷
this.brush = new fabric.RibbonBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
// 基于主宽度更新丝带宽度
this.ribbonWidth = options.width * 2;
}
if (options.color !== undefined) {
brush.color = options.color;
// 如果启用渐变,更新渐变的第一个颜色
if (this.gradient && this.gradientColors.length > 0) {
this.gradientColors[0] = options.color;
this.updateGradient();
}
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 丝带笔刷特有属性
if (options.ribbonWidth !== undefined) {
this.ribbonWidth = options.ribbonWidth;
// 如果原生笔刷支持此属性
if (brush.ribbonWidth !== undefined) {
brush.ribbonWidth = this.ribbonWidth;
}
}
if (options.widthVariation !== undefined) {
this.widthVariation = options.widthVariation;
// 如果原生笔刷支持此属性
if (brush.widthVariation !== undefined) {
brush.widthVariation = this.widthVariation;
}
}
if (options.ribbonSmoothness !== undefined) {
this.ribbonSmoothness = options.ribbonSmoothness;
// 如果原生笔刷支持此属性
if (brush.ribbonSmoothness !== undefined) {
brush.ribbonSmoothness = this.ribbonSmoothness;
}
}
if (options.gradient !== undefined) {
this.gradient = options.gradient;
this.updateGradient();
}
if (options.gradientColors !== undefined) {
this.gradientColors = options.gradientColors;
this.updateGradient();
}
}
/**
* 更新渐变设置
* @private
*/
updateGradient() {
if (!this.brush || !this.canvas) return;
if (this.gradient && this.gradientColors.length >= 2) {
// 创建渐变对象
const ctx = this.canvas.contextTop;
const gradient = ctx.createLinearGradient(0, 0, this.ribbonWidth, 0);
// 添加渐变色
const colorCount = this.gradientColors.length;
this.gradientColors.forEach((color, index) => {
gradient.addColorStop(index / (colorCount - 1), color);
});
// 如果原生笔刷支持渐变
if (typeof this.brush.setGradient === "function") {
this.brush.setGradient(gradient);
} else if (this.brush.gradient !== undefined) {
this.brush.gradient = gradient;
}
// 如果原生笔刷支持渐变标志
if (this.brush.useGradient !== undefined) {
this.brush.useGradient = true;
}
} else if (this.brush.useGradient !== undefined) {
// 禁用渐变
this.brush.useGradient = false;
}
}
/**
* 设置丝带宽度
* @param {Number} width 宽度值
*/
setRibbonWidth(width) {
this.ribbonWidth = Math.max(5, Math.min(100, width));
if (this.brush && this.brush.ribbonWidth !== undefined) {
this.brush.ribbonWidth = this.ribbonWidth;
}
// 更新渐变(因为宽度变了)
if (this.gradient) {
this.updateGradient();
}
return this.ribbonWidth;
}
/**
* 设置宽度变化率
* @param {Number} variation 变化率(0-1)
*/
setWidthVariation(variation) {
this.widthVariation = Math.max(0, Math.min(1, variation));
if (this.brush && this.brush.widthVariation !== undefined) {
this.brush.widthVariation = this.widthVariation;
}
return this.widthVariation;
}
/**
* 设置丝带平滑度
* @param {Number} smoothness 平滑度值(0-1)
*/
setRibbonSmoothness(smoothness) {
this.ribbonSmoothness = Math.max(0, Math.min(1, smoothness));
if (this.brush && this.brush.ribbonSmoothness !== undefined) {
this.brush.ribbonSmoothness = this.ribbonSmoothness;
}
return this.ribbonSmoothness;
}
/**
* 启用/禁用渐变效果
* @param {Boolean} enabled 是否启用
*/
setGradient(enabled) {
this.gradient = enabled;
this.updateGradient();
return this.gradient;
}
/**
* 设置渐变颜色
* @param {Array} colors 颜色数组
*/
setGradientColors(colors) {
if (Array.isArray(colors) && colors.length >= 2) {
this.gradientColors = colors;
this.updateGradient();
}
return this.gradientColors;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义丝带笔刷特有属性
const ribbonProperties = [
{
id: "ribbonWidth",
name: "飘带宽度",
type: "slider",
defaultValue: this.ribbonWidth,
min: 5,
max: 100,
step: 5,
description: "控制飘带的最大宽度",
category: "飘带设置",
order: 100,
},
{
id: "widthVariation",
name: "宽度变化",
type: "slider",
defaultValue: this.widthVariation,
min: 0,
max: 1,
step: 0.05,
description: "控制飘带宽度的变化程度",
category: "飘带设置",
order: 110,
},
{
id: "ribbonSmoothness",
name: "平滑度",
type: "slider",
defaultValue: this.ribbonSmoothness,
min: 0,
max: 1,
step: 0.05,
description: "控制飘带曲线的平滑程度",
category: "飘带设置",
order: 120,
},
{
id: "gradient",
name: "启用渐变",
type: "checkbox",
defaultValue: this.gradient,
description: "是否启用渐变效果",
category: "飘带设置",
order: 130,
},
{
id: "gradientColor1",
name: "渐变起始颜色",
type: "color",
defaultValue: this.gradientColors[0] || "#000000",
description: "设置渐变的起始颜色",
category: "飘带设置",
order: 140,
visibleWhen: { gradient: true },
},
{
id: "gradientColor2",
name: "渐变结束颜色",
type: "color",
defaultValue: this.gradientColors[1] || "#555555",
description: "设置渐变的结束颜色",
category: "飘带设置",
order: 150,
visibleWhen: { gradient: true },
},
];
// 合并并返回所有属性
return [...baseProperties, ...ribbonProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理丝带笔刷特有属性
if (propId === "ribbonWidth") {
this.setRibbonWidth(value);
return true;
} else if (propId === "widthVariation") {
this.setWidthVariation(value);
return true;
} else if (propId === "ribbonSmoothness") {
this.setRibbonSmoothness(value);
return true;
} else if (propId === "gradient") {
this.setGradient(value);
return true;
} else if (propId === "gradientColor1") {
const colors = [...this.gradientColors];
colors[0] = value;
this.setGradientColors(colors);
return true;
} else if (propId === "gradientColor2") {
const colors = [...this.gradientColors];
colors[1] = value;
this.setGradientColors(colors);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjMDAwIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjNTU1Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHBhdGggZD0iTTIwIDUwQzMwIDMwIDUwIDMwIDYwIDUwQzcwIDcwIDgwIDcwIDkwIDUwIiBzdHJva2U9InVybCgjZ3JhZCkiIHN0cm9rZS13aWR0aD0iMTAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZmlsbD0ibm9uZSIvPjwvc3ZnPg==";
}
}

View File

@@ -0,0 +1,362 @@
import { BaseBrush } from "../BaseBrush";
/**
* 阴影笔刷
* 创建带有阴影效果的绘制,有深浅变化
*/
export class ShadedBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "shaded",
name: "阴影笔",
description: "创建带有阴影效果的绘制,适合素描和明暗表现",
category: "绘画笔刷",
icon: "shaded",
...options,
});
// 阴影笔刷特有属性
this.shadowColor = options.shadowColor || "#000000";
this.shadowBlur = options.shadowBlur || 5;
this.shadowOffsetX = options.shadowOffsetX || 2;
this.shadowOffsetY = options.shadowOffsetY || 2;
this.blendMode = options.blendMode || "multiply";
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生阴影笔刷
this.brush = new fabric.ShadedBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 阴影笔刷特有属性
if (options.shadowColor !== undefined) {
this.shadowColor = options.shadowColor;
// 如果原生笔刷支持此属性,则设置
if (brush.shadow) {
brush.shadow.color = this.shadowColor;
} else {
brush.shadow = new fabric.Shadow({
color: this.shadowColor,
blur: this.shadowBlur,
offsetX: this.shadowOffsetX,
offsetY: this.shadowOffsetY,
});
}
}
if (options.shadowBlur !== undefined) {
this.shadowBlur = options.shadowBlur;
// 如果原生笔刷支持此属性,则设置
if (brush.shadow) {
brush.shadow.blur = this.shadowBlur;
} else {
brush.shadow = new fabric.Shadow({
color: this.shadowColor,
blur: this.shadowBlur,
offsetX: this.shadowOffsetX,
offsetY: this.shadowOffsetY,
});
}
}
if (options.shadowOffsetX !== undefined) {
this.shadowOffsetX = options.shadowOffsetX;
// 如果原生笔刷支持此属性,则设置
if (brush.shadow) {
brush.shadow.offsetX = this.shadowOffsetX;
} else {
brush.shadow = new fabric.Shadow({
color: this.shadowColor,
blur: this.shadowBlur,
offsetX: this.shadowOffsetX,
offsetY: this.shadowOffsetY,
});
}
}
if (options.shadowOffsetY !== undefined) {
this.shadowOffsetY = options.shadowOffsetY;
// 如果原生笔刷支持此属性,则设置
if (brush.shadow) {
brush.shadow.offsetY = this.shadowOffsetY;
} else {
brush.shadow = new fabric.Shadow({
color: this.shadowColor,
blur: this.shadowBlur,
offsetX: this.shadowOffsetX,
offsetY: this.shadowOffsetY,
});
}
}
if (options.blendMode !== undefined) {
this.blendMode = options.blendMode;
// 如果原生笔刷支持此属性,则设置
if (brush.globalCompositeOperation !== undefined) {
brush.globalCompositeOperation = this.blendMode;
}
}
}
/**
* 设置阴影颜色
* @param {String} color 颜色值
*/
setShadowColor(color) {
this.shadowColor = color;
if (this.brush && this.brush.shadow) {
this.brush.shadow.color = this.shadowColor;
}
return this.shadowColor;
}
/**
* 设置阴影模糊值
* @param {Number} blur 模糊值
*/
setShadowBlur(blur) {
this.shadowBlur = Math.max(0, Math.min(50, blur));
if (this.brush && this.brush.shadow) {
this.brush.shadow.blur = this.shadowBlur;
}
return this.shadowBlur;
}
/**
* 设置阴影X偏移
* @param {Number} offset X偏移值
*/
setShadowOffsetX(offset) {
this.shadowOffsetX = Math.max(-20, Math.min(20, offset));
if (this.brush && this.brush.shadow) {
this.brush.shadow.offsetX = this.shadowOffsetX;
}
return this.shadowOffsetX;
}
/**
* 设置阴影Y偏移
* @param {Number} offset Y偏移值
*/
setShadowOffsetY(offset) {
this.shadowOffsetY = Math.max(-20, Math.min(20, offset));
if (this.brush && this.brush.shadow) {
this.brush.shadow.offsetY = this.shadowOffsetY;
}
return this.shadowOffsetY;
}
/**
* 设置混合模式
* @param {String} mode 混合模式
*/
setBlendMode(mode) {
const validModes = [
"normal",
"multiply",
"screen",
"overlay",
"darken",
"lighten",
"color-dodge",
"color-burn",
"hard-light",
"soft-light",
"difference",
"exclusion",
"hue",
"saturation",
"color",
"luminosity",
];
if (validModes.includes(mode)) {
this.blendMode = mode;
if (this.brush && this.brush.globalCompositeOperation !== undefined) {
this.brush.globalCompositeOperation = this.blendMode;
}
}
return this.blendMode;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义阴影笔刷特有属性
const shadedProperties = [
{
id: "shadowColor",
name: "阴影颜色",
type: "color",
defaultValue: this.shadowColor,
description: "设置阴影的颜色",
category: "阴影设置",
order: 100,
},
{
id: "shadowBlur",
name: "阴影模糊",
type: "slider",
defaultValue: this.shadowBlur,
min: 0,
max: 50,
step: 1,
description: "控制阴影的模糊程度",
category: "阴影设置",
order: 110,
},
{
id: "shadowOffsetX",
name: "阴影X偏移",
type: "slider",
defaultValue: this.shadowOffsetX,
min: -20,
max: 20,
step: 1,
description: "控制阴影的水平偏移",
category: "阴影设置",
order: 120,
},
{
id: "shadowOffsetY",
name: "阴影Y偏移",
type: "slider",
defaultValue: this.shadowOffsetY,
min: -20,
max: 20,
step: 1,
description: "控制阴影的垂直偏移",
category: "阴影设置",
order: 130,
},
{
id: "blendMode",
name: "混合模式",
type: "select",
defaultValue: this.blendMode,
options: [
{ value: "normal", label: "正常" },
{ value: "multiply", label: "正片叠底" },
{ value: "screen", label: "滤色" },
{ value: "overlay", label: "叠加" },
{ value: "darken", label: "变暗" },
{ value: "lighten", label: "变亮" },
{ value: "color-dodge", label: "颜色减淡" },
{ value: "color-burn", label: "颜色加深" },
{ value: "hard-light", label: "强光" },
{ value: "soft-light", label: "柔光" },
{ value: "difference", label: "差值" },
{ value: "exclusion", label: "排除" },
],
description: "设置阴影的混合模式",
category: "阴影设置",
order: 140,
},
];
// 合并并返回所有属性
return [...baseProperties, ...shadedProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理阴影笔刷特有属性
if (propId === "shadowColor") {
this.setShadowColor(value);
return true;
} else if (propId === "shadowBlur") {
this.setShadowBlur(value);
return true;
} else if (propId === "shadowOffsetX") {
this.setShadowOffsetX(value);
return true;
} else if (propId === "shadowOffsetY") {
this.setShadowOffsetY(value);
return true;
} else if (propId === "blendMode") {
this.setBlendMode(value);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48Y2lyY2xlIGN4PSI0MCIgY3k9IjQwIiByPSIyMCIgZmlsbD0iIzY2NiIvPjxjaXJjbGUgY3g9IjQ1IiBjeT0iNDUiIHI9IjIwIiBmaWxsPSIjMDAwIi8+PHBhdGggZD0iTTIwIDgwQzMwIDYwIDUwIDcwIDcwIDUwIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iOCIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTIzIDgzQzMzIDYzIDUzIDczIDczIDUzIiBzdHJva2U9IiM2NjYiIHN0cm9rZS13aWR0aD0iOCIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9zdmc+";
}
}

View File

@@ -0,0 +1,371 @@
import { BaseBrush } from "../BaseBrush";
/**
* 素描笔刷
* 创建手绘素描效果,有不规则的线条和纹理
*/
export class SketchyBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "sketchy",
name: "素描",
description: "创建手绘素描效果,有不规则的线条和纹理",
category: "绘画笔刷",
icon: "sketchy",
...options,
});
// 素描笔刷特有属性
this.roughness = options.roughness || 0.7;
this.bowing = options.bowing || 0.5;
this.stroke = options.stroke !== undefined ? options.stroke : true;
this.hachureAngle = options.hachureAngle || 60;
this.dashOffset = options.dashOffset || 0;
this.dashArray = options.dashArray || [6, 2];
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生素描笔刷
this.brush = new fabric.SketchyBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 素描笔刷特有属性
if (options.roughness !== undefined) {
this.roughness = options.roughness;
// 如果原生笔刷支持此属性,则设置
if (brush.roughness !== undefined) {
brush.roughness = this.roughness;
}
}
if (options.bowing !== undefined) {
this.bowing = options.bowing;
// 如果原生笔刷支持此属性,则设置
if (brush.bowing !== undefined) {
brush.bowing = this.bowing;
}
}
if (options.stroke !== undefined) {
this.stroke = options.stroke;
// 如果原生笔刷支持此属性,则设置
if (brush.stroke !== undefined) {
brush.stroke = this.stroke;
}
}
if (options.hachureAngle !== undefined) {
this.hachureAngle = options.hachureAngle;
// 如果原生笔刷支持此属性,则设置
if (brush.hachureAngle !== undefined) {
brush.hachureAngle = this.hachureAngle;
}
}
if (options.dashOffset !== undefined) {
this.dashOffset = options.dashOffset;
// 如果原生笔刷支持此属性,则设置
if (brush.dashOffset !== undefined) {
brush.dashOffset = this.dashOffset;
}
}
if (options.dashArray !== undefined) {
this.dashArray = options.dashArray;
// 如果原生笔刷支持此属性,则设置
if (brush.dashArray !== undefined) {
brush.dashArray = this.dashArray;
}
}
// 为笔刷设置手绘效果
const originalOnMouseMove = brush.onMouseMove;
brush.onMouseMove = function (pointer, options) {
// 添加微小随机偏移,模拟手绘效果
const jitter = (this.width / 4) * this.roughness;
pointer.x += (Math.random() - 0.5) * jitter;
pointer.y += (Math.random() - 0.5) * jitter;
// 调用原始方法
if (originalOnMouseMove) {
originalOnMouseMove.call(this, pointer, options);
}
};
}
/**
* 设置粗糙度
* @param {Number} value 粗糙度值(0-1)
*/
setRoughness(value) {
this.roughness = Math.max(0, Math.min(1, value));
if (this.brush && this.brush.roughness !== undefined) {
this.brush.roughness = this.roughness;
}
return this.roughness;
}
/**
* 设置弯曲度
* @param {Number} value 弯曲度值(0-1)
*/
setBowing(value) {
this.bowing = Math.max(0, Math.min(1, value));
if (this.brush && this.brush.bowing !== undefined) {
this.brush.bowing = this.bowing;
}
return this.bowing;
}
/**
* 设置是否描边
* @param {Boolean} value 是否描边
*/
setStroke(value) {
this.stroke = value;
if (this.brush && this.brush.stroke !== undefined) {
this.brush.stroke = this.stroke;
}
return this.stroke;
}
/**
* 设置素描线条角度
* @param {Number} value 角度值(0-180)
*/
setHachureAngle(value) {
this.hachureAngle = Math.max(0, Math.min(180, value));
if (this.brush && this.brush.hachureAngle !== undefined) {
this.brush.hachureAngle = this.hachureAngle;
}
return this.hachureAngle;
}
/**
* 设置虚线偏移量
* @param {Number} value 偏移量
*/
setDashOffset(value) {
this.dashOffset = value;
if (this.brush && this.brush.dashOffset !== undefined) {
this.brush.dashOffset = this.dashOffset;
}
return this.dashOffset;
}
/**
* 设置虚线数组
* @param {Array} value 虚线数组[线长, 间隔]
*/
setDashArray(value) {
if (Array.isArray(value) && value.length >= 2) {
this.dashArray = value;
if (this.brush && this.brush.dashArray !== undefined) {
this.brush.dashArray = this.dashArray;
}
}
return this.dashArray;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义素描笔刷特有属性
const sketchyProperties = [
{
id: "roughness",
name: "粗糙度",
type: "slider",
defaultValue: this.roughness,
min: 0,
max: 1,
step: 0.05,
description: "控制素描线条的粗糙程度",
category: "素描设置",
order: 100,
},
{
id: "bowing",
name: "弯曲度",
type: "slider",
defaultValue: this.bowing,
min: 0,
max: 1,
step: 0.05,
description: "控制素描线条的弯曲程度",
category: "素描设置",
order: 110,
},
{
id: "stroke",
name: "描边",
type: "checkbox",
defaultValue: this.stroke,
description: "是否使用描边",
category: "素描设置",
order: 120,
},
{
id: "hachureAngle",
name: "线条角度",
type: "slider",
defaultValue: this.hachureAngle,
min: 0,
max: 180,
step: 5,
description: "控制素描线条的角度",
category: "素描设置",
order: 130,
},
{
id: "dashOffset",
name: "虚线偏移",
type: "slider",
defaultValue: this.dashOffset,
min: 0,
max: 10,
step: 1,
description: "控制虚线的偏移量",
category: "素描设置",
order: 140,
},
{
id: "dashArray",
name: "虚线模式",
type: "select",
defaultValue: JSON.stringify(this.dashArray),
options: [
{ value: JSON.stringify([0]), label: "实线" },
{ value: JSON.stringify([6, 2]), label: "短虚线" },
{ value: JSON.stringify([10, 5]), label: "长虚线" },
{ value: JSON.stringify([2, 2]), label: "点线" },
{ value: JSON.stringify([10, 5, 2, 5]), label: "点划线" },
],
description: "设置虚线的模式",
category: "素描设置",
order: 150,
parseValue: (value) => JSON.parse(value),
},
];
// 合并并返回所有属性
return [...baseProperties, ...sketchyProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理素描笔刷特有属性
if (propId === "roughness") {
this.setRoughness(value);
return true;
} else if (propId === "bowing") {
this.setBowing(value);
return true;
} else if (propId === "stroke") {
this.setStroke(value);
return true;
} else if (propId === "hachureAngle") {
this.setHachureAngle(value);
return true;
} else if (propId === "dashOffset") {
this.setDashOffset(value);
return true;
} else if (propId === "dashArray") {
let parsedValue = value;
if (typeof value === "string") {
try {
parsedValue = JSON.parse(value);
} catch (e) {
console.error("Invalid dashArray value:", e);
return false;
}
}
this.setDashArray(parsedValue);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNMjMgMzBDMjUgMjggNTIgMzggNzUgMzciIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIzIiBmaWxsPSJub25lIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMjIgNDBDMjIgMzggNTkgNDYgNzYgNDMiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIyIiBmaWxsPSJub25lIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMjAgNTBDMjIgNDggNTYgNTYgNzYgNTIiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIyIiBmaWxsPSJub25lIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMjQgNjBDMjMgNTggNDYgNjQgNzUgNjQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIyLjgiIGZpbGw9Im5vbmUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yNiA3MkMyNyA2OSA0OSA3NCA3NSA3MiIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9IjIuNCIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9zdmc+";
}
}

View File

@@ -0,0 +1,316 @@
import { BaseBrush } from "../BaseBrush";
/**
* 喷漆笔刷
* 创建喷漆效果,点状分散的绘制风格
*/
export class SpraypaintBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "spraypaint",
name: "喷漆笔刷",
description: "创建喷漆效果,点状分散的绘制风格",
category: "绘画笔刷",
icon: "spraypaint",
...options,
});
// 喷漆笔刷特有属性
this.density = options.density || 20;
this.sprayRadius = options.sprayRadius || 10;
this.randomOpacity =
options.randomOpacity !== undefined ? options.randomOpacity : true;
this.dotSize = options.dotSize || 1;
this.dotShape = options.dotShape || "circle";
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生喷漆笔刷
this.brush = new fabric.SprayBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 设置基本属性
if (options.width !== undefined) {
brush.width = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 喷漆笔刷特有属性
if (options.density !== undefined) {
this.density = options.density;
// 如果原生笔刷支持此属性,则设置
if (brush.density !== undefined) {
brush.density = this.density;
}
}
if (options.sprayRadius !== undefined) {
this.sprayRadius = options.sprayRadius;
// 如果原生笔刷支持此属性,则设置
if (brush.sprayWidth !== undefined) {
brush.sprayWidth = this.sprayRadius;
} else if (brush.width !== undefined) {
brush.width = this.sprayRadius;
}
}
if (options.randomOpacity !== undefined) {
this.randomOpacity = options.randomOpacity;
// 如果原生笔刷支持此属性,则设置
if (brush.randomOpacity !== undefined) {
brush.randomOpacity = this.randomOpacity;
}
}
if (options.dotSize !== undefined) {
this.dotSize = options.dotSize;
// 如果原生笔刷支持此属性,则设置
if (brush.dotWidth !== undefined) {
brush.dotWidth = this.dotSize;
}
}
if (options.dotShape !== undefined) {
this.dotShape = options.dotShape;
// 如果原生笔刷支持此属性,则设置
if (brush.dotShape !== undefined) {
brush.dotShape = this.dotShape;
}
}
}
/**
* 设置喷漆密度
* @param {Number} value 密度值
*/
setDensity(value) {
this.density = Math.max(1, Math.min(100, value));
if (this.brush && this.brush.density !== undefined) {
this.brush.density = this.density;
}
return this.density;
}
/**
* 设置喷漆半径
* @param {Number} value 半径值
*/
setSprayRadius(value) {
this.sprayRadius = Math.max(1, value);
if (this.brush) {
if (this.brush.sprayWidth !== undefined) {
this.brush.sprayWidth = this.sprayRadius;
} else if (this.brush.width !== undefined) {
this.brush.width = this.sprayRadius;
}
}
return this.sprayRadius;
}
/**
* 设置是否随机透明度
* @param {Boolean} value 是否随机透明度
*/
setRandomOpacity(value) {
this.randomOpacity = value;
if (this.brush && this.brush.randomOpacity !== undefined) {
this.brush.randomOpacity = this.randomOpacity;
}
return this.randomOpacity;
}
/**
* 设置点大小
* @param {Number} value 点大小
*/
setDotSize(value) {
this.dotSize = Math.max(0.1, value);
if (this.brush && this.brush.dotWidth !== undefined) {
this.brush.dotWidth = this.dotSize;
}
return this.dotSize;
}
/**
* 设置点形状
* @param {String} value 点形状,如 'circle', 'square', 'diamond'
*/
setDotShape(value) {
const validShapes = ["circle", "square", "diamond", "random"];
if (validShapes.includes(value)) {
this.dotShape = value;
if (this.brush && this.brush.dotShape !== undefined) {
this.brush.dotShape = this.dotShape;
}
}
return this.dotShape;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义喷漆笔刷特有属性
const spraypaintProperties = [
{
id: "density",
name: "喷漆密度",
type: "slider",
defaultValue: this.density,
min: 1,
max: 100,
step: 1,
description: "控制喷漆点的密度",
category: "喷漆设置",
order: 100,
},
{
id: "sprayRadius",
name: "喷漆半径",
type: "slider",
defaultValue: this.sprayRadius,
min: 1,
max: 50,
step: 1,
description: "控制喷漆的覆盖半径",
category: "喷漆设置",
order: 110,
},
{
id: "randomOpacity",
name: "随机透明度",
type: "checkbox",
defaultValue: this.randomOpacity,
description: "使喷漆点有随机透明度",
category: "喷漆设置",
order: 120,
},
{
id: "dotSize",
name: "点大小",
type: "slider",
defaultValue: this.dotSize,
min: 0.1,
max: 10,
step: 0.1,
description: "控制喷漆点的大小",
category: "喷漆设置",
order: 130,
},
{
id: "dotShape",
name: "点形状",
type: "select",
defaultValue: this.dotShape,
options: [
{ value: "circle", label: "圆形" },
{ value: "square", label: "方形" },
{ value: "diamond", label: "菱形" },
{ value: "random", label: "随机" },
],
description: "设置喷漆点的形状",
category: "喷漆设置",
order: 140,
},
];
// 合并并返回所有属性
return [...baseProperties, ...spraypaintProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理喷漆笔刷特有属性
if (propId === "density") {
this.setDensity(value);
return true;
} else if (propId === "sprayRadius") {
this.setSprayRadius(value);
return true;
} else if (propId === "randomOpacity") {
this.setRandomOpacity(value);
return true;
} else if (propId === "dotSize") {
this.setDotSize(value);
return true;
} else if (propId === "dotShape") {
this.setDotShape(value);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48Y2lyY2xlIGN4PSI1NSIgY3k9IjUwIiByPSIyMCIgZmlsbD0icmdiYSgwLDAsMCwwLjEpIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMC41Ii8+PGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iMSIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjU1IiBjeT0iNTUiIHI9IjAuOCIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjYwIiBjeT0iNDUiIHI9IjEuMiIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjQ1IiBjeT0iNTUiIHI9IjAuNyIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjQ3IiBjeT0iNDgiIHI9IjAuOSIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjU4IiBjeT0iNTMiIHI9IjEuMSIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjYyIiBjeT0iNTYiIHI9IjAuNiIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjUyIiBjeT0iNTgiIHI9IjAuOCIgZmlsbD0iIzAwMCIvPjxjaXJjbGUgY3g9IjU0IiBjeT0iNDMiIHI9IjEiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI0OSIgY3k9IjQzIiByPSIwLjYiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI0MyIgY3k9IjQ3IiByPSIwLjciIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI2NSIgY3k9IjQ4IiByPSIwLjkiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI2MiIgY3k9IjQxIiByPSIwLjUiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI0OSIgY3k9IjYxIiByPSIwLjgiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI2NiIgY3k9IjUyIiByPSIwLjciIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI0MSIgY3k9IjUxIiByPSIwLjYiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI2OCIgY3k9IjU3IiByPSIwLjQiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI0NSIgY3k9IjQwIiByPSIwLjUiIGZpbGw9IiMwMDAiLz48Y2lyY2xlIGN4PSI1NyIgY3k9IjYxIiByPSIwLjciIGZpbGw9IiMwMDAiLz48L3N2Zz4=";
}
}

View File

@@ -0,0 +1,855 @@
import { BaseBrush } from "../BaseBrush";
//import { fabric } from "fabric-with-all";
import texturePresetManager from "../TexturePresetManager";
/**
* 纹理笔刷
* 使用图像纹理进行绘制的笔刷
*/
export class TextureBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "texture",
name: "纹理笔刷",
description: "使用图像纹理进行绘制的笔刷",
category: "特效笔刷",
icon: "texture",
...options,
});
// 纹理笔刷特有属性
this.textureSource = options.textureSource || null;
this.textureRepeat = options.textureRepeat || "repeat";
this.textureScale = options.textureScale || 1;
this.textureAngle = options.textureAngle || 0;
this.textureOpacity =
options.textureOpacity !== undefined ? options.textureOpacity : 1;
// 预设材质相关
this.selectedTextureId = options.selectedTextureId || null;
this.texturePresets = [];
// 加载预设材质
this._loadTexturePresets();
// 当前选中的材质索引
this.currentTextureIndex = options.currentTextureIndex || 0;
// 从预设管理器加载自定义材质
texturePresetManager.loadCustomTexturesFromStorage();
}
/**
* 加载材质预设
* @private
*/
_loadTexturePresets() {
// 从预设管理器获取所有材质
this.texturePresets = texturePresetManager.getAllTextures();
// 如果没有选中的材质ID使用第一个预设材质
if (!this.selectedTextureId && this.texturePresets.length > 0) {
this.selectedTextureId = this.texturePresets[0].id;
this.currentTextureIndex = 0;
}
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生纹理笔刷
this.brush = new fabric.PatternBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
// 如果有选中的材质,则设置纹理
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 设置基本属性
if (options.width !== undefined) {
brush.width = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 纹理笔刷特有属性
if (options.textureRepeat !== undefined) {
this.textureRepeat = options.textureRepeat;
// 需要重新应用纹理以应用重复模式
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
}
if (options.textureScale !== undefined) {
this.textureScale = options.textureScale;
// 需要重新应用纹理以应用缩放
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
}
if (options.textureAngle !== undefined) {
this.textureAngle = options.textureAngle;
// 需要重新应用纹理以应用旋转角度
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
}
if (options.textureOpacity !== undefined) {
this.textureOpacity = options.textureOpacity;
// 需要重新应用纹理以应用透明度
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
}
}
/**
* 根据材质ID设置纹理
* @param {String} textureId 材质ID
* @returns {Promise} 加载完成的Promise
*/
setTextureById(textureId) {
const texture = texturePresetManager.getTextureById(textureId);
if (!texture) {
return Promise.reject(new Error(`材质 ${textureId} 不存在`));
}
this.selectedTextureId = textureId;
// 更新当前材质索引
const allTextures = texturePresetManager.getAllTextures();
this.currentTextureIndex = allTextures.findIndex((t) => t.id === textureId);
return this.setTexture(texture.path);
}
/**
* 设置纹理
* @param {String|Object} source 纹理源URL或Image对象
* @returns {Promise} 加载完成的Promise
*/
setTexture(source) {
this.textureSource = source;
if (!this.brush) {
return Promise.reject(new Error("笔刷实例不存在"));
}
return new Promise((resolve, reject) => {
if (typeof source === "string") {
// 如果是URL加载图像
fabric.util.loadImage(source, (img) => {
if (!img) {
reject(new Error("纹理加载失败"));
return;
}
this._applyTextureToPatternBrush(img);
resolve(img);
});
} else if (
source instanceof Image ||
source instanceof HTMLCanvasElement
) {
// 如果已经是Image或Canvas对象直接使用
this._applyTextureToPatternBrush(source);
resolve(source);
} else {
reject(new Error("无效的纹理源"));
}
});
}
/**
* 将纹理应用到PatternBrush
* @param {Object} img 图像对象
* @private
*/
_applyTextureToPatternBrush(img) {
if (!this.brush || !img) return;
// 创建Canvas来处理纹理
const canvasTexture = document.createElement("canvas");
const ctx = canvasTexture.getContext("2d");
// 根据缩放设置Canvas大小
const width = img.width * this.textureScale;
const height = img.height * this.textureScale;
canvasTexture.width = width;
canvasTexture.height = height;
// 绘制前应用旋转
if (this.textureAngle !== 0) {
ctx.save();
ctx.translate(width / 2, height / 2);
ctx.rotate((this.textureAngle * Math.PI) / 180);
ctx.translate(-width / 2, -height / 2);
ctx.drawImage(img, 0, 0, width, height);
ctx.restore();
} else {
ctx.drawImage(img, 0, 0, width, height);
}
// 应用透明度
if (this.textureOpacity < 1) {
ctx.globalAlpha = this.textureOpacity;
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
}
// 创建Pattern对象
const pattern = new fabric.Pattern({
source: canvasTexture,
repeat: this.textureRepeat,
});
// 设置笔刷源纹理
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;
}
}
/**
* 设置纹理重复模式
* @param {String} mode 重复模式:'repeat', 'repeat-x', 'repeat-y', 'no-repeat'
* @returns {String} 设置后的重复模式
*/
setTextureRepeat(mode) {
const validModes = ["repeat", "repeat-x", "repeat-y", "no-repeat"];
if (validModes.includes(mode)) {
this.textureRepeat = mode;
// 重新应用纹理以更新重复模式
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
}
return this.textureRepeat;
}
/**
* 设置纹理缩放比例
* @param {Number} scale 缩放比例
* @returns {Number} 设置后的缩放比例
*/
setTextureScale(scale) {
this.textureScale = Math.max(0.1, scale);
// 重新应用纹理以更新缩放
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
return this.textureScale;
}
/**
* 设置纹理旋转角度
* @param {Number} angle 旋转角度(度)
* @returns {Number} 设置后的旋转角度
*/
setTextureAngle(angle) {
this.textureAngle = angle % 360;
// 重新应用纹理以更新旋转角度
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
return this.textureAngle;
}
/**
* 设置纹理透明度
* @param {Number} opacity 透明度
* @returns {Number} 设置后的透明度
*/
setTextureOpacity(opacity) {
this.textureOpacity = Math.min(1, Math.max(0, opacity));
// 重新应用纹理以更新透明度
if (this.selectedTextureId) {
this.setTextureById(this.selectedTextureId);
} else if (this.textureSource) {
this.setTexture(this.textureSource);
}
return this.textureOpacity;
}
/**
* 切换到下一个预设材质
* @returns {Promise} 切换完成的Promise
*/
nextTexture() {
const textures = texturePresetManager.getAllTextures();
if (textures.length === 0) return Promise.resolve();
this.currentTextureIndex = (this.currentTextureIndex + 1) % textures.length;
const nextTexture = textures[this.currentTextureIndex];
return this.setTextureById(nextTexture.id);
}
/**
* 切换到上一个预设材质
* @returns {Promise} 切换完成的Promise
*/
previousTexture() {
const textures = texturePresetManager.getAllTextures();
if (textures.length === 0) return Promise.resolve();
this.currentTextureIndex =
this.currentTextureIndex === 0
? textures.length - 1
: this.currentTextureIndex - 1;
const prevTexture = textures[this.currentTextureIndex];
return this.setTextureById(prevTexture.id);
}
/**
* 使用索引切换纹理
* @param {Number} index 纹理索引
*/
switchTexture(index) {
const textures = texturePresetManager.getAllTextures();
if (index >= 0 && index < textures.length) {
this.currentTextureIndex = index;
const texture = textures[index];
return this.setTextureById(texture.id);
}
return Promise.reject(new Error("无效的纹理索引"));
}
/**
* 获取当前选中的材质信息
* @returns {Object|null} 材质信息
*/
getCurrentTexture() {
if (this.selectedTextureId) {
return texturePresetManager.getTextureById(this.selectedTextureId);
}
return null;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 获取所有可用材质
const allTextures = texturePresetManager.getAllTextures();
const textureOptions = allTextures.map((texture, index) => ({
value: texture.id,
label: texture.name,
preview: texturePresetManager.getTexturePreviewUrl(texture),
category: texture.category,
}));
// 定义纹理笔刷特有属性
const textureProperties = [
{
id: "textureSelector",
name: "材质选择",
type: "texture-grid",
defaultValue: this.selectedTextureId,
options: textureOptions,
description: "选择要使用的纹理",
category: "纹理设置",
order: 100,
hidden: allTextures.length === 0,
},
{
id: "textureRepeat",
name: "纹理重复模式",
type: "select",
defaultValue: this.textureRepeat,
options: [
{ value: "repeat", label: "双向重复" },
{ value: "repeat-x", label: "水平重复" },
{ value: "repeat-y", label: "垂直重复" },
{ value: "no-repeat", label: "不重复" },
],
description: "设置纹理的重复模式",
category: "纹理设置",
order: 110,
},
{
id: "textureScale",
name: "纹理缩放",
type: "slider",
defaultValue: this.textureScale,
min: 0.1,
max: 5,
step: 0.1,
description: "调整纹理的缩放比例",
category: "纹理设置",
order: 120,
},
{
id: "textureAngle",
name: "纹理旋转",
type: "slider",
defaultValue: this.textureAngle,
min: 0,
max: 360,
step: 5,
description: "调整纹理的旋转角度",
category: "纹理设置",
order: 130,
},
{
id: "textureOpacity",
name: "纹理透明度",
type: "slider",
defaultValue: this.textureOpacity,
min: 0,
max: 1,
step: 0.05,
description: "调整纹理的透明度",
category: "纹理设置",
order: 140,
},
{
id: "uploadTexture",
name: "上传纹理",
type: "button",
action: "uploadTexture",
description: "上传自定义纹理",
category: "纹理设置",
order: 150,
},
{
id: "texturePreview",
name: "纹理预览",
type: "preview",
description: "当前纹理预览",
category: "纹理设置",
order: 160,
getValue: () => {
const currentTexture = this.getCurrentTexture();
return currentTexture
? texturePresetManager.getTexturePreviewUrl(currentTexture)
: null;
},
},
];
// 合并并返回所有属性
return [...baseProperties, ...textureProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理纹理笔刷特有属性
if (propId === "textureSelector") {
this.setTextureById(value);
return true;
} else if (propId === "textureRepeat") {
this.setTextureRepeat(value);
return true;
} else if (propId === "textureScale") {
this.setTextureScale(value);
return true;
} else if (propId === "textureAngle") {
this.setTextureAngle(value);
return true;
} else if (propId === "textureOpacity") {
this.setTextureOpacity(value);
return true;
} else if (propId === "uploadTexture") {
// 触发上传纹理事件
// 这里通常由外部处理返回true表示属性被处理
return true;
}
return false;
}
/**
* 添加自定义材质
* @param {Object} textureData 材质数据
* @returns {String} 材质ID
*/
addCustomTexture(textureData) {
const textureId = texturePresetManager.addCustomTexture(textureData);
// 重新加载材质预设
this._loadTexturePresets();
// 保存到本地存储
texturePresetManager.saveCustomTexturesToStorage();
return textureId;
}
/**
* 删除自定义材质
* @param {String} textureId 材质ID
* @returns {Boolean} 是否删除成功
*/
removeCustomTexture(textureId) {
const success = texturePresetManager.removeCustomTexture(textureId);
if (success) {
// 如果删除的是当前选中的材质,切换到第一个可用材质
if (this.selectedTextureId === textureId) {
const allTextures = texturePresetManager.getAllTextures();
if (allTextures.length > 0) {
this.setTextureById(allTextures[0].id);
} else {
this.selectedTextureId = null;
this.currentTextureIndex = 0;
}
}
// 重新加载材质预设
this._loadTexturePresets();
// 保存到本地存储
texturePresetManager.saveCustomTexturesToStorage();
}
return success;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
const currentTexture = this.getCurrentTexture();
if (currentTexture) {
return texturePresetManager.getTexturePreviewUrl(currentTexture);
}
// 返回默认纹理预览
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48ZGVmcz48cGF0dGVybiBpZD0icGF0dGVybiIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIj48cmVjdCB3aWR0aD0iNSIgaGVpZ2h0PSI1IiBmaWxsPSIjZGRkIi8+PHJlY3QgeD0iNSIgeT0iNSIgd2lkdGg9IjUiIGhlaWdodD0iNSIgZmlsbD0iI2RkZCIvPjwvcGF0dGVybj48L2RlZnM+PHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9InVybCgjcGF0dGVybikiLz48L3N2Zz4=";
}
/**
* 笔刷被选中时调用
* @override
*/
onSelected() {
// 重新加载材质预设(可能有新的自定义材质)
this._loadTexturePresets();
}
/**
* 销毁笔刷实例并清理资源
* @override
*/
destroy() {
super.destroy();
this.textureSource = null;
this.selectedTextureId = null;
this.texturePresets = [];
}
/**
* 设置材质属性
* @param {String} property 属性名称
* @param {any} value 属性值
* @returns {Boolean} 是否设置成功
*/
setTextureProperty(property, value) {
switch (property) {
case "scale":
return this.setTextureScale(value);
case "opacity":
return this.setTextureOpacity(value);
case "repeat":
return this.setTextureRepeat(value);
case "angle":
return this.setTextureAngle(value);
default:
return false;
}
}
/**
* 获取材质属性
* @param {String} property 属性名称
* @returns {any} 属性值
*/
getTextureProperty(property) {
switch (property) {
case "scale":
return this.textureScale;
case "opacity":
return this.textureOpacity;
case "repeat":
return this.textureRepeat;
case "angle":
return this.textureAngle;
case "textureId":
return this.selectedTextureId;
default:
return undefined;
}
}
/**
* 应用材质预设
* @param {String|Object} preset 预设ID或预设对象
* @returns {Boolean} 是否应用成功
*/
applyTexturePreset(preset) {
let presetData = null;
if (typeof preset === "string") {
// 如果是预设ID从预设管理器获取
presetData = texturePresetManager.applyTexturePreset(preset);
} else if (typeof preset === "object") {
// 如果是预设对象,直接使用
presetData = preset;
}
if (!presetData) {
console.warn("无效的材质预设:", preset);
return false;
}
// 应用预设设置
if (presetData.textureId) {
this.setTextureById(presetData.textureId);
}
if (presetData.scale !== undefined) {
this.setTextureScale(presetData.scale);
}
if (presetData.opacity !== undefined) {
this.setTextureOpacity(presetData.opacity);
}
if (presetData.repeat !== undefined) {
this.setTextureRepeat(presetData.repeat);
}
if (presetData.angle !== undefined) {
this.setTextureAngle(presetData.angle);
}
// 如果预设包含笔刷属性,也一并应用
if (presetData.brushSize !== undefined && this.brush) {
this.brush.width = presetData.brushSize;
}
if (presetData.brushOpacity !== undefined && this.brush) {
this.brush.opacity = presetData.brushOpacity;
}
if (presetData.brushColor !== undefined && this.brush) {
this.brush.color = presetData.brushColor;
}
return true;
}
/**
* 获取当前材质状态
* @returns {Object} 当前材质状态
*/
getCurrentTextureState() {
return {
textureId: this.selectedTextureId,
scale: this.textureScale,
opacity: this.textureOpacity,
repeat: this.textureRepeat,
angle: this.textureAngle,
// 包含笔刷状态
brushSize: this.brush ? this.brush.width : this.options.width,
brushOpacity: this.brush ? this.brush.opacity : this.options.opacity,
brushColor: this.brush ? this.brush.color : this.options.color,
};
}
/**
* 恢复材质状态
* @param {Object} state 要恢复的状态
* @returns {Boolean} 是否恢复成功
*/
restoreTextureState(state) {
if (!state) return false;
try {
// 恢复材质属性
if (state.textureId) {
this.setTextureById(state.textureId);
}
if (state.scale !== undefined) {
this.setTextureScale(state.scale);
}
if (state.opacity !== undefined) {
this.setTextureOpacity(state.opacity);
}
if (state.repeat !== undefined) {
this.setTextureRepeat(state.repeat);
}
if (state.angle !== undefined) {
this.setTextureAngle(state.angle);
}
// 恢复笔刷属性
if (this.brush) {
if (state.brushSize !== undefined) {
this.brush.width = state.brushSize;
}
if (state.brushOpacity !== undefined) {
this.brush.opacity = state.brushOpacity;
}
if (state.brushColor !== undefined) {
this.brush.color = state.brushColor;
}
}
return true;
} catch (error) {
console.error("恢复材质状态失败:", error);
return false;
}
}
/**
* 创建材质预设
* @param {String} name 预设名称
* @returns {String} 预设ID
*/
createTexturePreset(name) {
const currentState = this.getCurrentTextureState();
return texturePresetManager.createTexturePreset(name, currentState);
}
/**
* 获取可用的材质分类
* @returns {Array} 分类数组
*/
getTextureCategories() {
return texturePresetManager.getCategories();
}
/**
* 根据分类获取材质
* @param {String} category 分类名称
* @returns {Array} 材质数组
*/
getTexturesByCategory(category) {
return texturePresetManager.getTexturesByCategory(category);
}
/**
* 搜索材质
* @param {String} query 搜索关键词
* @returns {Array} 匹配的材质数组
*/
searchTextures(query) {
return texturePresetManager.searchTextures(query);
}
/**
* 预加载材质图像
* @param {String} textureId 材质ID
* @returns {Promise<HTMLImageElement>} 图像对象
*/
preloadTexture(textureId) {
return texturePresetManager.loadTextureImage(textureId);
}
/**
* 批量预加载材质
* @param {Array} textureIds 材质ID数组
* @returns {Promise<Array>} 加载结果数组
*/
preloadTextures(textureIds) {
const loadPromises = textureIds.map((id) =>
this.preloadTexture(id).catch((error) => ({ id, error }))
);
return Promise.all(loadPromises);
}
/**
* 获取材质统计信息
* @returns {Object} 统计信息
*/
getTextureStats() {
return texturePresetManager.getStats();
}
}

View File

@@ -0,0 +1,266 @@
import { BaseBrush } from "../BaseBrush";
/**
* 书法笔刷
* 模拟中国传统书法效果,具有笔锋和墨色变化
*/
export class WritingBrush extends BaseBrush {
/**
* 构造函数
* @param {Object} canvas fabric画布实例
* @param {Object} options 配置选项
*/
constructor(canvas, options = {}) {
super(canvas, {
id: "writing",
name: "书法笔",
description: "模拟中国传统书法毛笔效果,具有笔锋和墨色变化",
category: "特效笔刷",
icon: "writing",
...options,
});
// 书法笔刷特有属性
this.brushPressure = options.brushPressure || 0.7;
this.inkAmount = options.inkAmount || 20;
this.brushTaperFactor = options.brushTaperFactor || 0.6;
this.enableInkDripping =
options.enableInkDripping !== undefined
? options.enableInkDripping
: true;
}
/**
* 创建笔刷实例
* @returns {Object} fabric笔刷实例
*/
create() {
if (!this.canvas) {
throw new Error("画布实例不存在");
}
// 创建fabric原生书法笔刷
this.brush = new fabric.WritingBrush(this.canvas);
// 配置笔刷
this.configure(this.brush, this.options);
return this.brush;
}
/**
* 配置笔刷
* @param {Object} brush fabric笔刷实例
* @param {Object} options 配置选项
*/
configure(brush, options = {}) {
if (!brush) return;
// 基础属性配置
if (options.width !== undefined) {
brush.width = options.width;
}
if (options.color !== undefined) {
brush.color = options.color;
}
if (options.opacity !== undefined) {
brush.opacity = options.opacity;
}
// 书法笔刷特有属性
if (options.brushPressure !== undefined) {
this.brushPressure = options.brushPressure;
// 如果原生笔刷支持此属性,则设置
if (brush.brushPressure !== undefined) {
brush.brushPressure = this.brushPressure;
}
}
if (options.inkAmount !== undefined) {
this.inkAmount = options.inkAmount;
// 如果原生笔刷支持此属性,则设置
if (brush.inkAmount !== undefined) {
brush.inkAmount = this.inkAmount;
}
}
if (options.brushTaperFactor !== undefined) {
this.brushTaperFactor = options.brushTaperFactor;
// 如果原生笔刷支持此属性,则设置
if (brush.brushTaperFactor !== undefined) {
brush.brushTaperFactor = this.brushTaperFactor;
}
}
if (options.enableInkDripping !== undefined) {
this.enableInkDripping = options.enableInkDripping;
// 如果原生笔刷支持此属性,则设置
if (brush.enableInkDripping !== undefined) {
brush.enableInkDripping = this.enableInkDripping;
}
}
}
/**
* 设置笔压感应
* @param {Number} pressure 笔压值(0-1)
*/
setBrushPressure(pressure) {
this.brushPressure = Math.max(0.1, Math.min(1, pressure));
if (this.brush && this.brush.brushPressure !== undefined) {
this.brush.brushPressure = this.brushPressure;
}
return this.brushPressure;
}
/**
* 设置墨量
* @param {Number} amount 墨量值
*/
setInkAmount(amount) {
this.inkAmount = Math.max(1, Math.min(50, amount));
if (this.brush && this.brush.inkAmount !== undefined) {
this.brush.inkAmount = this.inkAmount;
}
return this.inkAmount;
}
/**
* 设置笔锋系数
* @param {Number} factor 笔锋系数(0-1)
*/
setBrushTaperFactor(factor) {
this.brushTaperFactor = Math.max(0, Math.min(1, factor));
if (this.brush && this.brush.brushTaperFactor !== undefined) {
this.brush.brushTaperFactor = this.brushTaperFactor;
}
return this.brushTaperFactor;
}
/**
* 启用/禁用墨滴效果
* @param {Boolean} enabled 是否启用
*/
setInkDripping(enabled) {
this.enableInkDripping = enabled;
if (this.brush && this.brush.enableInkDripping !== undefined) {
this.brush.enableInkDripping = this.enableInkDripping;
}
return this.enableInkDripping;
}
/**
* 获取笔刷可配置属性
* @returns {Array} 可配置属性描述数组
* @override
*/
getConfigurableProperties() {
// 获取基础属性
const baseProperties = super.getConfigurableProperties();
// 定义书法笔刷特有属性
const writingProperties = [
{
id: "brushPressure",
name: "笔压感应",
type: "slider",
defaultValue: this.brushPressure,
min: 0.1,
max: 1,
step: 0.05,
description: "控制笔触的力度感应",
category: "书法设置",
order: 100,
},
{
id: "inkAmount",
name: "墨量",
type: "slider",
defaultValue: this.inkAmount,
min: 1,
max: 50,
step: 1,
description: "控制笔触中的墨水量",
category: "书法设置",
order: 110,
},
{
id: "brushTaperFactor",
name: "笔锋系数",
type: "slider",
defaultValue: this.brushTaperFactor,
min: 0,
max: 1,
step: 0.05,
description: "控制笔锋的尖锐程度",
category: "书法设置",
order: 120,
},
{
id: "enableInkDripping",
name: "墨滴效果",
type: "checkbox",
defaultValue: this.enableInkDripping,
description: "是否启用墨滴效果",
category: "书法设置",
order: 130,
},
];
// 合并并返回所有属性
return [...baseProperties, ...writingProperties];
}
/**
* 更新笔刷属性
* @param {String} propId 属性ID
* @param {any} value 属性值
* @returns {Boolean} 是否更新成功
* @override
*/
updateProperty(propId, value) {
// 先检查基类能否处理此属性
if (super.updateProperty(propId, value)) {
return true;
}
// 处理书法笔刷特有属性
if (propId === "brushPressure") {
this.setBrushPressure(value);
return true;
} else if (propId === "inkAmount") {
this.setInkAmount(value);
return true;
} else if (propId === "brushTaperFactor") {
this.setBrushTaperFactor(value);
return true;
} else if (propId === "enableInkDripping") {
this.setInkDripping(value);
return true;
}
return false;
}
/**
* 获取预览图
* @returns {String} 预览图URL
*/
getPreview() {
return "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNMzAgMzBDNTAgMzAgNjAgNzAgODAgNzAiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTc1IDYwQzc4IDcwIDg1IDY1IDkwIDcwIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBmaWxsPSJub25lIi8+PC9zdmc+";
}
}