feat(web-platform): release V0.8.3 常规界面优化
web-platform-release / Build and publish release (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
web-platform-release / Build and publish release (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
This commit is contained in:
@@ -67,6 +67,7 @@ function objectPreview(object: EditableMapObject): THREE.Group {
|
||||
group.name = `__platform_map_editor_${object.id}`;
|
||||
group.userData.mapEditorObjectId = object.id;
|
||||
group.userData.mapEditorLocked = object.placementMode === 'locked';
|
||||
group.userData.mapEditorPlacementMode = object.placementMode;
|
||||
group.position.fromArray(object.pose.position);
|
||||
group.quaternion.set(
|
||||
object.pose.quaternion[1],
|
||||
@@ -132,8 +133,25 @@ export class MapEditorLayer {
|
||||
private readonly pointer = new THREE.Vector2();
|
||||
private readonly objects = new Map<string, THREE.Group>();
|
||||
private readonly previewInstances = new Map<string, THREE.Group>();
|
||||
private readonly surfaceIndicator = new THREE.Group();
|
||||
private readonly surfaceRingMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0x34d399,
|
||||
transparent: true,
|
||||
opacity: 0.42,
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
side: THREE.DoubleSide,
|
||||
});
|
||||
private readonly surfaceLineMaterial = new THREE.LineBasicMaterial({
|
||||
color: 0x6ee7b7,
|
||||
transparent: true,
|
||||
opacity: 0.7,
|
||||
depthTest: false,
|
||||
});
|
||||
private selectedId: string | null = null;
|
||||
private documentLoaded = false;
|
||||
private surfaceIndicatorScale = 0.25;
|
||||
private surfacePulseStarted = 0;
|
||||
|
||||
constructor(
|
||||
private readonly scene: THREE.Scene,
|
||||
@@ -143,7 +161,25 @@ export class MapEditorLayer {
|
||||
) {
|
||||
this.previewGroup.name = '__platform_map_editor_scene_previews__';
|
||||
this.group.name = '__platform_map_editor__';
|
||||
scene.add(this.previewGroup, this.group);
|
||||
this.surfaceIndicator.name = '__platform_snap_surface_indicator__';
|
||||
const surfaceRing = new THREE.Mesh(
|
||||
new THREE.RingGeometry(0.72, 1, 48),
|
||||
this.surfaceRingMaterial,
|
||||
);
|
||||
surfaceRing.renderOrder = 110;
|
||||
const surfaceCross = new THREE.LineSegments(
|
||||
new THREE.BufferGeometry().setFromPoints([
|
||||
new THREE.Vector3(-1.18, 0, 0),
|
||||
new THREE.Vector3(1.18, 0, 0),
|
||||
new THREE.Vector3(0, -1.18, 0),
|
||||
new THREE.Vector3(0, 1.18, 0),
|
||||
]),
|
||||
this.surfaceLineMaterial,
|
||||
);
|
||||
surfaceCross.renderOrder = 110;
|
||||
this.surfaceIndicator.add(surfaceRing, surfaceCross);
|
||||
this.surfaceIndicator.visible = false;
|
||||
scene.add(this.previewGroup, this.group, this.surfaceIndicator);
|
||||
this.transform = new TransformControls(camera, domElement);
|
||||
this.transform.setSpace('world');
|
||||
this.transform.setSize(0.8);
|
||||
@@ -164,6 +200,39 @@ export class MapEditorLayer {
|
||||
return this.documentLoaded || this.previewInstances.size > 0;
|
||||
}
|
||||
|
||||
selectedBoundingSphere(): THREE.Sphere | null {
|
||||
const selected = this.selectedId ? this.objects.get(this.selectedId) : undefined;
|
||||
if (!selected) return null;
|
||||
selected.updateWorldMatrix(true, true);
|
||||
const bounds = new THREE.Box3().setFromObject(selected);
|
||||
if (bounds.isEmpty()) return null;
|
||||
return bounds.getBoundingSphere(new THREE.Sphere());
|
||||
}
|
||||
|
||||
flashSurfaceAlignment(id?: string): void {
|
||||
if (id && id !== this.selectedId) return;
|
||||
const selected = this.selectedId ? this.objects.get(this.selectedId) : undefined;
|
||||
if (!selected || selected.userData.mapEditorLocked) return;
|
||||
this.positionSurfaceIndicator(selected);
|
||||
this.startSurfacePulse();
|
||||
this.surfaceIndicator.visible = true;
|
||||
}
|
||||
|
||||
update(now: number): void {
|
||||
const selected = this.selectedId ? this.objects.get(this.selectedId) : undefined;
|
||||
if (!selected || selected.userData.mapEditorLocked) {
|
||||
this.surfaceIndicator.visible = false;
|
||||
return;
|
||||
}
|
||||
this.positionSurfaceIndicator(selected);
|
||||
const elapsed = now - this.surfacePulseStarted;
|
||||
const pulse = elapsed >= 0 && elapsed < 720 ? 1 - elapsed / 720 : 0;
|
||||
const wave = pulse * (0.16 + Math.sin((elapsed / 720) * Math.PI * 4) * 0.08);
|
||||
this.surfaceIndicator.scale.setScalar(this.surfaceIndicatorScale * (1 + wave));
|
||||
this.surfaceRingMaterial.opacity = 0.32 + pulse * 0.38;
|
||||
this.surfaceLineMaterial.opacity = 0.5 + pulse * 0.4;
|
||||
}
|
||||
|
||||
setDocument(
|
||||
document: EditableMapDocument | null,
|
||||
instanceTransform: MapInstanceTransform = DEFAULT_MAP_INSTANCE_TRANSFORM,
|
||||
@@ -231,6 +300,7 @@ export class MapEditorLayer {
|
||||
}
|
||||
|
||||
selectObject(id: string | null, notify = false): void {
|
||||
const previousId = this.selectedId;
|
||||
this.selectedId = id && this.objects.has(id) ? id : null;
|
||||
this.transform.detach();
|
||||
for (const [objectId, root] of this.objects)
|
||||
@@ -246,7 +316,11 @@ export class MapEditorLayer {
|
||||
});
|
||||
const selected = this.selectedId ? this.objects.get(this.selectedId) : undefined;
|
||||
const editable = Boolean(selected && !selected.userData.mapEditorLocked);
|
||||
if (selected && editable) this.transform.attach(selected);
|
||||
if (selected && editable) {
|
||||
this.transform.attach(selected);
|
||||
this.positionSurfaceIndicator(selected);
|
||||
if (previousId !== this.selectedId) this.startSurfacePulse();
|
||||
} else this.surfaceIndicator.visible = false;
|
||||
this.helper.visible = editable;
|
||||
if (notify) this.callbacks.onSelect(this.selectedId);
|
||||
}
|
||||
@@ -285,6 +359,32 @@ export class MapEditorLayer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private startSurfacePulse(): void {
|
||||
const now = performance.now();
|
||||
this.surfacePulseStarted = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
|
||||
? now - 720
|
||||
: now;
|
||||
}
|
||||
|
||||
private positionSurfaceIndicator(selected: THREE.Group): void {
|
||||
selected.updateWorldMatrix(true, true);
|
||||
const bounds = new THREE.Box3().setFromObject(selected);
|
||||
if (bounds.isEmpty()) {
|
||||
this.surfaceIndicator.visible = false;
|
||||
return;
|
||||
}
|
||||
const center = bounds.getCenter(new THREE.Vector3());
|
||||
const size = bounds.getSize(new THREE.Vector3());
|
||||
const placementMode = selected.userData.mapEditorPlacementMode;
|
||||
const color = placementMode === 'gravity' ? 0x38bdf8 : 0x34d399;
|
||||
this.surfaceRingMaterial.color.setHex(color);
|
||||
this.surfaceLineMaterial.color.setHex(color);
|
||||
this.surfaceIndicatorScale = Math.min(1.5, Math.max(0.16, Math.max(size.x, size.y) * 0.62));
|
||||
this.surfaceIndicator.position.set(center.x, center.y, bounds.min.z + 0.004);
|
||||
this.surfaceIndicator.scale.setScalar(this.surfaceIndicatorScale);
|
||||
this.surfaceIndicator.visible = true;
|
||||
}
|
||||
|
||||
private commitTransform(): void {
|
||||
if (!this.selectedId) return;
|
||||
const object = this.objects.get(this.selectedId);
|
||||
@@ -323,6 +423,8 @@ export class MapEditorLayer {
|
||||
this.clearPreviewInstances();
|
||||
this.group.position.set(0, 0, 0);
|
||||
this.group.rotation.set(0, 0, 0);
|
||||
this.surfaceIndicator.visible = false;
|
||||
this.surfacePulseStarted = 0;
|
||||
this.transform.enabled = false;
|
||||
this.callbacks.onDragging(false);
|
||||
}
|
||||
@@ -332,6 +434,11 @@ export class MapEditorLayer {
|
||||
this.callbacks.onDragging(false);
|
||||
this.transform.dispose();
|
||||
this.helper.removeFromParent();
|
||||
for (const child of this.surfaceIndicator.children)
|
||||
if (child instanceof THREE.Mesh || child instanceof THREE.Line) child.geometry.dispose();
|
||||
this.surfaceRingMaterial.dispose();
|
||||
this.surfaceLineMaterial.dispose();
|
||||
this.surfaceIndicator.removeFromParent();
|
||||
this.previewGroup.removeFromParent();
|
||||
this.group.removeFromParent();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user