优化
This commit is contained in:
7
index.js
7
index.js
@ -220,9 +220,10 @@ export const executeEvent = async (dropzone_data, result, sku) => {
|
|||||||
enable: true,
|
enable: true,
|
||||||
axis: rotation.y === 0 || rotation.y === 180 ? 'x' : 'z',
|
axis: rotation.y === 0 || rotation.y === 180 ? 'x' : 'z',
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
boundaryConstraint: true, // 启用边界限制
|
// snapToZone: true, // 开关1:拖拽吸附到最近的分割区域
|
||||||
snapToZone: false, // 启用拖拽吸附到最近的分割区域
|
// returnWhenOutOfBounds: false, // 开关2:拖拽到区域外时返回原位置
|
||||||
onOccupiedZone: '' // 拖拽到已占用区域时的行为:'return' 返回原位置,'replace' 替换目标位置的模型(默认 'return') "" 允许重叠
|
// handleOccupiedZone: true, // 开关3:处理已占用区域(false=允许重叠)
|
||||||
|
// occupiedZoneAction: 'return' // 当开关3=true时的行为:'return'=返回原位置,'replace'=替换
|
||||||
},
|
},
|
||||||
transform: {
|
transform: {
|
||||||
position: position,
|
position: position,
|
||||||
|
|||||||
@ -11,9 +11,10 @@ export interface DragConfig {
|
|||||||
enable: boolean;
|
enable: boolean;
|
||||||
axis?: 'x' | 'y' | 'z' | 'xy' | 'xz' | 'yz' | 'xyz';
|
axis?: 'x' | 'y' | 'z' | 'xy' | 'xz' | 'yz' | 'xyz';
|
||||||
step?: number;
|
step?: number;
|
||||||
boundaryConstraint?: boolean; // 边界限制:拖拽不得超出当前墙面的放置区域
|
|
||||||
snapToZone?: boolean; // 拖拽吸附:松开时自动吸附到最近的分割区域
|
snapToZone?: boolean; // 拖拽吸附:松开时自动吸附到最近的分割区域
|
||||||
onOccupiedZone?: 'return' | 'replace' | ''; // 拖拽到已占用区域时的行为:'return' 返回原位置,'replace' 替换目标位置的模型,'' 什么都不做(允许重叠)
|
returnWhenOutOfBounds?: boolean; // 拖拽到区域外时返回原位置
|
||||||
|
handleOccupiedZone?: boolean; // 拖拽到已占用区域时的处理:true=返回原位置或替换,false=允许重叠
|
||||||
|
occupiedZoneAction?: 'return' | 'replace'; // 当 handleOccupiedZone=true 时的具体行为:'return' 返回原位置,'replace' 替换目标位置的模型
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,7 +134,7 @@ export class AppModelDrag extends Monobehiver {
|
|||||||
hasShownZones = false;
|
hasShownZones = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听拖拽中事件(用于边界限制和延迟显示分割区域)
|
// 监听拖拽中事件(用于延迟显示分割区域)
|
||||||
pointerDragBehavior.onDragObservable.add((event) => {
|
pointerDragBehavior.onDragObservable.add((event) => {
|
||||||
// 检查是否实际移动了
|
// 检查是否实际移动了
|
||||||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
||||||
@ -146,11 +147,6 @@ export class AppModelDrag extends Monobehiver {
|
|||||||
hasShownZones = true;
|
hasShownZones = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用边界限制
|
|
||||||
if (dragInfo.config.boundaryConstraint) {
|
|
||||||
this.applyBoundaryConstraint(modelId, event.dragPlanePoint);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听拖拽结束事件
|
// 监听拖拽结束事件
|
||||||
@ -337,76 +333,6 @@ export class AppModelDrag extends Monobehiver {
|
|||||||
appDropZone.hide();
|
appDropZone.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 应用边界限制
|
|
||||||
* @param modelId 模型ID
|
|
||||||
* @param dragPoint 拖拽点位置
|
|
||||||
*/
|
|
||||||
private applyBoundaryConstraint(modelId: string, dragPoint: Vector3): void {
|
|
||||||
const meshes = this.mainApp.appModel?.modelDic?.Get(modelId);
|
|
||||||
if (!meshes || !meshes.length) return;
|
|
||||||
|
|
||||||
const rootMesh = meshes[0];
|
|
||||||
const appDropZone = this.mainApp.appDropZone;
|
|
||||||
if (!appDropZone) return;
|
|
||||||
|
|
||||||
// 查找模型所在的墙面
|
|
||||||
let wallName: string | null = null;
|
|
||||||
appDropZone['zoneModelMap']?.forEach((id: string, zoneKey: string) => {
|
|
||||||
if (id === modelId) {
|
|
||||||
const match = zoneKey.match(/^(.+)\[(\d+)\]$/);
|
|
||||||
if (match) {
|
|
||||||
wallName = match[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!wallName) return;
|
|
||||||
|
|
||||||
// 获取该墙面的所有分割区域
|
|
||||||
const wallZones = appDropZone.getZonesByWall(wallName);
|
|
||||||
if (!wallZones.length) return;
|
|
||||||
|
|
||||||
// 计算墙面的边界(所有分割区域的包围盒)
|
|
||||||
let minX = Number.POSITIVE_INFINITY;
|
|
||||||
let maxX = Number.NEGATIVE_INFINITY;
|
|
||||||
let minZ = Number.POSITIVE_INFINITY;
|
|
||||||
let maxZ = Number.NEGATIVE_INFINITY;
|
|
||||||
|
|
||||||
wallZones.forEach(zone => {
|
|
||||||
const halfWidth = zone.width / 2;
|
|
||||||
const halfHeight = zone.height / 2;
|
|
||||||
|
|
||||||
// 根据法线方向计算边界
|
|
||||||
if (Math.abs(zone.normal.x) > 0.5) {
|
|
||||||
// 左右墙面(法线沿X轴)
|
|
||||||
minZ = Math.min(minZ, zone.center.z - halfWidth);
|
|
||||||
maxZ = Math.max(maxZ, zone.center.z + halfWidth);
|
|
||||||
} else if (Math.abs(zone.normal.z) > 0.5) {
|
|
||||||
// 前后墙面(法线沿Z轴)
|
|
||||||
minX = Math.min(minX, zone.center.x - halfWidth);
|
|
||||||
maxX = Math.max(maxX, zone.center.x + halfWidth);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 限制模型位置在边界内
|
|
||||||
const currentPos = rootMesh.position;
|
|
||||||
const constrainedPos = currentPos.clone();
|
|
||||||
|
|
||||||
if (minX !== Number.POSITIVE_INFINITY && maxX !== Number.NEGATIVE_INFINITY) {
|
|
||||||
constrainedPos.x = Math.max(minX, Math.min(maxX, currentPos.x));
|
|
||||||
}
|
|
||||||
if (minZ !== Number.POSITIVE_INFINITY && maxZ !== Number.NEGATIVE_INFINITY) {
|
|
||||||
constrainedPos.z = Math.max(minZ, Math.min(maxZ, currentPos.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果位置被限制,更新模型位置
|
|
||||||
if (!constrainedPos.equals(currentPos)) {
|
|
||||||
rootMesh.position.copyFrom(constrainedPos);
|
|
||||||
console.log(`[边界限制] 模型 ${modelId} 位置被限制在边界内`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将模型吸附到最近的分割区域
|
* 将模型吸附到最近的分割区域
|
||||||
* @param modelId 模型ID
|
* @param modelId 模型ID
|
||||||
@ -454,97 +380,55 @@ export class AppModelDrag extends Monobehiver {
|
|||||||
|
|
||||||
// 获取拖拽配置
|
// 获取拖拽配置
|
||||||
const dragInfo = this.modelDragMap.get(modelId);
|
const dragInfo = this.modelDragMap.get(modelId);
|
||||||
|
const returnWhenOutOfBounds = dragInfo?.config.returnWhenOutOfBounds ?? false;
|
||||||
|
const handleOccupiedZone = dragInfo?.config.handleOccupiedZone ?? false;
|
||||||
|
const occupiedZoneAction = dragInfo?.config.occupiedZoneAction ?? 'return';
|
||||||
|
|
||||||
// 如果启用了边界约束,检查模型是否在墙面边界内
|
// 检查是否拖拽到区域外
|
||||||
if (dragInfo?.config.boundaryConstraint) {
|
let isOutOfBounds = false;
|
||||||
// 计算墙面的边界
|
|
||||||
let minX = Number.POSITIVE_INFINITY;
|
|
||||||
let maxX = Number.NEGATIVE_INFINITY;
|
|
||||||
let minZ = Number.POSITIVE_INFINITY;
|
|
||||||
let maxZ = Number.NEGATIVE_INFINITY;
|
|
||||||
|
|
||||||
wallZones.forEach(zone => {
|
// 计算墙面的边界
|
||||||
const halfWidth = zone.width / 2;
|
let minX = Number.POSITIVE_INFINITY;
|
||||||
|
let maxX = Number.NEGATIVE_INFINITY;
|
||||||
|
let minZ = Number.POSITIVE_INFINITY;
|
||||||
|
let maxZ = Number.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
// 根据法线方向计算边界
|
wallZones.forEach(zone => {
|
||||||
if (Math.abs(zone.normal.x) > 0.5) {
|
const halfWidth = zone.width / 2;
|
||||||
// 左右墙面(法线沿X轴)
|
|
||||||
minZ = Math.min(minZ, zone.center.z - halfWidth);
|
|
||||||
maxZ = Math.max(maxZ, zone.center.z + halfWidth);
|
|
||||||
} else if (Math.abs(zone.normal.z) > 0.5) {
|
|
||||||
// 前后墙面(法线沿Z轴)
|
|
||||||
minX = Math.min(minX, zone.center.x - halfWidth);
|
|
||||||
maxX = Math.max(maxX, zone.center.x + halfWidth);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 检查当前位置是否在边界内
|
// 根据法线方向计算边界
|
||||||
const currentPos = rootMesh.position;
|
if (Math.abs(zone.normal.x) > 0.5) {
|
||||||
let isOutOfBounds = false;
|
// 左右墙面(法线沿X轴)
|
||||||
|
minZ = Math.min(minZ, zone.center.z - halfWidth);
|
||||||
if (minX !== Number.POSITIVE_INFINITY && maxX !== Number.NEGATIVE_INFINITY) {
|
maxZ = Math.max(maxZ, zone.center.z + halfWidth);
|
||||||
if (currentPos.x < minX || currentPos.x > maxX) {
|
} else if (Math.abs(zone.normal.z) > 0.5) {
|
||||||
isOutOfBounds = true;
|
// 前后墙面(法线沿Z轴)
|
||||||
}
|
minX = Math.min(minX, zone.center.x - halfWidth);
|
||||||
|
maxX = Math.max(maxX, zone.center.x + halfWidth);
|
||||||
}
|
}
|
||||||
if (minZ !== Number.POSITIVE_INFINITY && maxZ !== Number.NEGATIVE_INFINITY) {
|
});
|
||||||
if (currentPos.z < minZ || currentPos.z > maxZ) {
|
|
||||||
isOutOfBounds = true;
|
// 检查当前位置是否在边界内
|
||||||
}
|
const currentPos = rootMesh.position;
|
||||||
|
|
||||||
|
if (minX !== Number.POSITIVE_INFINITY && maxX !== Number.NEGATIVE_INFINITY) {
|
||||||
|
if (currentPos.x < minX || currentPos.x > maxX) {
|
||||||
|
isOutOfBounds = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 如果超出边界,回到原来的区域
|
if (minZ !== Number.POSITIVE_INFINITY && maxZ !== Number.NEGATIVE_INFINITY) {
|
||||||
if (isOutOfBounds && originalZoneIndex !== -1) {
|
if (currentPos.z < minZ || currentPos.z > maxZ) {
|
||||||
console.log(`[拖拽吸附] 模型 ${modelId} 超出边界,检查原区域 ${originalZoneIndex}`);
|
isOutOfBounds = true;
|
||||||
|
|
||||||
// 检查原区域是否已被其他模型占用
|
|
||||||
const originalZoneKey = `${wallName}[${originalZoneIndex}]`;
|
|
||||||
const occupyingModelId = appDropZone['zoneModelMap']?.get(originalZoneKey);
|
|
||||||
|
|
||||||
if (!occupyingModelId || occupyingModelId === modelId) {
|
|
||||||
// 原区域空闲或者就是当前模型,回到原区域
|
|
||||||
closestZoneIndex = originalZoneIndex;
|
|
||||||
} else {
|
|
||||||
// 原区域已被占用,找最近的空闲区域
|
|
||||||
console.log(`[拖拽吸附] 原区域已被模型 ${occupyingModelId} 占用,寻找空闲区域`);
|
|
||||||
|
|
||||||
let foundFreeZone = false;
|
|
||||||
for (let i = 0; i < wallZones.length; i++) {
|
|
||||||
const zoneKey = `${wallName}[${i}]`;
|
|
||||||
const occupant = appDropZone['zoneModelMap']?.get(zoneKey);
|
|
||||||
|
|
||||||
if (!occupant || occupant === modelId) {
|
|
||||||
// 找到空闲区域
|
|
||||||
closestZoneIndex = i;
|
|
||||||
foundFreeZone = true;
|
|
||||||
console.log(`[拖拽吸附] 找到空闲区域 ${i}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有空闲区域,保持在原区域(会触发交换逻辑)
|
|
||||||
if (!foundFreeZone) {
|
|
||||||
console.log(`[拖拽吸附] 没有空闲区域,回到原区域 ${originalZoneIndex}`);
|
|
||||||
closestZoneIndex = originalZoneIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetZone = wallZones[closestZoneIndex];
|
// 处理超出边界的情况(开关2:returnWhenOutOfBounds)
|
||||||
|
if (isOutOfBounds) {
|
||||||
|
console.log(`[拖拽吸附] 模型 ${modelId} 超出边界`);
|
||||||
|
|
||||||
// 检查目标区域是否已被其他模型占用
|
if (returnWhenOutOfBounds) {
|
||||||
const targetZoneKey = `${wallName}[${closestZoneIndex}]`;
|
// 启用了边界返回,回到原来的区域
|
||||||
const occupyingModelId = appDropZone['zoneModelMap']?.get(targetZoneKey);
|
console.log(`[拖拽吸附] 启用边界返回,回到原区域 ${originalZoneIndex}`);
|
||||||
const onOccupiedZone = dragInfo?.config.onOccupiedZone || 'return';
|
|
||||||
|
|
||||||
if (occupyingModelId && occupyingModelId !== modelId) {
|
|
||||||
// 目标区域已被其他模型占用
|
|
||||||
console.log(`[拖拽吸附] 目标区域 ${closestZoneIndex} 已被模型 ${occupyingModelId} 占用`);
|
|
||||||
|
|
||||||
if (onOccupiedZone === 'return') {
|
|
||||||
// 返回原位置
|
|
||||||
console.log(`[拖拽吸附] 配置为返回原位置,回到区域 ${originalZoneIndex}`);
|
|
||||||
if (originalZoneIndex !== -1) {
|
if (originalZoneIndex !== -1) {
|
||||||
const originalZone = wallZones[originalZoneIndex];
|
const originalZone = wallZones[originalZoneIndex];
|
||||||
if (originalZone) {
|
if (originalZone) {
|
||||||
@ -556,16 +440,56 @@ export class AppModelDrag extends Monobehiver {
|
|||||||
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||||||
rootMesh.rotation.y = angle;
|
rootMesh.rotation.y = angle;
|
||||||
|
|
||||||
console.log(`[拖拽吸附] 模型 ${modelId} 返回原区域 ${originalZoneIndex}`);
|
console.log(`[拖拽吸附] 模型 ${modelId} 已返回原区域 ${originalZoneIndex}`);
|
||||||
return; // 不更新映射,保持原映射
|
return; // 不更新映射,保持原映射
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (onOccupiedZone === 'replace') {
|
} else {
|
||||||
// 替换目标位置的模型(继续执行后面的逻辑)
|
// 未启用边界返回,保持当前位置,不做吸附
|
||||||
console.log(`[拖拽吸附] 配置为替换模型,将替换模型 ${occupyingModelId}`);
|
console.log(`[拖拽吸附] 未启用边界返回,保持当前位置,不做吸附`);
|
||||||
} else if (onOccupiedZone === '') {
|
// 更新映射关系(可能移出了原区域)
|
||||||
// 什么都不做,允许重叠(继续执行后面的逻辑,但不会触发替换)
|
this.updateModelZoneMapping(modelId);
|
||||||
console.log(`[拖拽吸附] 配置为允许重叠,继续放置`);
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetZone = wallZones[closestZoneIndex];
|
||||||
|
|
||||||
|
// 检查目标区域是否已被其他模型占用(开关2:handleOccupiedZone)
|
||||||
|
const targetZoneKey = `${wallName}[${closestZoneIndex}]`;
|
||||||
|
const occupyingModelId = appDropZone['zoneModelMap']?.get(targetZoneKey);
|
||||||
|
|
||||||
|
if (occupyingModelId && occupyingModelId !== modelId) {
|
||||||
|
// 目标区域已被其他模型占用
|
||||||
|
console.log(`[拖拽吸附] 目标区域 ${closestZoneIndex} 已被模型 ${occupyingModelId} 占用`);
|
||||||
|
|
||||||
|
if (handleOccupiedZone) {
|
||||||
|
// 启用了占用区域处理
|
||||||
|
if (occupiedZoneAction === 'return') {
|
||||||
|
// 返回原位置
|
||||||
|
console.log(`[拖拽吸附] 配置为返回原位置,回到区域 ${originalZoneIndex}`);
|
||||||
|
if (originalZoneIndex !== -1) {
|
||||||
|
const originalZone = wallZones[originalZoneIndex];
|
||||||
|
if (originalZone) {
|
||||||
|
const offsetDistance = -0.05;
|
||||||
|
const returnPosition = originalZone.center.add(originalZone.normal.scale(offsetDistance));
|
||||||
|
rootMesh.position.copyFrom(returnPosition);
|
||||||
|
|
||||||
|
const targetDirection = originalZone.normal.scale(-1);
|
||||||
|
const angle = Math.atan2(targetDirection.x, targetDirection.z);
|
||||||
|
rootMesh.rotation.y = angle;
|
||||||
|
|
||||||
|
console.log(`[拖拽吸附] 模型 ${modelId} 返回原区域 ${originalZoneIndex}`);
|
||||||
|
return; // 不更新映射,保持原映射
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (occupiedZoneAction === 'replace') {
|
||||||
|
// 替换目标位置的模型(继续执行后面的逻辑)
|
||||||
|
console.log(`[拖拽吸附] 配置为替换模型,将替换模型 ${occupyingModelId}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 未启用占用区域处理,允许重叠(继续执行后面的逻辑)
|
||||||
|
console.log(`[拖拽吸附] 未启用占用区域处理,允许重叠`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,13 +602,14 @@ export class AppModelDrag extends Monobehiver {
|
|||||||
|
|
||||||
// 获取拖拽配置
|
// 获取拖拽配置
|
||||||
const dragInfo = this.modelDragMap.get(modelId);
|
const dragInfo = this.modelDragMap.get(modelId);
|
||||||
const onOccupiedZone = dragInfo?.config.onOccupiedZone || 'return';
|
const handleOccupiedZone = dragInfo?.config.handleOccupiedZone ?? false;
|
||||||
|
const occupiedZoneAction = dragInfo?.config.occupiedZoneAction ?? 'return';
|
||||||
|
|
||||||
if (existingModelId && existingModelId !== modelId) {
|
if (existingModelId && existingModelId !== modelId) {
|
||||||
console.log(`[边界检测] 目标区域 ${closestZoneIndex} 已有模型 ${existingModelId}`);
|
console.log(`[边界检测] 目标区域 ${closestZoneIndex} 已有模型 ${existingModelId}`);
|
||||||
|
|
||||||
// 只有在 'replace' 模式下才交换位置
|
// 只有在启用占用区域处理且为 'replace' 模式下才交换位置
|
||||||
if (onOccupiedZone === 'replace') {
|
if (handleOccupiedZone && occupiedZoneAction === 'replace') {
|
||||||
console.log(`[边界检测] 配置为替换模式,交换位置`);
|
console.log(`[边界检测] 配置为替换模式,交换位置`);
|
||||||
|
|
||||||
// 将原有模型移动到旧位置
|
// 将原有模型移动到旧位置
|
||||||
@ -712,9 +637,8 @@ export class AppModelDrag extends Monobehiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (onOccupiedZone === '') {
|
} else {
|
||||||
// 允许重叠,不做任何处理
|
console.log(`[边界检测] 未启用替换模式或未启用占用区域处理,允许重叠`);
|
||||||
console.log(`[边界检测] 配置为允许重叠,不交换位置`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -88,6 +88,12 @@ class AppRay extends Monobehiver {
|
|||||||
this.newPoint.set(pointerEvent.clientX, 0, pointerEvent.clientY);
|
this.newPoint.set(pointerEvent.clientX, 0, pointerEvent.clientY);
|
||||||
const distance = Vector3.Distance(this.oldPoint, this.newPoint);
|
const distance = Vector3.Distance(this.oldPoint, this.newPoint);
|
||||||
|
|
||||||
|
// 如果是长按后松手,隐藏分割区域
|
||||||
|
if (this.isLongPress) {
|
||||||
|
console.log('[长按] 松手,隐藏分割区域');
|
||||||
|
this.mainApp.appDropZone.hide();
|
||||||
|
}
|
||||||
|
|
||||||
// 只有在没有移动且不是长按的情况下才处理单击
|
// 只有在没有移动且不是长按的情况下才处理单击
|
||||||
if (distance < 5 && !this.isLongPress) {
|
if (distance < 5 && !this.isLongPress) {
|
||||||
this.handleSingleClick(pointerEvent, pickInfo);
|
this.handleSingleClick(pointerEvent, pickInfo);
|
||||||
|
|||||||
Reference in New Issue
Block a user