Files
FiDA_Front/src/components/Canvas/DepthCanvas/manager/brushes/TexturePresetManager.js
2026-03-11 15:34:56 +08:00

618 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 材质预设管理器
* 负责管理所有材质预设,包括内置预设和用户自定义预设
*/
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,
};
}
/**
* 验证纹理文件
* @param {File} file 要验证的文件
* @returns {Boolean} 是否为有效的纹理文件
*/
validateTextureFile(file) {
if (!file) {
console.warn("文件不存在");
return false;
}
// 检查文件类型
if (!file.type.startsWith("image/")) {
console.warn("文件类型无效,必须是图片文件");
return false;
}
// 检查文件大小(限制为 10MB
const maxSize = 10 * 1024 * 1024; // 10MB
if (file.size > maxSize) {
console.warn("文件大小超过限制10MB");
return false;
}
// 检查支持的图片格式
const supportedTypes = [
"image/jpeg",
"image/jpg",
"image/png",
"image/webp",
"image/svg+xml",
"image/bmp",
"image/gif",
];
if (!supportedTypes.includes(file.type)) {
console.warn("不支持的图片格式");
return false;
}
return true;
}
}
// 创建单例实例
const texturePresetManager = new TexturePresetManager();
// 导出单例
export default texturePresetManager;