@metav_xly/decal-editor
v1.1.3
Published
Vue3 + Element UI 贴花编辑器组件,基于 Three.js
Readme
@metav_xly/decal-editor
基于 Vue3 + Element UI + Three.js 的贴花编辑器组件
特性
- 🎨 基于 Three.js 的 3D 贴花编辑功能
- 🖱️ 鼠标点击添加贴花
- ⚙️ 实时编辑贴花属性(位置、旋转、缩放、纹理类型)
- 📋 贴花列表管理
- 💾 配置导入导出
- 🎯 支持多种纹理类型
- 📦 TypeScript 支持
- 🔧 可自定义材质构造器
安装
npm install @metav_xly/decal-editor
# 或
pnpm add @metav_xly/decal-editor
# 或
yarn add @metav_xly/decal-editor使用方法
全局注册
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import DecalEditor from '@metav_xly/decal-editor'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.use(DecalEditor)
app.mount('#app')局部使用
<template>
<DecalEditorComponent
:model-url="modelUrl"
:textures="textures"
@decal-added="onDecalAdded"
@decal-selected="onDecalSelected"
@decal-updated="onDecalUpdated"
@decal-removed="onDecalRemoved"
@decals-cleared="onDecalsCleared"
/>
</template>
<script setup>
import { ref } from 'vue'
import { DecalEditorComponent } from '@metav_xly/decal-editor'
// 准备纹理配置数据
const textures = ref([
{
label: '贴花类型1',
diffuseUrl: '/textures/decal1-diffuse.png',
normalUrl: '/textures/decal1-normal.jpg'
},
{
label: '贴花类型2',
diffuseUrl: '/textures/decal2-diffuse.png',
normalUrl: '/textures/decal2-normal.jpg'
},
{
label: '贴花类型3',
diffuseUrl: '/textures/decal3-diffuse.png',
normalUrl: '/textures/decal3-normal.jpg'
}
])
const modelUrl = ref('/models/your-model.glb')
// 事件处理
const onDecalAdded = (decal, index) => {
console.log('贴花已添加:', decal, index)
}
const onDecalSelected = (decal, index) => {
console.log('贴花已选中:', decal, index)
}
const onDecalUpdated = (decal, index) => {
console.log('贴花已更新:', decal, index)
}
const onDecalRemoved = (index) => {
console.log('贴花已删除:', index)
}
const onDecalsCleared = () => {
console.log('所有贴花已清空')
}
</script>使用核心类
import * as THREE from 'three'
import { DecalEditor, DecalLoader } from '@metav_xly/decal-editor'
// 创建 Three.js 场景
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
// 准备纹理配置
const textures = [
{
label: '贴花类型1',
diffuseUrl: '/textures/decal-diffuse.png',
normalUrl: '/textures/decal-normal.jpg'
},
{
label: '贴花类型2',
diffuseUrl: '/textures/decal2-diffuse.png',
normalUrl: '/textures/decal2-normal.jpg'
}
]
// 创建贴花编辑器
const decalEditor = new DecalEditor(scene, camera, renderer, textures)
// 设置目标网格
decalEditor.setTargetMesh(yourMesh)
// 设置参数
decalEditor.setParams({
minScale: 0.1,
maxScale: 5.0,
textureType: '0',
rotate: false,
helperSize: 20,
normalLength: 20
})
// 监听事件
decalEditor.onDecalAdded = (decal, index) => {
console.log('贴花已添加:', decal, index)
}
decalEditor.onDecalSelected = (decal, index) => {
console.log('贴花已选中:', decal, index)
}
decalEditor.onDecalUpdated = (decal, index) => {
console.log('贴花已更新:', decal, index)
}
decalEditor.onDecalRemoved = (index) => {
console.log('贴花已删除:', index)
}
decalEditor.onDecalsCleared = () => {
console.log('所有贴花已清空')
}
// 创建贴花加载器
const decalLoader = new DecalLoader(scene, textures)
// 加载配置
const config = {
version: '1.0',
decals: [
{
id: 0,
position: { x: 0, y: 0, z: 0 },
orientation: { x: 0, y: 0, z: 0 },
scale: 10,
textureType: '0',
targetName: 'mesh'
}
]
}
const loadedDecals = decalLoader.loadConfig(config, yourMesh)API 文档
DecalEditorComponent Props
| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|---------| | modelUrl | string | '' | 3D 模型文件 URL | | textures | TextureConfig[] | [] | 纹理配置数组 |
DecalEditorComponent Events
| 事件名 | 参数 | 说明 | |--------|------|------| | decal-added | (decal: THREE.Mesh, index: number) | 贴花添加时触发 | | decal-selected | (decal: THREE.Mesh, index: number) | 贴花选中时触发 | | decal-updated | (decal: THREE.Mesh, index: number) | 贴花更新时触发 | | decal-removed | (index: number) | 贴花删除时触发 | | decals-cleared | () | 清空所有贴花时触发 |
TextureConfig 接口
interface TextureConfig {
label: string // 纹理标签
diffuseUrl?: string // 漫反射纹理 URL
normalUrl?: string // 法线纹理 URL
}TextureSet 接口
interface TextureSet {
diffuse?: THREE.Texture // 漫反射纹理
normal?: THREE.Texture // 法线纹理
label: string // 纹理标签
}DecalParams 接口
interface DecalParams {
minScale: number // 最小缩放值
maxScale: number // 最大缩放值
textureType: string // 纹理类型索引
rotate: boolean // 是否随机旋转
helperSize: number // 辅助器大小
normalLength: number // 法线长度
}DecalEditor 类方法
| 方法 | 参数 | 返回值 | 说明 | |------|------|--------|------| | setTargetMesh | (mesh: THREE.Object3D) | void | 设置目标网格 | | setParams | (params: Partial) | void | 设置参数 | | createDecal | () | THREE.Mesh | null | 创建贴花 | | selectDecal | (index: number) | void | 选择贴花 | | updateDecal | (index: number, properties: DecalProperties) | void | 更新贴花 | | removeDecal | (index: number) | void | 删除贴花 | | removeAllDecals | () | void | 删除所有贴花 | | exportConfig | () | any | 导出配置 | | getDecals | () | THREE.Mesh[] | 获取所有贴花 | | getSelectedDecal | () | THREE.Mesh | null | 获取选中的贴花 | | getSelectedDecalIndex | () | number | 获取选中贴花的索引 |
DecalLoader 类方法
| 方法 | 参数 | 返回值 | 说明 | |------|------|--------|------| | loadConfig | (config: DecalConfig, targetMesh: THREE.Object3D) | THREE.Mesh[] | 加载配置 | | validateConfig | (config: any) | boolean | 验证配置格式 |
自定义材质构造器
import * as THREE from 'three'
import { DecalEditor } from '@metav_xly/decal-editor'
const customMaterialConstructor = (textureSet, index) => {
const material = new THREE.MeshPhongMaterial({
transparent: true,
depthTest: true,
depthWrite: false,
polygonOffset: true,
polygonOffsetFactor: -4
})
if (textureSet.diffuse) {
material.map = textureSet.diffuse
}
if (textureSet.normal) {
material.normalMap = textureSet.normal
}
// 根据索引和标签自定义材质属性
if (index === 0) {
material.color.setHex(0xff0000)
}
// 根据纹理标签设置不同属性
if (textureSet.label.includes('金属')) {
material.shininess = 100
}
return material
}
// 准备纹理配置
const textures = [
{
label: '金属纹理',
diffuseUrl: '/textures/metal-diffuse.png',
normalUrl: '/textures/metal-normal.jpg'
}
]
const decalEditor = new DecalEditor(
scene,
camera,
renderer,
textures,
customMaterialConstructor
)开发
# 安装依赖
pnpm install
# 开发模式
pnpm dev
# 构建库
pnpm build:lib
# 类型检查
pnpm type-check许可证
MIT
