fl-web-component 2.0.5 → 2.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,60 +1,66 @@
1
1
  <template>
2
- <div id="fl-model"></div>
2
+ <div :id="containId" class="fl-model-containor"></div>
3
3
  </template>
4
4
  <script>
5
- var [
6
- renderer,
7
- scene,
8
- camera,
9
- cameraControls,
10
- instructions,
11
- raycaster,
12
- mouse,
13
- labelRenderer,
14
- lineTexture,
15
- curve,
16
- downRaycaster,
17
- velocity,
18
- direction,
19
- clock,
20
- pointControls,
21
- threeMeasure,
22
- modelGroup,
23
- animateId,
24
- scenePass,
25
- outlineComposer,
26
- outlinePass,
27
- renderTarget,
28
- sceneClock,
29
- bizToThreeMatrix,
30
- threeToBizMatrix,
31
- stats,
32
- ] = (function* (v) {
33
- while (true) yield v;
34
- })(null);
35
-
36
- var [lastTime, firstTime, fpsClock, timeStamp, progress, lastMiddleClickTime] = (function* (v) {
37
- while (true) yield v;
38
- })(0);
39
5
  var singleFrameTime = 1 / 30;
6
+ var dragThreshold = 5; // 拖拽阈值,像素单位
40
7
 
41
- var [
42
- roaming,
43
- firstPerSign,
44
- canJump,
45
- moveForward,
46
- moveBackward,
47
- moveLeft,
48
- moveRight,
49
- measureFlag,
50
- // rotatedSceneFlag,
51
- ] = (function* (v) {
52
- while (true) yield v;
53
- })(false);
54
-
55
- var [spaceUp] = (function* (v) {
56
- while (true) yield v;
57
- })(true);
8
+ // 根据场景包围盒动态计算的最大dolly距离(对角线长度)
9
+ let maxDollyDistance = Infinity;
10
+
11
+ // var lastMiddleClickTime = 0;
12
+ // var [
13
+ // renderer,
14
+ // scene,
15
+ // camera,
16
+ // cameraControls,
17
+ // instructions,
18
+ // raycaster,
19
+ // mouse,
20
+ // labelRenderer,
21
+ // lineTexture,
22
+ // curve,
23
+ // downRaycaster,
24
+ // velocity,
25
+ // direction,
26
+ // clock,
27
+ // pointControls,
28
+ // threeMeasure,
29
+ // modelGroup,
30
+ // animateId,
31
+ // scenePass,
32
+ // outlineComposer,
33
+ // outlinePass,
34
+ // renderTarget,
35
+ // sceneClock,
36
+ // bizToThreeMatrix,
37
+ // threeToBizMatrix,
38
+ // stats,
39
+ // ] = (function* (v) {
40
+ // while (true) yield v;
41
+ // })(null);
42
+
43
+ // var [tlastTime, firstTime, fpsClock,timeStamp, progress, lastMiddleClickTime] = (function* (v) {
44
+ // while (true) yield v;
45
+ // })(0);
46
+
47
+ // var [
48
+ // roaming,
49
+ // firstPerSign,
50
+ // canJump,
51
+ // moveForward,
52
+ // moveBackward,
53
+ // moveLeft,
54
+ // moveRight,
55
+ // measureFlag,
56
+ // // rotatedSceneFlag,
57
+ // ] = (function* (v) {
58
+ // while (true) yield v;
59
+ // })(false);
60
+
61
+ // var [spaceUp] = (function* (v) {
62
+ // while (true) yield v;
63
+ // })(true);
58
64
 
59
65
  const renderedThisFrame = new Set();
60
66
 
@@ -65,9 +71,9 @@ function markRendered(mesh) {
65
71
  }
66
72
 
67
73
  // 交互期间丢弃当前帧渲染的标记
68
- let skipNextRenderFrame = false;
74
+ // let skipNextRenderFrame = false;
69
75
  // 增强的交互检测标记
70
- let forceSkipRendering = false;
76
+ // let forceSkipRendering = false;
71
77
  // let interactionFrameCount = 0; // 交互期间跳过的帧数计数
72
78
 
73
79
  var clippingMesh = [],
@@ -82,15 +88,11 @@ var clippingMesh = [],
82
88
  mouseDownPosition = { x: 0, y: 0 }, // 鼠标按下时的位置
83
89
  mouseUpPosition = { x: 0, y: 0 }, // 鼠标抬起时的位置
84
90
  isDragging = false, // 是否正在拖拽
85
- dragThreshold = 5, // 拖拽阈值,像素单位
86
91
  sceneBoundingBox = null, // 场景包围盒
87
92
  // 包围盒显示相关变量
88
93
  sceneBoundingBoxHelper = null, // 场景包围盒辅助线
89
94
  boundingBoxVisible = false; // 包围盒是否可见
90
95
 
91
- // 根据场景包围盒动态计算的最大dolly距离(对角线长度)
92
- let maxDollyDistance = Infinity;
93
-
94
96
  var removeSpeed = 200,
95
97
  upSpeed = 200; //控制器移动速度 , //控制跳起时的速度
96
98
  var roamConfig = {
@@ -166,6 +168,10 @@ export default {
166
168
  return {};
167
169
  },
168
170
  },
171
+ containId: {
172
+ type: String,
173
+ default: 'fl-model',
174
+ },
169
175
  },
170
176
  data() {
171
177
  return {
@@ -196,7 +202,90 @@ export default {
196
202
  },
197
203
  };
198
204
  },
205
+ beforeCreate() {
206
+ this.spaceUp = true;
207
+ let arr = [
208
+ 'renderer',
209
+ 'scene',
210
+ 'camera',
211
+ 'cameraControls',
212
+ 'instructions',
213
+ 'raycaster',
214
+ 'mouse',
215
+ 'labelRenderer',
216
+ 'lineTexture',
217
+ 'curve',
218
+ 'downRaycaster',
219
+ 'velocity',
220
+ 'direction',
221
+ 'clock',
222
+ 'pointControls',
223
+ 'threeMeasure',
224
+ 'modelGroup',
225
+ 'animateId',
226
+ 'scenePass',
227
+ 'outlineComposer',
228
+ 'outlinePass',
229
+ 'renderTarget',
230
+ 'sceneClock',
231
+ 'bizToThreeMatrix',
232
+ 'threeToBizMatrix',
233
+ 'stats',
234
+ 'centeringDebounceTimer',
235
+ 'sceneBoundingBox',
236
+ 'sceneBoundingBoxHelper',
237
+ ];
238
+ arr.forEach(item => {
239
+ this[item] = null;
240
+ });
241
+ [
242
+ 'roaming',
243
+ 'firstPerSign',
244
+ 'canJump',
245
+ 'moveForward',
246
+ 'moveBackward',
247
+ 'moveLeft',
248
+ 'moveRight',
249
+ 'measureFlag',
250
+ 'skipNextRenderFrame',
251
+ 'forceSkipRendering',
252
+ 'userInteracting',
253
+ 'hasExecutedCentering',
254
+ 'needsCenteringAfterInteraction',
255
+ 'isDragging',
256
+ 'boundingBoxVisible',
257
+ ].forEach(item => {
258
+ this[item] = false;
259
+ });
260
+
261
+ ['clippingMesh', 'modelActive'].forEach(item => {
262
+ this[item] = [];
263
+ });
264
+ ['removeSpeed', 'upSpeed'].forEach(item => {
265
+ this[item] = 200;
266
+ });
267
+
268
+ this.gui = null;
269
+ this.roamConfig = {
270
+ loop: false,
271
+ speed: 0, // 最大值为3
272
+ name: '',
273
+ };
274
+ this.guiParams = {
275
+ x轴: 0,
276
+ y轴: 0,
277
+ z轴: 0,
278
+ '-x轴': 0,
279
+ '-y轴': 0,
280
+ '-z轴': 0,
281
+ };
282
+ },
199
283
  created() {
284
+ ['lastTime', 'firstTime', 'fpsClock', 'timeStamp', 'progress'].forEach(item => {
285
+ this[item] = 0;
286
+ });
287
+ this.mouseDownPosition = { x: 0, y: 0 };
288
+ this.mouseUpPosition = { x: 0, y: 0 };
200
289
  // 初始化非响应式的高频状态对象
201
290
  this.noObserver = {
202
291
  modelStateManager: {
@@ -256,20 +345,19 @@ export default {
256
345
  occlusionWorkerRequestMap: new Map(),
257
346
  occlusionWorkerRequestId: 0,
258
347
  };
259
-
348
+ this.modelGroups = [];
349
+ this.modelActions = [];
350
+ this.lastMiddleClickTime = 0;
260
351
  CameraControls.install({ THREE: this.THREE });
261
- bizToThreeMatrix = new this.THREE.Matrix4();
262
- bizToThreeMatrix.makeRotationX(-Math.PI / 2);
263
- threeToBizMatrix = new this.THREE.Matrix4();
264
- threeToBizMatrix.makeRotationX(Math.PI / 2);
265
- fpsClock = new this.THREE.Clock();
266
- raycaster = new this.THREE.Raycaster();
267
- sceneClock = new this.THREE.Clock();
268
- mouse = new this.THREE.Vector2();
269
- const initialRect = instructions
270
- ? instructions.getBoundingClientRect()
271
- : { width: window.innerWidth, height: window.innerHeight };
272
- renderTarget = new this.THREE.WebGLRenderTarget(initialRect.width, initialRect.height, {
352
+ this.bizToThreeMatrix = new this.THREE.Matrix4();
353
+ this.bizToThreeMatrix.makeRotationX(-Math.PI / 2);
354
+ this.threeToBizMatrix = new this.THREE.Matrix4();
355
+ this.threeToBizMatrix.makeRotationX(Math.PI / 2);
356
+ this.fpsClock = new this.THREE.Clock();
357
+ this.raycaster = new this.THREE.Raycaster();
358
+ this.sceneClock = new this.THREE.Clock();
359
+ this.mouse = new this.THREE.Vector2();
360
+ this.renderTarget = new this.THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
273
361
  minFilter: this.THREE.LinearFilter,
274
362
  magFilter: this.THREE.LinearFilter,
275
363
  format: this.THREE.RGBAFormat,
@@ -278,7 +366,7 @@ export default {
278
366
  });
279
367
  },
280
368
  mounted() {
281
- instructions = document.getElementById('fl-model');
369
+ this.instructions = document.getElementById(this.containId); // 'fl-model'
282
370
  this.initRender();
283
371
  this.initScene();
284
372
  this.initCamera();
@@ -293,11 +381,11 @@ export default {
293
381
  // 判断是设备是手机还是电脑
294
382
  let isMobileDevice = this.isMobileDevice();
295
383
  if (isMobileDevice) {
296
- renderer.domElement.addEventListener('pointerup', this.mouseClick, false);
297
- renderer.domElement.addEventListener('pointerdown', this.mouseDown, false);
384
+ this.renderer.domElement.addEventListener('pointerup', this.mouseClick, false);
385
+ this.renderer.domElement.addEventListener('pointerdown', this.mouseDown, false);
298
386
  } else {
299
- renderer.domElement.addEventListener('mouseup', this.mouseClick, false);
300
- renderer.domElement.addEventListener('mousedown', this.mouseDown, false);
387
+ this.renderer.domElement.addEventListener('mouseup', this.mouseClick, false);
388
+ this.renderer.domElement.addEventListener('mousedown', this.mouseDown, false);
301
389
  }
302
390
  this.animate();
303
391
  },
@@ -336,13 +424,13 @@ export default {
336
424
  return instancedMesh ? instancedMesh.visible === false : false;
337
425
  },
338
426
  pushOutlineTarget(target) {
339
- if (!outlinePass || !target) return;
340
- if (!outlinePass.selectedObjects.includes(target)) {
341
- outlinePass.selectedObjects.push(target);
427
+ if (!this.outlinePass || !target) return;
428
+ if (!this.outlinePass.selectedObjects.includes(target)) {
429
+ this.outlinePass.selectedObjects.push(target);
342
430
  }
343
431
  },
344
432
  ensureOutlineInstanceProxy(instancedMesh, instanceIndex) {
345
- if (!scene || !instancedMesh || !instancedMesh.isInstancedMesh) return null;
433
+ if (!this.scene || !instancedMesh || !instancedMesh.isInstancedMesh) return null;
346
434
  const state = this.noObserver;
347
435
  if (!state || !state.outlineInstanceProxyMap) return null;
348
436
  const instanceId = this.getInstanceId(instancedMesh, instanceIndex);
@@ -382,7 +470,7 @@ export default {
382
470
  instanceIndex,
383
471
  };
384
472
 
385
- scene.add(proxy);
473
+ this.scene.add(proxy);
386
474
  state.outlineInstanceProxyMap.set(key, proxy);
387
475
  return proxy;
388
476
  },
@@ -394,11 +482,11 @@ export default {
394
482
  if (!proxy) return null;
395
483
 
396
484
  state.outlineInstanceProxyMap.delete(key);
397
- if (outlinePass) {
398
- const idx = outlinePass.selectedObjects.indexOf(proxy);
399
- if (idx !== -1) outlinePass.selectedObjects.splice(idx, 1);
485
+ if (this.outlinePass) {
486
+ const idx = this.outlinePass.selectedObjects.indexOf(proxy);
487
+ if (idx !== -1) this.outlinePass.selectedObjects.splice(idx, 1);
400
488
  }
401
- if (scene) scene.remove(proxy);
489
+ if (this.scene) this.scene.remove(proxy);
402
490
  if (proxy.material) proxy.material.dispose && proxy.material.dispose();
403
491
  return proxy;
404
492
  },
@@ -589,7 +677,7 @@ export default {
589
677
  const box = new this.THREE.Box3(
590
678
  new this.THREE.Vector3(minX, minY, minZ),
591
679
  new this.THREE.Vector3(maxX, maxY, maxZ)
592
- ).applyMatrix4(bizToThreeMatrix);
680
+ ).applyMatrix4(this.bizToThreeMatrix);
593
681
  this.noObserver.sceneBoxes.set(documentId, box);
594
682
  } else {
595
683
  this.noObserver.sceneBoxes.delete(documentId);
@@ -605,8 +693,8 @@ export default {
605
693
  if (occScene && occScene.children && occScene.children.length > 0) {
606
694
  const occBox = new this.THREE.Box3().setFromObject(occScene);
607
695
  if (!occBox.isEmpty()) {
608
- sceneBoundingBox = occBox;
609
- return sceneBoundingBox;
696
+ this.sceneBoundingBox = occBox;
697
+ return this.sceneBoundingBox;
610
698
  }
611
699
  }
612
700
 
@@ -637,11 +725,11 @@ export default {
637
725
  Number.isFinite(maxY) &&
638
726
  Number.isFinite(maxZ)
639
727
  ) {
640
- sceneBoundingBox = new this.THREE.Box3(
728
+ this.sceneBoundingBox = new this.THREE.Box3(
641
729
  new this.THREE.Vector3(minX, minY, minZ),
642
730
  new this.THREE.Vector3(maxX, maxY, maxZ)
643
731
  );
644
- return sceneBoundingBox;
732
+ return this.sceneBoundingBox;
645
733
  }
646
734
  }
647
735
 
@@ -651,12 +739,12 @@ export default {
651
739
  for (let i = 1; i < boxes.length; i++) {
652
740
  firstBox.union(boxes[i]);
653
741
  }
654
- sceneBoundingBox = firstBox;
655
- return sceneBoundingBox;
742
+ this.sceneBoundingBox = firstBox;
743
+ return this.sceneBoundingBox;
656
744
  }
657
745
 
658
- sceneBoundingBox = new this.THREE.Box3();
659
- return sceneBoundingBox;
746
+ this.sceneBoundingBox = new this.THREE.Box3();
747
+ return this.sceneBoundingBox;
660
748
  },
661
749
  setBoxIndex(boxJson, documentId, isAdd = true) {
662
750
  if (!this._boxIndex) this._boxIndex = new Map();
@@ -665,7 +753,7 @@ export default {
665
753
  if (this.noObserver && this.noObserver.documentModelIds) {
666
754
  this.noObserver.documentModelIds.clear();
667
755
  }
668
- hasExecutedCentering = false;
756
+ this.hasExecutedCentering = false;
669
757
  this.buildOctreeFromBoxIndex();
670
758
  return;
671
759
  }
@@ -687,13 +775,13 @@ export default {
687
775
  if (hasMatrix && it.matrix.length >= 16) {
688
776
  const matrix = new this.THREE.Matrix4().fromArray(it.matrix);
689
777
  const worldMatrix = matrix.clone();
690
- if (bizToThreeMatrix) {
691
- worldMatrix.premultiply(bizToThreeMatrix);
778
+ if (this.bizToThreeMatrix) {
779
+ worldMatrix.premultiply(this.bizToThreeMatrix);
692
780
  }
693
781
  boxThree.applyMatrix4(worldMatrix);
694
- } else if (bizToThreeMatrix) {
782
+ } else if (this.bizToThreeMatrix) {
695
783
  // 注意:applyMatrix4 会重新计算 AABB
696
- boxThree.applyMatrix4(bizToThreeMatrix);
784
+ boxThree.applyMatrix4(this.bizToThreeMatrix);
697
785
  }
698
786
 
699
787
  const userData = {
@@ -760,7 +848,7 @@ export default {
760
848
 
761
849
  // 当前无模型了,重置相机中心状态
762
850
  if (this.noObserver.documentModelIds.size == 0) {
763
- hasExecutedCentering = false;
851
+ this.hasExecutedCentering = false;
764
852
  }
765
853
  }
766
854
  }
@@ -768,11 +856,16 @@ export default {
768
856
  console.log('time end', Date.now());
769
857
  },
770
858
  tryInitialCenterAfterBoundsReady() {
771
- if (hasExecutedCentering || userInteracting) return;
772
- if (!sceneBoundingBox || !sceneBoundingBox.isBox3 || sceneBoundingBox.isEmpty()) return;
859
+ if (this.hasExecutedCentering || this.userInteracting) return;
860
+ if (
861
+ !this.sceneBoundingBox ||
862
+ !this.sceneBoundingBox.isBox3 ||
863
+ this.sceneBoundingBox.isEmpty()
864
+ )
865
+ return;
773
866
  if (!this._boxIndex || this._boxIndex.size === 0) return;
774
867
 
775
- this.smartModelCenter(sceneBoundingBox, 0);
868
+ this.smartModelCenter(this.sceneBoundingBox, 0);
776
869
  },
777
870
  buildOctreeFromBoxIndex() {
778
871
  if (!this._boxIndex || this._boxIndex.size === 0) {
@@ -801,7 +894,7 @@ export default {
801
894
  new this.THREE.Vector3(minX, minY, minZ),
802
895
  new this.THREE.Vector3(maxX, maxY, maxZ)
803
896
  );
804
- sceneBoundingBox = rootBox.clone();
897
+ this.sceneBoundingBox = rootBox.clone();
805
898
  this._octreeMaxItems = 64;
806
899
  this._octreeMaxDepth = 12;
807
900
  this._octree = { box: rootBox, items: [], children: null, depth: 0 };
@@ -874,14 +967,14 @@ export default {
874
967
  },
875
968
  _getCurrentFrustum() {
876
969
  // 确保相机矩阵是最新的
877
- if (camera) {
878
- camera.updateMatrixWorld();
879
- camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
970
+ if (this.camera) {
971
+ this.camera.updateMatrixWorld();
972
+ this.camera.matrixWorldInverse.copy(this.camera.matrixWorld).invert();
880
973
  }
881
974
  const frustum = new this.THREE.Frustum();
882
975
  const vpMatrix = new this.THREE.Matrix4().multiplyMatrices(
883
- camera.projectionMatrix,
884
- camera.matrixWorldInverse
976
+ this.camera.projectionMatrix,
977
+ this.camera.matrixWorldInverse
885
978
  );
886
979
  frustum.setFromProjectionMatrix(vpMatrix);
887
980
  return frustum;
@@ -946,26 +1039,26 @@ export default {
946
1039
  return results;
947
1040
  },
948
1041
  setCameraFar() {
949
- const bbox = sceneBoundingBox;
1042
+ const bbox = this.sceneBoundingBox;
950
1043
  const center = bbox.getCenter(new this.THREE.Vector3());
951
1044
  const size = bbox.getSize(new this.THREE.Vector3());
952
1045
  const maxDist = size.length();
953
- // const distance = camera.position.distanceTo(center);
954
- camera.far = maxDist * 2;
955
- // camera.far = 100;
956
- camera.updateProjectionMatrix();
1046
+ // const distance = this.camera.position.distanceTo(center);
1047
+ this.camera.far = maxDist * 2;
1048
+ // this.camera.far = 100;
1049
+ this.camera.updateProjectionMatrix();
957
1050
  },
958
1051
  // 新增:当相机位于场景包围盒内时,动态调整相机远裁剪面
959
1052
  adjustCameraFarPlaneForSceneBox() {
960
1053
  try {
961
- if (!camera || !sceneBoundingBox || !sceneBoundingBox.isBox3) return;
1054
+ if (!this.camera || !this.sceneBoundingBox || !this.sceneBoundingBox.isBox3) return;
962
1055
 
963
1056
  // 获取最新相机位置
964
- camera.updateMatrixWorld(true);
965
- const camPos = new this.THREE.Vector3().setFromMatrixPosition(camera.matrixWorld);
1057
+ this.camera.updateMatrixWorld(true);
1058
+ const camPos = new this.THREE.Vector3().setFromMatrixPosition(this.camera.matrixWorld);
966
1059
 
967
- const min = sceneBoundingBox.min;
968
- const max = sceneBoundingBox.max;
1060
+ const min = this.sceneBoundingBox.min;
1061
+ const max = this.sceneBoundingBox.max;
969
1062
 
970
1063
  // 包围盒八个顶点
971
1064
  const vertices = [
@@ -987,13 +1080,13 @@ export default {
987
1080
  }
988
1081
 
989
1082
  // 确保 newFar > near,且更新投影矩阵
990
- const newFar = Math.max(maxDistance, camera.near + 0.1);
991
- if (Math.abs(camera.far - newFar) > 1e-6) {
992
- camera.far = newFar;
993
- camera.updateProjectionMatrix();
1083
+ const newFar = Math.max(maxDistance, this.camera.near + 0.1);
1084
+ if (Math.abs(this.camera.far - newFar) > 1e-6) {
1085
+ this.camera.far = newFar;
1086
+ this.camera.updateProjectionMatrix();
994
1087
  }
995
1088
 
996
- // console.log('camera.far', camera.far)
1089
+ // console.log('this.camera.far', this.camera.far)
997
1090
  } catch (e) {
998
1091
  // 防御式处理,避免交互中断
999
1092
  }
@@ -1002,7 +1095,7 @@ export default {
1002
1095
  * 计算当前相机视口在指定平面上的投影范围,并返回视椎体高度(min/max)。
1003
1096
  * frustumY.min 会被限制:不小于平面在视椎体 x/z 范围内的最低 y 值(如果可计算)。
1004
1097
  *
1005
- * @param {THREE.Camera} camera
1098
+ * @param {THREE.Camera} this.camera
1006
1099
  * @param {number} width
1007
1100
  * @param {number} height
1008
1101
  * @param {THREE.Plane} plane - three.js 的 Plane,方程为: normal.dot(p) + constant = 0
@@ -1013,8 +1106,8 @@ export default {
1013
1106
  getScreenPlaneBounds(camera, width, height, plane, opts = {}) {
1014
1107
  const { includeFrustumY = false } = opts;
1015
1108
 
1016
- camera.updateMatrixWorld();
1017
- if (camera.projectionMatrixNeedsUpdate) camera.updateProjectionMatrix();
1109
+ this.camera.updateMatrixWorld();
1110
+ if (this.camera.projectionMatrixNeedsUpdate) this.camera.updateProjectionMatrix();
1018
1111
 
1019
1112
  const screenCorners = [
1020
1113
  [0, 0], // 左下
@@ -1034,11 +1127,11 @@ export default {
1034
1127
 
1035
1128
  const ndcNear = ndcBase.clone();
1036
1129
  ndcNear.z = -1;
1037
- frustumPoints.push(ndcNear.clone().unproject(camera));
1130
+ frustumPoints.push(ndcNear.clone().unproject(this.camera));
1038
1131
 
1039
1132
  const ndcFar = ndcBase.clone();
1040
1133
  ndcFar.z = 1;
1041
- frustumPoints.push(ndcFar.clone().unproject(camera));
1134
+ frustumPoints.push(ndcFar.clone().unproject(this.camera));
1042
1135
  }
1043
1136
 
1044
1137
  const frustumBox = new this.THREE.Box3().setFromPoints(frustumPoints);
@@ -1085,14 +1178,14 @@ export default {
1085
1178
  // 基于远/近裁剪面角点计算在 Y 轴的投影范围(min/max)
1086
1179
  const nearFarY = computePlaneMinYOverBox(plane, frustumBox);
1087
1180
 
1088
- const target = this.getCameraTargetOnPlane(camera, plane);
1181
+ const target = this.getCameraTargetOnPlane(this.camera, plane);
1089
1182
 
1090
- // 将最终范围限制在 sceneBoundingBox 内(若已存在)
1183
+ // 将最终范围限制在 this.sceneBoundingBox 内(若已存在)
1091
1184
  let finalMinY = nearFarY.min;
1092
1185
  let finalMaxY = nearFarY.max;
1093
- if (sceneBoundingBox && sceneBoundingBox.isBox3) {
1094
- finalMinY = Math.max(finalMinY, sceneBoundingBox.min.y);
1095
- finalMaxY = Math.min(finalMaxY, sceneBoundingBox.max.y);
1186
+ if (this.sceneBoundingBox && this.sceneBoundingBox.isBox3) {
1187
+ finalMinY = Math.max(finalMinY, this.sceneBoundingBox.min.y);
1188
+ finalMaxY = Math.min(finalMaxY, this.sceneBoundingBox.max.y);
1096
1189
  }
1097
1190
 
1098
1191
  return {
@@ -1104,46 +1197,48 @@ export default {
1104
1197
  },
1105
1198
 
1106
1199
  /**
1107
- * 计算相机射线与场景包围盒(sceneBoundingBox)的交点,以及该点到视椎体近裁剪面。
1108
- * 优先使用相机的目标点(cameraControls._target)确定射线方向;若不可用则回退为相机世界前向。
1200
+ * 计算相机射线与场景包围盒(this.sceneBoundingBox)的交点,以及该点到视椎体近裁剪面。
1201
+ * 优先使用相机的目标点(this.cameraControls._target)确定射线方向;若不可用则回退为相机世界前向。
1109
1202
  * 若包围盒为空,回退为根据当前模型组/场景计算一次包围盒。
1110
1203
  * 若与包围盒无交点且提供了平面参数,则回退为与该平面的交点;仍无交点则将相机位置投影到该平面。
1111
1204
  *
1112
- * @param {THREE.Camera} camera - 相机对象
1205
+ * @param {THREE.Camera} this.camera - 相机对象
1113
1206
  * @param {THREE.Plane} [plane] - 可选,用于无包围盒交点时的回退平面
1114
1207
  * @returns {Object} { targetPoint: THREE.Vector3, distanceToNearPlane: number, cameraPosition: THREE.Vector3, nearPlanePoint: THREE.Vector3 }
1115
1208
  */
1116
1209
  getCameraTargetOnPlane(camera, plane) {
1117
- camera.updateMatrixWorld();
1118
- if (camera.projectionMatrixNeedsUpdate) camera.updateProjectionMatrix();
1210
+ this.camera.updateMatrixWorld();
1211
+ if (this.camera.projectionMatrixNeedsUpdate) this.camera.updateProjectionMatrix();
1119
1212
 
1120
1213
  // 相机世界位置
1121
- const cameraPosition = new this.THREE.Vector3().setFromMatrixPosition(camera.matrixWorld);
1214
+ const cameraPosition = new this.THREE.Vector3().setFromMatrixPosition(
1215
+ this.camera.matrixWorld
1216
+ );
1122
1217
 
1123
- // 从相机指向目标点的射线方向(优先使用 cameraControls._target)
1218
+ // 从相机指向目标点的射线方向(优先使用 this.cameraControls._target)
1124
1219
  const rayDirection = new this.THREE.Vector3();
1125
1220
  if (
1126
- typeof cameraControls !== 'undefined' &&
1127
- cameraControls &&
1128
- cameraControls.enabled &&
1129
- cameraControls._target
1221
+ typeof this.cameraControls !== 'undefined' &&
1222
+ this.cameraControls &&
1223
+ this.cameraControls.enabled &&
1224
+ this.cameraControls._target
1130
1225
  ) {
1131
- rayDirection.copy(cameraControls._target).sub(cameraPosition).normalize();
1226
+ rayDirection.copy(this.cameraControls._target).sub(cameraPosition).normalize();
1132
1227
  } else {
1133
- camera.getWorldDirection(rayDirection); // -Z 方向(世界坐标)
1228
+ this.camera.getWorldDirection(rayDirection); // -Z 方向(世界坐标)
1134
1229
  }
1135
1230
 
1136
1231
  const ray = new this.THREE.Ray(cameraPosition.clone(), rayDirection.clone());
1137
1232
 
1138
1233
  // 准备/计算场景包围盒
1139
- // if (!sceneBoundingBox) {
1234
+ // if (!this.sceneBoundingBox) {
1140
1235
  // const box3 = new this.THREE.Box3();
1141
- // if (typeof modelGroup !== 'undefined' && modelGroup) {
1142
- // box3.expandByObject(modelGroup);
1143
- // } else if (scene) {
1144
- // box3.expandByObject(scene);
1236
+ // if (typeof this.modelGroup !== 'undefined' && this.modelGroup) {
1237
+ // box3.expandByObject(this.modelGroup);
1238
+ // } else if (this.scene) {
1239
+ // box3.expandByObject(this.scene);
1145
1240
  // }
1146
- // sceneBoundingBox = box3;
1241
+ // this.sceneBoundingBox = box3;
1147
1242
  // }
1148
1243
 
1149
1244
  // 与场景包围盒的交点(若无交点则进行回退)
@@ -1151,22 +1246,24 @@ export default {
1151
1246
  let boxHitPoint = null;
1152
1247
  // 若包围盒不存在或为空,兜底从模型组/场景计算一次
1153
1248
  if (
1154
- !sceneBoundingBox ||
1155
- (sceneBoundingBox.isBox3 && sceneBoundingBox.isEmpty && sceneBoundingBox.isEmpty())
1249
+ !this.sceneBoundingBox ||
1250
+ (this.sceneBoundingBox.isBox3 &&
1251
+ this.sceneBoundingBox.isEmpty &&
1252
+ this.sceneBoundingBox.isEmpty())
1156
1253
  ) {
1157
1254
  const obj =
1158
- typeof modelGroup !== 'undefined' &&
1159
- modelGroup &&
1160
- modelGroup.children &&
1161
- modelGroup.children.length
1162
- ? modelGroup
1163
- : scene;
1255
+ typeof this.modelGroup !== 'undefined' &&
1256
+ this.modelGroup &&
1257
+ this.modelGroup.children &&
1258
+ this.modelGroup.children.length
1259
+ ? this.modelGroup
1260
+ : this.scene;
1164
1261
  if (obj) {
1165
- sceneBoundingBox = new this.THREE.Box3().setFromObject(obj);
1262
+ this.sceneBoundingBox = new this.THREE.Box3().setFromObject(obj);
1166
1263
  }
1167
1264
  }
1168
- if (sceneBoundingBox && sceneBoundingBox.isBox3) {
1169
- boxHitPoint = ray.intersectBox(sceneBoundingBox, new this.THREE.Vector3());
1265
+ if (this.sceneBoundingBox && this.sceneBoundingBox.isBox3) {
1266
+ boxHitPoint = ray.intersectBox(this.sceneBoundingBox, new this.THREE.Vector3());
1170
1267
  }
1171
1268
 
1172
1269
  // targetPoint 保留原平面回退逻辑(用于其他需要点位的场景)
@@ -1206,14 +1303,14 @@ export default {
1206
1303
  * @returns {boolean} 是否在视椎体内
1207
1304
  */
1208
1305
  isModelInFrustum(model, instanceId = null, frustum = null) {
1209
- if (!model || !camera) return true;
1306
+ if (!model || !this.camera) return true;
1210
1307
 
1211
1308
  // 优先使用传入的 frustum,避免重复创建
1212
1309
  if (!frustum) {
1213
1310
  frustum = new this.THREE.Frustum();
1214
1311
  const matrix = new this.THREE.Matrix4().multiplyMatrices(
1215
- camera.projectionMatrix,
1216
- camera.matrixWorldInverse
1312
+ this.camera.projectionMatrix,
1313
+ this.camera.matrixWorldInverse
1217
1314
  );
1218
1315
  frustum.setFromProjectionMatrix(matrix);
1219
1316
  }
@@ -1247,7 +1344,7 @@ export default {
1247
1344
  * @returns {number} SSE值
1248
1345
  */
1249
1346
  calculateSSE(model) {
1250
- if (!model || !camera || !renderer) return 0;
1347
+ if (!model || !this.camera || !this.renderer) return 0;
1251
1348
 
1252
1349
  // 获取模型的包围盒
1253
1350
  const box = new this.THREE.Box3().setFromObject(model);
@@ -1261,7 +1358,7 @@ export default {
1261
1358
 
1262
1359
  // 获取相机世界位置
1263
1360
  const cameraPosition = new this.THREE.Vector3();
1264
- camera.getWorldPosition(cameraPosition);
1361
+ this.camera.getWorldPosition(cameraPosition);
1265
1362
 
1266
1363
  // 计算相机到物体包围盒中心的距离
1267
1364
  const distance = cameraPosition.distanceTo(center);
@@ -1270,10 +1367,10 @@ export default {
1270
1367
  if (distance === 0) return Infinity;
1271
1368
 
1272
1369
  // 获取视口高度
1273
- const viewportHeight = renderer.domElement.clientHeight;
1370
+ const viewportHeight = this.renderer.domElement.clientHeight;
1274
1371
 
1275
1372
  // 计算tan(fov/2),fov是以度为单位
1276
- const halfFovRad = this.THREE.MathUtils.degToRad(camera.fov / 2);
1373
+ const halfFovRad = this.THREE.MathUtils.degToRad(this.camera.fov / 2);
1277
1374
  const tanHalfFov = Math.tan(halfFovRad);
1278
1375
 
1279
1376
  // 计算SSE
@@ -1283,14 +1380,14 @@ export default {
1283
1380
  },
1284
1381
  // 针对已卸载实例的信息进行视锥体检测
1285
1382
  isInstanceInfoInFrustum(instanceInfo) {
1286
- if (!instanceInfo || !camera) return true;
1383
+ if (!instanceInfo || !this.camera) return true;
1287
1384
  const { geometry, originalMatrix, parent } = instanceInfo;
1288
1385
  if (!geometry || !originalMatrix) return false;
1289
1386
 
1290
1387
  const frustum = new this.THREE.Frustum();
1291
1388
  const vpMatrix = new this.THREE.Matrix4().multiplyMatrices(
1292
- camera.projectionMatrix,
1293
- camera.matrixWorldInverse
1389
+ this.camera.projectionMatrix,
1390
+ this.camera.matrixWorldInverse
1294
1391
  );
1295
1392
  frustum.setFromProjectionMatrix(vpMatrix);
1296
1393
 
@@ -1316,7 +1413,7 @@ export default {
1316
1413
  const modelState = this.noObserver
1317
1414
  ? this.noObserver.modelStateManager
1318
1415
  : this.modelStateManager;
1319
- if (!this.modelStateManager.frustumCheckEnabled || !scene) return;
1416
+ if (!this.modelStateManager.frustumCheckEnabled || !this.scene) return;
1320
1417
  const now = Date.now();
1321
1418
  if (now - modelState.lastCullingTime < 100) {
1322
1419
  return;
@@ -1329,8 +1426,8 @@ export default {
1329
1426
 
1330
1427
  const globalFrustum = this._frustum;
1331
1428
  const globalVpMatrix = this._vpMatrix.multiplyMatrices(
1332
- camera.projectionMatrix,
1333
- camera.matrixWorldInverse
1429
+ this.camera.projectionMatrix,
1430
+ this.camera.matrixWorldInverse
1334
1431
  );
1335
1432
  globalFrustum.setFromProjectionMatrix(globalVpMatrix);
1336
1433
 
@@ -1368,12 +1465,12 @@ export default {
1368
1465
  if (occlusionEnabled) {
1369
1466
  const state = occlusionState;
1370
1467
  const w =
1371
- renderer && renderer.domElement
1372
- ? renderer.domElement.clientWidth || renderer.domElement.width || 0
1468
+ this.renderer && this.renderer.domElement
1469
+ ? this.renderer.domElement.clientWidth || this.renderer.domElement.width || 0
1373
1470
  : 0;
1374
1471
  const h =
1375
- renderer && renderer.domElement
1376
- ? renderer.domElement.clientHeight || renderer.domElement.height || 0
1472
+ this.renderer && this.renderer.domElement
1473
+ ? this.renderer.domElement.clientHeight || this.renderer.domElement.height || 0
1377
1474
  : 0;
1378
1475
  // bufferWidth/Height 仍从响应式对象读取以支持动态修改
1379
1476
  // 性能优化:使用降采样缓冲区进行遮挡剔除,大幅减少 readPixels 耗时 (从全屏降至约 256px 宽)
@@ -1409,15 +1506,15 @@ export default {
1409
1506
  if (!state._occScene) state._occScene = new this.THREE.Scene();
1410
1507
  // if (!state._occPerfStats) state._occPerfStats = Object.create(null);
1411
1508
  // const _occRecordPerf = (name, ms, meta) => {
1412
- // const stats = state._occPerfStats;
1413
- // const prev = stats[name];
1509
+ // const this.stats = state._occPerfStats;
1510
+ // const prev = this.stats[name];
1414
1511
  // const next = prev || { count: 0, total: 0, max: 0, last: 0, meta: null };
1415
1512
  // next.count++;
1416
1513
  // next.total += ms;
1417
1514
  // next.last = ms;
1418
1515
  // if (ms > next.max) next.max = ms;
1419
1516
  // if (meta) next.meta = meta;
1420
- // stats[name] = next;
1517
+ // this.stats[name] = next;
1421
1518
  // };
1422
1519
 
1423
1520
  // 过滤掉透明物体
@@ -1543,8 +1640,8 @@ export default {
1543
1640
  _tempScale.copy(halfSize).multiplyScalar(2);
1544
1641
  _tempMatrix.copy(matrix);
1545
1642
  _tempMatrix.scale(_tempScale);
1546
- if (typeof bizToThreeMatrix !== 'undefined' && bizToThreeMatrix) {
1547
- _tempMatrix.premultiply(bizToThreeMatrix);
1643
+ if (typeof this.bizToThreeMatrix !== 'undefined' && this.bizToThreeMatrix) {
1644
+ _tempMatrix.premultiply(this.bizToThreeMatrix);
1548
1645
  }
1549
1646
 
1550
1647
  boxes.mesh.setMatrixAt(i, _tempMatrix);
@@ -1648,7 +1745,7 @@ export default {
1648
1745
  const vertexCount = (obbData.length / 3) | 0;
1649
1746
  const needTransform =
1650
1747
  (matrixArr && matrixArr.length >= 16) ||
1651
- (typeof bizToThreeMatrix !== 'undefined' && bizToThreeMatrix);
1748
+ (typeof this.bizToThreeMatrix !== 'undefined' && this.bizToThreeMatrix);
1652
1749
 
1653
1750
  if (needTransform) {
1654
1751
  if (matrixArr && matrixArr.length >= 16) {
@@ -1656,8 +1753,8 @@ export default {
1656
1753
  } else {
1657
1754
  _tempMatA.identity();
1658
1755
  }
1659
- if (typeof bizToThreeMatrix !== 'undefined' && bizToThreeMatrix) {
1660
- _tempMatA.premultiply(bizToThreeMatrix);
1756
+ if (typeof this.bizToThreeMatrix !== 'undefined' && this.bizToThreeMatrix) {
1757
+ _tempMatA.premultiply(this.bizToThreeMatrix);
1661
1758
  }
1662
1759
  }
1663
1760
 
@@ -1814,8 +1911,8 @@ export default {
1814
1911
  tempMatrix.copy(matrix);
1815
1912
  tempScale.copy(halfSize).multiplyScalar(2);
1816
1913
  tempMatrix.scale(tempScale);
1817
- if (typeof bizToThreeMatrix !== 'undefined' && bizToThreeMatrix) {
1818
- tempMatrix.premultiply(bizToThreeMatrix);
1914
+ if (typeof this.bizToThreeMatrix !== 'undefined' && this.bizToThreeMatrix) {
1915
+ tempMatrix.premultiply(this.bizToThreeMatrix);
1819
1916
  }
1820
1917
  } else {
1821
1918
  c.box.getSize(tempScale);
@@ -1842,23 +1939,23 @@ export default {
1842
1939
  state._occTransparentMaxIdx = 0;
1843
1940
  }
1844
1941
 
1845
- // console.log('renderer', renderer)
1846
- const prevToneMapping = renderer.toneMapping;
1847
- const prevTarget = renderer.getRenderTarget ? renderer.getRenderTarget() : null;
1942
+ // console.log('this.renderer', this.renderer)
1943
+ const prevToneMapping = this.renderer.toneMapping;
1944
+ const prevTarget = this.renderer.getRenderTarget ? this.renderer.getRenderTarget() : null;
1848
1945
  let prevClearColorHex = 0x000000;
1849
1946
  let prevClearAlpha = 0;
1850
1947
  try {
1851
- const c = renderer.getClearColor ? renderer.getClearColor() : null;
1948
+ const c = this.renderer.getClearColor ? this.renderer.getClearColor() : null;
1852
1949
  if (c && c.isColor) prevClearColorHex = c.getHex();
1853
1950
  prevClearAlpha =
1854
- typeof renderer.getClearAlpha === 'function' ? renderer.getClearAlpha() : 0;
1951
+ typeof this.renderer.getClearAlpha === 'function' ? this.renderer.getClearAlpha() : 0;
1855
1952
  } catch (_) {}
1856
- renderer.toneMapping = this.THREE.NoToneMapping;
1857
- renderer.setRenderTarget(rt);
1858
- renderer.setClearColor(new this.THREE.Color(0, 0, 0), 0);
1859
- renderer.clear(true, true, false);
1860
- camera.updateMatrixWorld(true);
1861
- renderer.render(state._occScene, camera);
1953
+ this.renderer.toneMapping = this.THREE.NoToneMapping;
1954
+ this.renderer.setRenderTarget(rt);
1955
+ this.renderer.setClearColor(new this.THREE.Color(0, 0, 0), 0);
1956
+ this.renderer.clear(true, true, false);
1957
+ this.camera.updateMatrixWorld(true);
1958
+ this.renderer.render(state._occScene, this.camera);
1862
1959
  this.updateGlobalSceneBoundingBox();
1863
1960
  // const t1 = performance.now();
1864
1961
  // 从响应式对象读取 previewEnabled
@@ -1883,11 +1980,11 @@ export default {
1883
1980
  }
1884
1981
  state._previewCanvas.width = sw;
1885
1982
  state._previewCanvas.height = sh;
1886
- const cssW = Math.min(renderer.domElement.width / 5);
1887
- const cssH = Math.min(renderer.domElement.height / 5);
1983
+ const cssW = Math.min(this.renderer.domElement.width / 5);
1984
+ const cssH = Math.min(this.renderer.domElement.height / 5);
1888
1985
  state._previewCanvas.style.width = cssW + 'px';
1889
1986
  state._previewCanvas.style.height = cssH + 'px';
1890
- renderer.readRenderTargetPixels(rt, 0, 0, sw, sh, state._previewBuffer);
1987
+ this.renderer.readRenderTargetPixels(rt, 0, 0, sw, sh, state._previewBuffer);
1891
1988
  if (
1892
1989
  !state._previewImageData ||
1893
1990
  state._previewImageData.width !== sw ||
@@ -1915,7 +2012,7 @@ export default {
1915
2012
  }
1916
2013
  // const tPreview1 = performance.now();
1917
2014
  // _occRecordPerf('occ_preview_ms', tPreview1 - tPreview0, { sw, sh });
1918
- renderer.readRenderTargetPixels(rt, 0, 0, sw, sh, state._colorBuffer);
2015
+ this.renderer.readRenderTargetPixels(rt, 0, 0, sw, sh, state._colorBuffer);
1919
2016
 
1920
2017
  const idIndexArr = activeIdIndexArr;
1921
2018
  const maxIdx = typeof state._occMaxIdx === 'number' ? state._occMaxIdx : 0;
@@ -1956,10 +2053,10 @@ export default {
1956
2053
  const transparentMaxIdx =
1957
2054
  typeof state._occTransparentMaxIdx === 'number' ? state._occTransparentMaxIdx : 0;
1958
2055
  if (transparentMaxIdx > 0 && transparentMesh && transparentMesh.visible) {
1959
- renderer.setClearColor(new this.THREE.Color(0, 0, 0), 0);
1960
- renderer.clear(true, false, false);
1961
- renderer.render(state._occTransparentScene, camera);
1962
- renderer.readRenderTargetPixels(rt, 0, 0, sw, sh, state._colorBuffer);
2056
+ this.renderer.setClearColor(new this.THREE.Color(0, 0, 0), 0);
2057
+ this.renderer.clear(true, false, false);
2058
+ this.renderer.render(state._occTransparentScene, this.camera);
2059
+ this.renderer.readRenderTargetPixels(rt, 0, 0, sw, sh, state._colorBuffer);
1963
2060
 
1964
2061
  const tIdIndexArr = transparentIdIndexArr;
1965
2062
  const tScanResult = await this.scanOcclusionIndices(
@@ -1982,9 +2079,9 @@ export default {
1982
2079
  }
1983
2080
  }
1984
2081
 
1985
- renderer.setRenderTarget(prevTarget);
1986
- renderer.setClearColor(prevClearColorHex, prevClearAlpha);
1987
- renderer.toneMapping = prevToneMapping;
2082
+ this.renderer.setRenderTarget(prevTarget);
2083
+ this.renderer.setClearColor(prevClearColorHex, prevClearAlpha);
2084
+ this.renderer.toneMapping = prevToneMapping;
1988
2085
  } catch (e) {
1989
2086
  for (let i = 0; i < candidates.length; i++) {
1990
2087
  visibleIdSet.add(candidates[i].modelId);
@@ -1999,7 +2096,7 @@ export default {
1999
2096
  visibleIdSet.forEach(id => visibleIds.push(id));
2000
2097
  const toLoadSet = new Set(visibleIdSet);
2001
2098
  const parentsToCheck = new Set();
2002
- scene.traverse(child => {
2099
+ this.scene.traverse(child => {
2003
2100
  if (!child.isInstancedMesh) return;
2004
2101
  // 如果复用数量等于大于2个,则跳过裁剪和剔除处理(始终保留)
2005
2102
  if (child.count >= 2) {
@@ -2051,7 +2148,7 @@ export default {
2051
2148
  }
2052
2149
 
2053
2150
  // 仅当所有实例都不活跃时,才执行卸载
2054
- if (!firstPerSign && !roaming && allInactive) {
2151
+ if (!this.firstPerSign && !this.roaming && allInactive) {
2055
2152
  toUnload.push({ modelId, child });
2056
2153
  }
2057
2154
  }
@@ -2072,7 +2169,7 @@ export default {
2072
2169
  if (modelInVisible) {
2073
2170
  toLoadSet.delete(modelId);
2074
2171
  }
2075
- if (!firstPerSign && !roaming && (!inFrustum || !modelInVisible)) {
2172
+ if (!this.firstPerSign && !this.roaming && (!inFrustum || !modelInVisible)) {
2076
2173
  toUnload.push({ modelId, child });
2077
2174
  }
2078
2175
  });
@@ -2080,7 +2177,7 @@ export default {
2080
2177
  if (child && child.parent) parentsToCheck.add(child.parent);
2081
2178
  this.unloadInstancedModel(modelId, child);
2082
2179
  }
2083
- if (scene) {
2180
+ if (this.scene) {
2084
2181
  parentsToCheck.forEach(group => {
2085
2182
  if (group && group.children && group.children.length === 0) {
2086
2183
  group.removeFromParent();
@@ -2090,11 +2187,11 @@ export default {
2090
2187
  this.modelStateManager.isloadedModelsIds = Object.freeze(Array.from(toLoadSet));
2091
2188
  },
2092
2189
  isBoxInFrustum(box) {
2093
- if (!camera) return true;
2190
+ if (!this.camera) return true;
2094
2191
  const frustum = new this.THREE.Frustum();
2095
2192
  const vpMatrix = new this.THREE.Matrix4().multiplyMatrices(
2096
- camera.projectionMatrix,
2097
- camera.matrixWorldInverse
2193
+ this.camera.projectionMatrix,
2194
+ this.camera.matrixWorldInverse
2098
2195
  );
2099
2196
  frustum.setFromProjectionMatrix(vpMatrix);
2100
2197
  return frustum.intersectsBox(box);
@@ -2192,6 +2289,7 @@ export default {
2192
2289
  * @param {string} [config.projectId] - 项目ID
2193
2290
  */
2194
2291
  init(config = {}) {
2292
+ console.log('init');
2195
2293
  this.initStreamLoader(config.modelApi, config);
2196
2294
  },
2197
2295
 
@@ -2209,7 +2307,10 @@ export default {
2209
2307
  debug: isDebug,
2210
2308
  renderModelData: this.renderModelData.bind(this),
2211
2309
  ensureNotInteracting: async abortSignal => {
2212
- if (userInteracting || this.noObserver.batchLoadingState.interactionState.isInteracting) {
2310
+ if (
2311
+ this.userInteracting ||
2312
+ this.noObserver.batchLoadingState.interactionState.isInteracting
2313
+ ) {
2213
2314
  await new Promise(resolve => setTimeout(resolve, 0));
2214
2315
  }
2215
2316
  },
@@ -2318,17 +2419,17 @@ export default {
2318
2419
  * 同步获取当前WebGL画面的截图数据,解决preserveDrawingBuffer为false时html2canvas截黑屏的问题
2319
2420
  */
2320
2421
  getScreenshotDataURL() {
2321
- if (!renderer || !scene || !camera) return null;
2422
+ if (!this.renderer || !this.scene || !this.camera) return null;
2322
2423
 
2323
2424
  // 强制渲染一帧
2324
- if (outlineComposer) {
2325
- outlineComposer.render();
2425
+ if (this.outlineComposer) {
2426
+ this.outlineComposer.render();
2326
2427
  } else {
2327
- renderer.render(scene, camera);
2428
+ this.renderer.render(this.scene, this.camera);
2328
2429
  }
2329
2430
 
2330
2431
  // 立即同步提取像素数据
2331
- return renderer.domElement.toDataURL('image/png', 1.0);
2432
+ return this.renderer.domElement.toDataURL('image/png', 1.0);
2332
2433
  },
2333
2434
  /**
2334
2435
  * 内部渲染流式数据方法
@@ -2406,7 +2507,7 @@ export default {
2406
2507
  },
2407
2508
  computeFrustumAABB() {
2408
2509
  // 确保相机矩阵最新
2409
- camera.updateMatrixWorld(true);
2510
+ this.camera.updateMatrixWorld(true);
2410
2511
 
2411
2512
  // NDC 八个角
2412
2513
  const ndcCorners = {
@@ -2422,8 +2523,8 @@ export default {
2422
2523
 
2423
2524
  for (let key in ndcCorners) {
2424
2525
  const v = new this.THREE.Vector3(...ndcCorners[key]);
2425
- v.unproject(camera); // NDC → world
2426
- v.applyMatrix4(threeToBizMatrix); // world → biz
2526
+ v.unproject(this.camera); // NDC → world
2527
+ v.applyMatrix4(this.threeToBizMatrix); // world → biz
2427
2528
  ndcCorners[key] = v;
2428
2529
  }
2429
2530
 
@@ -2459,79 +2560,85 @@ export default {
2459
2560
  };
2460
2561
  },
2461
2562
  initRender() {
2462
- renderer = new this.THREE.WebGLRenderer({
2563
+ this.renderer = new this.THREE.WebGLRenderer({
2463
2564
  antialias: true,
2464
2565
  alpha: true,
2465
2566
  logarithmicDepthBuffer: true,
2466
2567
  powerPreference: 'high-performance',
2467
2568
  preserveDrawingBuffer: false, //保留图形缓冲区 TODO 临时截图使用
2468
2569
  });
2469
- renderer.debug.checkShaderErrors = false;
2470
- renderer.info.autoReset = false;
2471
- renderer.setPixelRatio(window.devicePixelRatio);
2472
- const rect = instructions.getBoundingClientRect();
2473
- renderer.setSize(rect.width, rect.height);
2474
- renderer.domElement.id = 'three-model';
2475
- renderer.shadowMap.enabled = true;
2476
- renderer.outputEncoding = this.THREE.sRGBEncoding;
2477
- instructions.appendChild(renderer.domElement);
2478
- renderer.setClearAlpha(0);
2570
+ this.renderer.debug.checkShaderErrors = false;
2571
+ this.renderer.info.autoReset = false;
2572
+ this.renderer.setPixelRatio(window.devicePixelRatio);
2573
+ const rect = this.instructions.getBoundingClientRect();
2574
+ this.renderer.setSize(rect.width, rect.height);
2575
+ this.renderer.domElement.id = 'three-model-' + this.containId;
2576
+ this.renderer.shadowMap.enabled = true;
2577
+ this.renderer.outputEncoding = this.THREE.sRGBEncoding;
2578
+ this.instructions.appendChild(this.renderer.domElement);
2579
+ this.renderer.setClearAlpha(0);
2479
2580
 
2480
2581
  // 监听体系已重构至 initCameraChangeObserver,此处仅保留必要的初始化
2481
2582
  // 原始的 wheel 监听逻辑已迁移
2482
- // renderer.domElement.addEventListener('wheel', this._wheelHandler);
2583
+ // this.renderer.domElement.addEventListener('wheel', this._wheelHandler);
2483
2584
 
2484
2585
  // 与校审截图功能冲突,暂时先注释掉
2485
2586
  // -----------
2486
- // renderer.autoClear = false;
2487
- // renderer.autoClearColor = false;
2488
- // renderer.autoClearDepth = false;
2489
- // renderer.autoClearStencil = false;
2587
+ // this.renderer.autoClear = false;
2588
+ // this.renderer.autoClearColor = false;
2589
+ // this.renderer.autoClearDepth = false;
2590
+ // this.renderer.autoClearStencil = false;
2490
2591
  // -----------
2491
2592
  },
2492
2593
  initPostProcessing() {
2493
- outlineComposer = new EffectComposer(renderer, renderTarget);
2594
+ this.outlineComposer = new EffectComposer(this.renderer, this.renderTarget);
2595
+
2596
+ const renderPass = new RenderPass(this.scene, this.camera);
2597
+ this.outlineComposer.addPass(renderPass);
2494
2598
 
2495
- const renderPass = new RenderPass(scene, camera);
2496
- outlineComposer.addPass(renderPass);
2599
+ const rect = this.instructions.getBoundingClientRect();
2600
+ this.outlinePass = new OutlinePass(
2601
+ new this.THREE.Vector2(rect.width, rect.height),
2602
+ this.scene,
2603
+ this.camera
2604
+ );
2497
2605
 
2498
- const rect = instructions.getBoundingClientRect();
2499
- outlinePass = new OutlinePass(new this.THREE.Vector2(rect.width, rect.height), scene, camera);
2500
- outlinePass.edgeStrength = 3;
2501
- outlinePass.edgeGlow = 0.5; // 边缘模糊度
2502
- outlinePass.edgeThickness = 2; // 轮廓线宽度
2503
- outlinePass.visibleEdgeColor.set('#ffffff'); // 默认白色,后续可动态调整
2504
- outlinePass.hiddenEdgeColor.set('#ffffff');
2505
- outlineComposer.addPass(outlinePass);
2606
+ this.outlinePass.edgeStrength = 3;
2607
+ this.outlinePass.edgeGlow = 0.5; // 边缘模糊度
2608
+ this.outlinePass.edgeThickness = 2; // 轮廓线宽度
2609
+ this.outlinePass.visibleEdgeColor.set('#ffffff'); // 默认白色,后续可动态调整
2610
+ this.outlinePass.hiddenEdgeColor.set('#ffffff');
2611
+ this.outlineComposer.addPass(this.outlinePass);
2506
2612
 
2507
2613
  const outputPass = new OutputPass();
2508
- outlineComposer.addPass(outputPass);
2614
+ this.outlineComposer.addPass(outputPass);
2509
2615
  },
2510
2616
  initScene() {
2511
- modelGroup = new this.THREE.Group();
2512
- scene = new this.THREE.Scene();
2617
+ this.modelGroup = new this.THREE.Group();
2618
+ this.scene = new this.THREE.Scene();
2513
2619
  if (isDebug) {
2514
- stats = new Stats();
2515
- document.body.appendChild(stats.dom);
2620
+ this.stats = new Stats();
2621
+ document.body.appendChild(this.stats.dom);
2516
2622
  }
2517
2623
  },
2518
2624
  initCamera() {
2519
- const rect = instructions.getBoundingClientRect();
2520
- camera = new this.THREE.PerspectiveCamera(45, rect.width / rect.height, 0.1, 1000000);
2625
+ const rect = this.instructions.getBoundingClientRect();
2626
+ this.camera = new this.THREE.PerspectiveCamera(45, rect.width / rect.height, 0.1, 1000000);
2521
2627
  // camera.position.set(0, 100, 150);
2628
+ // this.camera.position.set(0, 100, 150);
2522
2629
  },
2523
2630
  initControl() {
2524
2631
  // 初始化控件
2525
- cameraControls = new CameraControls(camera, renderer.domElement);
2526
- cameraControls.dollyToCursor = true;
2527
- cameraControls.smoothTime = 0.1;
2528
- cameraControls.draggingSmoothTime = 0.05;
2529
- cameraControls.truckSpeed = 2.0;
2530
- cameraControls.infinityDolly = true;
2531
- cameraControls.minDistance = 10;
2632
+ this.cameraControls = new CameraControls(this.camera, this.renderer.domElement);
2633
+ this.cameraControls.dollyToCursor = true;
2634
+ this.cameraControls.smoothTime = 0.1;
2635
+ this.cameraControls.draggingSmoothTime = 0.05;
2636
+ this.cameraControls.truckSpeed = 2.0;
2637
+ this.cameraControls.infinityDolly = true;
2638
+ this.cameraControls.minDistance = 10;
2532
2639
  // 若已存在场景包围盒,初始化时设置最大dolly距离为其对角线长度
2533
2640
 
2534
- cameraControls.dollySpeed = 0.15; // 鼠标滚轮每次移动速度倍率
2641
+ this.cameraControls.dollySpeed = 0.15; // 鼠标滚轮每次移动速度倍率
2535
2642
  },
2536
2643
 
2537
2644
  setCameraObserverEnabled(enabled) {
@@ -2545,7 +2652,7 @@ export default {
2545
2652
  initCameraChangeObserver() {
2546
2653
  // 初始化 control 状态标志
2547
2654
  this.noObserver.isControlActive = false;
2548
-
2655
+ let _this = this;
2549
2656
  // 1. 创建统一的响应触发器 (Debounced)
2550
2657
  this._cameraChangeObserver = this.debounce(
2551
2658
  async source => {
@@ -2615,17 +2722,17 @@ export default {
2615
2722
  const isZoomIn = deltaY < 0; // 靠近
2616
2723
 
2617
2724
  // if (isZoomOut) {
2618
- // cameraControls.infinityDolly = false; // 向外遵守 maxDistance
2725
+ // this.cameraControls.infinityDolly = false; // 向外遵守 maxDistance
2619
2726
  // } else if (isZoomIn) {
2620
- // cameraControls.infinityDolly = true; // 向前允许推进(超越 minDistance)
2727
+ // this.cameraControls.infinityDolly = true; // 向前允许推进(超越 minDistance)
2621
2728
  // }
2622
2729
 
2623
2730
  // dolly最大距离限制
2624
2731
  try {
2625
- if (sceneBoundingBox && isFinite(maxDollyDistance)) {
2732
+ if (this.sceneBoundingBox && isFinite(maxDollyDistance)) {
2626
2733
  if (isZoomOut) {
2627
- camera.updateMatrixWorld(true);
2628
- const currentDistance = camera.position.distanceTo(cameraControls._target);
2734
+ this.camera.updateMatrixWorld(true);
2735
+ const currentDistance = this.camera.position.distanceTo(this.cameraControls._target);
2629
2736
  if (currentDistance >= maxDollyDistance) {
2630
2737
  event.preventDefault && event.preventDefault();
2631
2738
  event.stopImmediatePropagation && event.stopImmediatePropagation();
@@ -2640,53 +2747,53 @@ export default {
2640
2747
  // 触发观察者
2641
2748
  this._cameraChangeObserver('wheel');
2642
2749
  };
2643
- renderer.domElement.addEventListener('wheel', this._wheelHandler);
2750
+ this.renderer.domElement.addEventListener('wheel', this._wheelHandler);
2644
2751
 
2645
2752
  // 4. 监听器 - Controls (用户拖拽/操作)
2646
2753
  this._onControlStart = res => {
2647
2754
  this.noObserver.isControlActive = true;
2648
2755
 
2649
2756
  // 统一交互开始处理(相机)
2650
- this.beginInteraction('camera');
2757
+ this.beginInteraction('this.camera');
2651
2758
  this.$emit('controlStart', res);
2652
2759
 
2653
2760
  // 记录鼠标按下时的位置
2654
2761
  const event = res.originalEvent || window.event;
2655
2762
  if (event) {
2656
- mouseDownPosition.x = event.clientX || 0;
2657
- mouseDownPosition.y = event.clientY || 0;
2658
- isDragging = false;
2763
+ _this.mouseDownPosition.x = event.clientX || 0;
2764
+ _this.mouseDownPosition.y = event.clientY || 0;
2765
+ _this.isDragging = false;
2659
2766
  }
2660
2767
  };
2661
2768
 
2662
2769
  this._onControlEnd = res => {
2663
- this.noObserver.isControlActive = false;
2770
+ _this.noObserver.isControlActive = false;
2664
2771
 
2665
2772
  this.$emit('controlEnd', res);
2666
2773
 
2667
2774
  // 统一交互结束处理(相机)
2668
- this.endInteraction('camera', res, { immediateResume: false });
2775
+ this.endInteraction('this.camera', res, { immediateResume: false });
2669
2776
 
2670
2777
  // 记录鼠标抬起时的位置并计算是否发生了拖拽
2671
2778
  const event = res.originalEvent || window.event;
2672
2779
  if (event) {
2673
- mouseUpPosition.x = event.clientX || 0;
2674
- mouseUpPosition.y = event.clientY || 0;
2675
- const deltaX = Math.abs(mouseUpPosition.x - mouseDownPosition.x);
2676
- const deltaY = Math.abs(mouseUpPosition.y - mouseDownPosition.y);
2780
+ _this.mouseUpPosition.x = event.clientX || 0;
2781
+ _this.mouseUpPosition.y = event.clientY || 0;
2782
+ const deltaX = Math.abs(_this.mouseUpPosition.x - _this.mouseDownPosition.x);
2783
+ const deltaY = Math.abs(_this.mouseUpPosition.y - _this.mouseDownPosition.y);
2677
2784
  const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
2678
- isDragging = distance > dragThreshold;
2785
+ _this.isDragging = distance > dragThreshold;
2679
2786
  }
2680
2787
 
2681
2788
  // 只有在实际发生拖拽时才执行相关操作
2682
- if (isDragging) {
2789
+ if (_this.isDragging) {
2683
2790
  // 居中逻辑
2684
- // if (needsCenteringAfterInteraction && !hasExecutedCentering) {
2791
+ // if (this.needsCenteringAfterInteraction && !this.hasExecutedCentering) {
2685
2792
  // setTimeout(() => {
2686
- // if (modelGroups.length > 0) {
2687
- // this.setModelCenter(modelGroups[modelGroups.length - 1]);
2688
- // hasExecutedCentering = true;
2689
- // needsCenteringAfterInteraction = false;
2793
+ // if (this.modelGroups.length > 0) {
2794
+ // this.setModelCenter(this.modelGroups[this.modelGroups.length - 1]);
2795
+ // this.hasExecutedCentering = true;
2796
+ // this.needsCenteringAfterInteraction = false;
2690
2797
  // }
2691
2798
  // }, 300);
2692
2799
  // }
@@ -2696,32 +2803,32 @@ export default {
2696
2803
  }
2697
2804
  };
2698
2805
 
2699
- cameraControls.addEventListener('controlstart', this._onControlStart);
2700
- cameraControls.addEventListener('controlend', this._onControlEnd);
2806
+ this.cameraControls.addEventListener('controlstart', this._onControlStart);
2807
+ this.cameraControls.addEventListener('controlend', this._onControlEnd);
2701
2808
 
2702
2809
  // 5. 监听器 - Programmatic (程序化动画/过渡结束)
2703
- // 监听 rest 事件,捕获 cameraControls.setLookAt(..., true) 等动画结束(以及阻尼停止)
2810
+ // 监听 rest 事件,捕获 this.cameraControls.setLookAt(..., true) 等动画结束(以及阻尼停止)
2704
2811
  // 注意:rest 事件在用户交互完全静止时也会触发,与 controlEnd 有部分重叠,但 Observer 有防抖,影响不大
2705
2812
  this._onRest = () => {
2706
2813
  this._cameraChangeObserver('rest');
2707
2814
  };
2708
- cameraControls.addEventListener('rest', this._onRest);
2815
+ this.cameraControls.addEventListener('rest', this._onRest);
2709
2816
  },
2710
2817
  // 初始化光源
2711
2818
  initLight() {
2712
- const pmremGenerator = new this.THREE.PMREMGenerator(renderer);
2713
- scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
2819
+ const pmremGenerator = new this.THREE.PMREMGenerator(this.renderer);
2820
+ this.scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
2714
2821
  },
2715
2822
  // 初始化文字画布
2716
2823
  initLabelRender() {
2717
- labelRenderer = new CSS2DRenderer();
2718
- const rect = instructions.getBoundingClientRect();
2719
- labelRenderer.setSize(rect.width, rect.height);
2720
- labelRenderer.domElement.style.position = 'absolute';
2824
+ this.labelRenderer = new CSS2DRenderer();
2825
+ const rect = this.instructions.getBoundingClientRect();
2826
+ this.labelRenderer.setSize(rect.width, rect.height);
2827
+ this.labelRenderer.domElement.style.position = 'absolute';
2721
2828
 
2722
- labelRenderer.domElement.style.top = '0';
2723
- labelRenderer.domElement.style.pointerEvents = 'none';
2724
- instructions.appendChild(labelRenderer.domElement);
2829
+ this.labelRenderer.domElement.style.top = '0';
2830
+ this.labelRenderer.domElement.style.pointerEvents = 'none';
2831
+ this.instructions.appendChild(this.labelRenderer.domElement);
2725
2832
  },
2726
2833
  // 根据模型数据绘制模型实体 业务平台可调用此方法加载模型
2727
2834
  /*
@@ -2730,6 +2837,7 @@ export default {
2730
2837
  meshNameConfig: {}
2731
2838
  */
2732
2839
  drawModel(data, color = '', meshNameConfig = {}, options = {}) {
2840
+ console.log(data);
2733
2841
  // data = require('./mock.json');
2734
2842
  // 使用新的分帧加载方法,提供进度回调
2735
2843
  return this.drawModelWithBatchLoading(
@@ -2739,7 +2847,7 @@ export default {
2739
2847
  options,
2740
2848
  progress => {
2741
2849
  // 触发原有的进度事件
2742
- this.$emit('loadProgress', progress);
2850
+ this.$emit('loadProgress', this.progress);
2743
2851
  },
2744
2852
  complete => {
2745
2853
  options?.onComplete?.(complete);
@@ -2751,13 +2859,13 @@ export default {
2751
2859
  },
2752
2860
  // 动态设置视角滚轮的距离
2753
2861
  setCameraConfig() {
2754
- // let box3 = new this.THREE.Box3().setFromObject(sceneBoundingBox);
2862
+ // let box3 = new this.THREE.Box3().setFromObject(this.sceneBoundingBox);
2755
2863
  let size = new this.THREE.Vector3();
2756
- sceneBoundingBox.getSize(size);
2864
+ this.sceneBoundingBox.getSize(size);
2757
2865
  const maxBorder = Math.max(size.x, size.y, size.z);
2758
- // cameraControls.camera.far = maxBorder * 10; // 设置相机的远裁剪面
2759
- cameraControls.minDistance = maxBorder * 0.2; // 动态设置视角滚轮的距离
2760
- camera.updateProjectionMatrix();
2866
+ // this.cameraControls.this.camera.far = maxBorder * 10; // 设置相机的远裁剪面
2867
+ this.cameraControls.minDistance = maxBorder * 0.2; // 动态设置视角滚轮的距离
2868
+ this.camera.updateProjectionMatrix();
2761
2869
  },
2762
2870
  // 获取mesh的中心点
2763
2871
  getMeshCenterAndVolume(mesh) {
@@ -2773,14 +2881,14 @@ export default {
2773
2881
  },
2774
2882
  mouseDown(event) {
2775
2883
  // 在鼠标按下时启用渲染中断以提升响应性
2776
- forceSkipRendering = true;
2777
- skipNextRenderFrame = true;
2884
+ this.forceSkipRendering = true;
2885
+ this.skipNextRenderFrame = true;
2778
2886
 
2779
2887
  const intersects = this.getRaycasterObjects(event);
2780
- firstTime = new Date().getTime();
2888
+ this.firstTime = new Date().getTime();
2781
2889
  let params = {
2782
2890
  event,
2783
- firstTime,
2891
+ firstTime: this.firstTime,
2784
2892
  };
2785
2893
  intersects.length &&
2786
2894
  (params.v3Position = {
@@ -2792,18 +2900,20 @@ export default {
2792
2900
  },
2793
2901
  getRaycasterObjects(event) {
2794
2902
  // 保护空值,避免在未初始化或销毁后触发错误
2795
- if (!renderer || !renderer.domElement || !camera || !scene) return [];
2903
+ if (!this.renderer || !this.renderer.domElement || !this.camera || !this.scene) return [];
2796
2904
  // 获取元素在页面中的偏移位置
2797
- const rect = renderer.domElement.getBoundingClientRect();
2905
+ const rect = this.renderer.domElement.getBoundingClientRect();
2798
2906
  const x = event.clientX - rect.left;
2799
2907
  const y = event.clientY - rect.top;
2800
2908
 
2801
- // mouse.x = (event.clientX / instructions.offsetWidth) * 2 - 1;
2802
- // mouse.y = -(event.clientY / instructions.offsetHeight) * 2 + 1;
2803
- mouse.x = (x / rect.width) * 2 - 1;
2804
- mouse.y = -(y / rect.height) * 2 + 1;
2805
- raycaster.setFromCamera(mouse, camera);
2806
- return scene && scene.children ? raycaster.intersectObjects(scene.children, true) : [];
2909
+ // this.mouse.x = (event.clientX / this.instructions.offsetWidth) * 2 - 1;
2910
+ // this.mouse.y = -(event.clientY / this.instructions.offsetHeight) * 2 + 1;
2911
+ this.mouse.x = (x / rect.width) * 2 - 1;
2912
+ this.mouse.y = -(y / rect.height) * 2 + 1;
2913
+ this.raycaster.setFromCamera(this.mouse, this.camera);
2914
+ return this.scene && this.scene.children
2915
+ ? this.raycaster.intersectObjects(this.scene.children, true)
2916
+ : [];
2807
2917
  },
2808
2918
  getInstanceId(instancedMesh, instanceIndex, options) {
2809
2919
  if (!instancedMesh || instanceIndex === undefined || instanceIndex === null) {
@@ -2819,28 +2929,28 @@ export default {
2819
2929
  mouseClick(event) {
2820
2930
  // 鼠标抬起时重置渲染中断标记
2821
2931
  setTimeout(() => {
2822
- forceSkipRendering = false;
2932
+ this.forceSkipRendering = false;
2823
2933
  // interactionFrameCount = 0;
2824
2934
  // 恢复批次加载操作
2825
2935
  this.resumeBatchLoading();
2826
2936
  }, 50); // 短暂延迟确保交互完成
2827
2937
 
2828
2938
  // 在测量模式下,不进行事件暴露
2829
- if (!measureFlag) {
2830
- lastTime = new Date().getTime();
2831
- if (lastTime - firstTime < 300) {
2939
+ if (!this.measureFlag) {
2940
+ this.lastTime = new Date().getTime();
2941
+ if (this.lastTime - this.firstTime < 300) {
2832
2942
  const intersects = this.getRaycasterObjects(event);
2833
2943
  let params = {};
2834
2944
  let cameraData = {
2835
2945
  position: {
2836
- x: cameraControls.camera.position.x,
2837
- y: cameraControls.camera.position.y,
2838
- z: cameraControls.camera.position.z,
2946
+ x: this.cameraControls.camera.position.x,
2947
+ y: this.cameraControls.camera.position.y,
2948
+ z: this.cameraControls.camera.position.z,
2839
2949
  },
2840
2950
  lookAt: {
2841
- heading: cameraControls._target.x,
2842
- pitch: cameraControls._target.y,
2843
- roll: cameraControls._target.z,
2951
+ heading: this.cameraControls._target.x,
2952
+ pitch: this.cameraControls._target.y,
2953
+ roll: this.cameraControls._target.z,
2844
2954
  },
2845
2955
  };
2846
2956
  if (intersects.length > 0) {
@@ -2874,11 +2984,11 @@ export default {
2874
2984
  if (event.button === 0) {
2875
2985
  this.$emit('leftClick', params);
2876
2986
  } else if (event.button === 1) {
2877
- if (lastTime - lastMiddleClickTime < 2000) {
2987
+ if (this.lastTime - this.lastMiddleClickTime < 2000) {
2878
2988
  this.$emit('middleDblClick', params);
2879
- lastMiddleClickTime = 0;
2989
+ this.lastMiddleClickTime = 0;
2880
2990
  } else {
2881
- lastMiddleClickTime = lastTime;
2991
+ this.lastMiddleClickTime = this.lastTime;
2882
2992
  }
2883
2993
  }
2884
2994
  }
@@ -2886,24 +2996,24 @@ export default {
2886
2996
  },
2887
2997
  // 窗口发生改变时, 更新渲染器与相机的大小
2888
2998
  resize(width, height) {
2889
- if (camera) {
2890
- camera.aspect = width / height;
2891
- camera.updateProjectionMatrix();
2892
- renderer.setSize(width, height, true);
2893
- if (renderTarget) {
2894
- renderTarget.setSize(width, height);
2999
+ if (this.camera) {
3000
+ this.camera.aspect = width / height;
3001
+ this.camera.updateProjectionMatrix();
3002
+ this.renderer.setSize(width, height, true);
3003
+ if (this.renderTarget) {
3004
+ this.renderTarget.setSize(width, height);
2895
3005
  }
2896
- if (outlineComposer) {
2897
- outlineComposer.setSize(width, height);
3006
+ if (this.outlineComposer) {
3007
+ this.outlineComposer.setSize(width, height);
2898
3008
  }
2899
- if (outlinePass) {
2900
- outlinePass.setSize(width, height);
3009
+ if (this.outlinePass) {
3010
+ this.outlinePass.setSize(width, height);
2901
3011
  }
2902
- labelRenderer.setSize(width, height);
3012
+ this.labelRenderer.setSize(width, height);
2903
3013
  // this.timeRender()
2904
3014
  // 这里也要更新测量
2905
- if (threeMeasure) {
2906
- threeMeasure.updateParams(width, height);
3015
+ if (this.threeMeasure) {
3016
+ this.threeMeasure.updateParams(width, height);
2907
3017
  }
2908
3018
  }
2909
3019
  },
@@ -2943,7 +3053,7 @@ export default {
2943
3053
  roll: center.z,
2944
3054
  enableTransition: options.enableTransition,
2945
3055
  });
2946
- // cameraControls.setLookAt(
3056
+ // this.cameraControls.setLookAt(
2947
3057
  // center.x,
2948
3058
  // center.y + size.y,
2949
3059
  // center.z + size.z / 2,
@@ -2952,37 +3062,37 @@ export default {
2952
3062
  // center.z,
2953
3063
  // true
2954
3064
  // );
2955
- // cameraControls.update(0);
2956
- // camera.updateProjectionMatrix();
2957
- // cameraControls.saveState();
3065
+ // this.cameraControls.update(0);
3066
+ // this.camera.updateProjectionMatrix();
3067
+ // this.cameraControls.saveState();
2958
3068
  },
2959
3069
  // 带防抖和用户交互检测的智能居中方法
2960
3070
  smartModelCenter(mesh, debounceDelay = 100) {
2961
- // if (hasExecutedCentering) {
3071
+ // if (this.hasExecutedCentering) {
2962
3072
  // return;
2963
3073
  // }
2964
3074
 
2965
3075
  // 如果用户正在交互,标记需要在交互结束后居中
2966
- if (userInteracting) {
2967
- needsCenteringAfterInteraction = true;
3076
+ if (this.userInteracting) {
3077
+ this.needsCenteringAfterInteraction = true;
2968
3078
  return;
2969
3079
  }
2970
3080
 
2971
3081
  // 清除之前的防抖定时器
2972
- if (centeringDebounceTimer) {
2973
- clearTimeout(centeringDebounceTimer);
3082
+ if (this.centeringDebounceTimer) {
3083
+ clearTimeout(this.centeringDebounceTimer);
2974
3084
  }
2975
3085
 
2976
3086
  // 设置新的防抖定时器
2977
- centeringDebounceTimer = setTimeout(() => {
3087
+ this.centeringDebounceTimer = setTimeout(() => {
2978
3088
  // 如果已经执行过居中操作,则不再执行
2979
3089
  if (
2980
- !userInteracting &&
2981
- !hasExecutedCentering &&
3090
+ !this.userInteracting &&
3091
+ !this.hasExecutedCentering &&
2982
3092
  this.noObserver.documentModelIds.size > 0
2983
3093
  ) {
2984
3094
  this.setModelCenter(mesh, { enableTransition: false });
2985
- hasExecutedCentering = true;
3095
+ this.hasExecutedCentering = true;
2986
3096
  // 触发场景更新
2987
3097
  this.notifyCameraChange();
2988
3098
  }
@@ -3004,9 +3114,10 @@ export default {
3004
3114
  * '#ff0000'
3005
3115
  * '#f00'
3006
3116
  * 'red'
3117
+ * hasOutline 是否需要加上高亮边。默认为true
3007
3118
  * }
3008
3119
  */
3009
- updateProperty(list) {
3120
+ updateProperty(list, hasOutline = true) {
3010
3121
  let isUpdate = false;
3011
3122
  for (let index = 0; index < list.length; index++) {
3012
3123
  let ele = list[index];
@@ -3025,12 +3136,14 @@ export default {
3025
3136
  this.removeOutlineObject(children, instanceIndex);
3026
3137
  return;
3027
3138
  }
3028
- if (outlinePass) {
3029
- if (children.isInstancedMesh) {
3030
- const proxy = this.ensureOutlineInstanceProxy(children, instanceIndex);
3031
- this.pushOutlineTarget(proxy);
3032
- } else if (!this.isSourceHiddenInstance(children, instanceId)) {
3033
- this.pushOutlineTarget(children);
3139
+ if (hasOutline) {
3140
+ if (this.outlinePass) {
3141
+ if (children.isInstancedMesh) {
3142
+ const proxy = this.ensureOutlineInstanceProxy(children, instanceIndex);
3143
+ this.pushOutlineTarget(proxy);
3144
+ } else if (!this.isSourceHiddenInstance(children, instanceId)) {
3145
+ this.pushOutlineTarget(children);
3146
+ }
3034
3147
  }
3035
3148
  }
3036
3149
  }
@@ -3111,8 +3224,8 @@ export default {
3111
3224
  });
3112
3225
  }
3113
3226
 
3114
- if (scene) {
3115
- scene.traverse(child => {
3227
+ if (this.scene) {
3228
+ this.scene.traverse(child => {
3116
3229
  if (!child || !child.isInstancedMesh) return;
3117
3230
  const userData = child.userData || {};
3118
3231
  if (!(userData.instancesMap instanceof Map) || userData.instancesMap.size === 0) return;
@@ -3159,7 +3272,7 @@ export default {
3159
3272
  this.removeOutlineObject(children, instanceIndex);
3160
3273
  return;
3161
3274
  }
3162
- if (outlinePass) {
3275
+ if (this.outlinePass) {
3163
3276
  if (children.isInstancedMesh) {
3164
3277
  const proxy = this.ensureOutlineInstanceProxy(children, instanceIndex);
3165
3278
  this.pushOutlineTarget(proxy);
@@ -3182,8 +3295,8 @@ export default {
3182
3295
  // 恢复模型原来的状态
3183
3296
  updateWholeProperty() {
3184
3297
  throw new Error('该方法已暂停使用,请使用restore方法。');
3185
- // if (scene) {
3186
- // scene.traverse(obj => {
3298
+ // if (this.scene) {
3299
+ // this.scene.traverse(obj => {
3187
3300
  // if (obj instanceof this.THREE.Mesh) {
3188
3301
  // // 恢复颜色
3189
3302
  // obj.setColorAt(obj.userData.instanceIndex, obj.material.userData.nColor)
@@ -3253,12 +3366,12 @@ export default {
3253
3366
  const { instanceIndex } = children.userData.instancesMap.get(instanceId);
3254
3367
  children.setColorAt(instanceIndex, children.material.userData.nColor);
3255
3368
  children.instanceColor.needsUpdate = true;
3256
- if (outlinePass) {
3369
+ if (this.outlinePass) {
3257
3370
  if (children.isInstancedMesh) {
3258
3371
  this.removeOutlineInstanceProxy(children, instanceIndex);
3259
3372
  } else {
3260
- const idx = outlinePass.selectedObjects.indexOf(children);
3261
- if (idx !== -1) outlinePass.selectedObjects.splice(idx, 1);
3373
+ const idx = this.outlinePass.selectedObjects.indexOf(children);
3374
+ if (idx !== -1) this.outlinePass.selectedObjects.splice(idx, 1);
3262
3375
  }
3263
3376
  }
3264
3377
  }
@@ -3301,7 +3414,7 @@ export default {
3301
3414
  // 定位到模型
3302
3415
  // name 模型名称 可以是数组
3303
3416
  locateModel(name) {
3304
- if (!scene) return;
3417
+ if (!this.scene) return;
3305
3418
  if (Array.isArray(name)) {
3306
3419
  const box3 = new this.THREE.Box3();
3307
3420
  name.forEach(n => {
@@ -3319,7 +3432,7 @@ export default {
3319
3432
  }
3320
3433
  let obj = this.getObjectByName(name)[0];
3321
3434
  if (obj) {
3322
- // cameraControls.fitToSphere(obj.parent, true); // TODO 待处理,先用 setModelCenter 进行定位
3435
+ // this.cameraControls.fitToSphere(obj.parent, true); // TODO 待处理,先用 setModelCenter 进行定位
3323
3436
  if (obj.isGroup) {
3324
3437
  this.setModelCenter(obj);
3325
3438
  } else if (obj.isMesh) {
@@ -3343,8 +3456,8 @@ export default {
3343
3456
  }
3344
3457
  */
3345
3458
  updatePropertyByCustom(params) {
3346
- if (!scene) return;
3347
- scene.traverse(child => {
3459
+ if (!this.scene) return;
3460
+ this.scene.traverse(child => {
3348
3461
  if (child.isMesh && child.userData[params.customName] === params.customValue) {
3349
3462
  for (const key in params.attr) {
3350
3463
  switch (key) {
@@ -3376,7 +3489,7 @@ export default {
3376
3489
  */
3377
3490
  cameraLocation(params) {
3378
3491
  const { enableTransition = true } = params;
3379
- cameraControls.setLookAt(
3492
+ this.cameraControls.setLookAt(
3380
3493
  params.x,
3381
3494
  params.y,
3382
3495
  params.z,
@@ -3385,7 +3498,7 @@ export default {
3385
3498
  params.roll,
3386
3499
  enableTransition
3387
3500
  );
3388
- cameraControls.update(0);
3501
+ this.cameraControls.update(0);
3389
3502
  },
3390
3503
  // 使用中心点和实体的长宽高进行定位
3391
3504
  /*
@@ -3400,7 +3513,7 @@ export default {
3400
3513
 
3401
3514
  let maxDim = Math.max(size.x, size.y, size.z);
3402
3515
  let minDim = Math.min(size.x, size.y, size.z);
3403
- let fov = camera.fov * (Math.PI / 180);
3516
+ let fov = this.camera.fov * (Math.PI / 180);
3404
3517
  // let baseDistance = viewAll
3405
3518
  // ? Math.abs((minDim * 1.0) / Math.sin(fov / 2))
3406
3519
  // : Math.abs((maxDim * 1.0) / Math.sin(fov / 2));
@@ -3412,7 +3525,7 @@ export default {
3412
3525
  let cameraCenter = viewAll
3413
3526
  ? new this.THREE.Vector3(p.x, p.y, p.z)
3414
3527
  : new this.THREE.Vector3(p.x, p.y, p.z).addScalar(Math.max(size.x, size.y, size.z));
3415
- cameraControls.setLookAt(
3528
+ this.cameraControls.setLookAt(
3416
3529
  cameraCenter.x,
3417
3530
  cameraCenter.y,
3418
3531
  cameraCenter.z,
@@ -3432,11 +3545,11 @@ export default {
3432
3545
  }
3433
3546
  */
3434
3547
  billboard(data) {
3435
- if (!scene) return null;
3548
+ if (!this.scene) return null;
3436
3549
  const divLabel = new CSS2DObject(data.billboard);
3437
3550
  divLabel.name = data.labelClass; // 这个是用来清除广告牌用的
3438
3551
  divLabel.position.set(data.x, data.y, data.z);
3439
- scene.add(divLabel);
3552
+ this.scene.add(divLabel);
3440
3553
  return divLabel;
3441
3554
  },
3442
3555
  // 通过名字获取实体对象, 返回数组
@@ -3445,10 +3558,10 @@ export default {
3445
3558
  passType: 要过滤的类型,默认是group,不返回group类型的实体
3446
3559
  */
3447
3560
  getObjectByName(name, passType = 'group') {
3448
- if (!scene) return [];
3561
+ if (!this.scene) return [];
3449
3562
  let object = [];
3450
3563
  const instancedMeshProps = instanceToInstancedMeshMap.get(name);
3451
- scene.traverse(item => {
3564
+ this.scene.traverse(item => {
3452
3565
  const tempName = instancedMeshProps ? instancedMeshProps.drawObjectId : name;
3453
3566
  if (item.name == tempName && item.type.toLowerCase() != passType.toLowerCase()) {
3454
3567
  object.push(item);
@@ -3458,8 +3571,8 @@ export default {
3458
3571
  },
3459
3572
  // 通过id获取实体对象, 返回查找到的对象
3460
3573
  getObjectById(id) {
3461
- if (!scene) return null;
3462
- return scene.getObjectById(id);
3574
+ if (!this.scene) return null;
3575
+ return this.scene.getObjectById(id);
3463
3576
  },
3464
3577
  getColorConfig() {
3465
3578
  const modelState = this.noObserver
@@ -3511,7 +3624,7 @@ export default {
3511
3624
  },
3512
3625
  // 通过id删除对象
3513
3626
  removeObjectById(id) {
3514
- if (!scene) return;
3627
+ if (!this.scene) return;
3515
3628
  let array = this.getObjectByName(id);
3516
3629
  array.forEach(item => {
3517
3630
  if (item.name === id) {
@@ -3520,13 +3633,13 @@ export default {
3520
3633
  if (item.isMesh) {
3521
3634
  item.clear();
3522
3635
  }
3523
- scene.remove(item);
3636
+ this.scene.remove(item);
3524
3637
  }
3525
3638
  });
3526
3639
  },
3527
3640
  // 通过名称删除对象
3528
3641
  removeObjectByName(name) {
3529
- if (!scene) return;
3642
+ if (!this.scene) return;
3530
3643
  let array = this.getObjectByName(name);
3531
3644
  array.forEach(item => {
3532
3645
  item.material && item.material.dispose();
@@ -3534,13 +3647,13 @@ export default {
3534
3647
  if (item.isMesh) {
3535
3648
  item.clear();
3536
3649
  }
3537
- if (scene) scene.remove(item);
3650
+ if (this.scene) this.scene.remove(item);
3538
3651
  });
3539
3652
  },
3540
3653
  // 删除场景中所有的实体
3541
3654
  removeAll() {
3542
3655
  return new Promise(resolve => {
3543
- if (scene) {
3656
+ if (this.scene) {
3544
3657
  this.removeTraverse();
3545
3658
  this.removeModelByDocumentId();
3546
3659
  resolve();
@@ -3550,10 +3663,10 @@ export default {
3550
3663
  });
3551
3664
  },
3552
3665
  removeTraverse() {
3553
- if (!modelGroup) return;
3554
- let length = modelGroup.children.length;
3666
+ if (!this.modelGroup) return;
3667
+ let length = this.modelGroup.children.length;
3555
3668
  if (length > 0) {
3556
- let list = modelGroup.children[0];
3669
+ let list = this.modelGroup.children[0];
3557
3670
  if (!list) return;
3558
3671
  list.traverse(item => {
3559
3672
  if (item.isMesh) {
@@ -3562,32 +3675,32 @@ export default {
3562
3675
  item.clear();
3563
3676
  item.material = null;
3564
3677
  item.geometry = null;
3565
- modelGroup && modelGroup.remove(item);
3678
+ this.modelGroup && this.modelGroup.remove(item);
3566
3679
  item = null;
3567
3680
  }
3568
3681
  });
3569
- modelGroup && modelGroup.remove(list);
3682
+ this.modelGroup && this.modelGroup.remove(list);
3570
3683
  this.removeTraverse();
3571
3684
  } else {
3572
3685
  // 在这里清除一些标记之类的
3573
- modelGroup.clear();
3574
- scene.remove(modelGroup);
3575
- renderer.clear();
3576
- modelGroup = null;
3577
- modelGroup = new this.THREE.Group();
3686
+ this.modelGroup.clear();
3687
+ this.scene.remove(this.modelGroup);
3688
+ this.renderer.clear();
3689
+ this.modelGroup = null;
3690
+ this.modelGroup = new this.THREE.Group();
3578
3691
  }
3579
3692
  },
3580
3693
  // 销毁场景 释放内存
3581
3694
  destroyScene() {
3582
- cancelAnimationFrame(animateId);
3695
+ cancelAnimationFrame(this.animateId);
3583
3696
 
3584
3697
  if (this.noObserver && this.noObserver.outlineInstanceProxyMap) {
3585
3698
  this.noObserver.outlineInstanceProxyMap.forEach(proxy => {
3586
- if (outlinePass) {
3587
- const idx = outlinePass.selectedObjects.indexOf(proxy);
3588
- if (idx !== -1) outlinePass.selectedObjects.splice(idx, 1);
3699
+ if (this.outlinePass) {
3700
+ const idx = this.outlinePass.selectedObjects.indexOf(proxy);
3701
+ if (idx !== -1) this.outlinePass.selectedObjects.splice(idx, 1);
3589
3702
  }
3590
- if (scene) scene.remove(proxy);
3703
+ if (this.scene) this.scene.remove(proxy);
3591
3704
  if (proxy && proxy.material && proxy.material.dispose) proxy.material.dispose();
3592
3705
  });
3593
3706
  this.noObserver.outlineInstanceProxyMap.clear();
@@ -3597,9 +3710,9 @@ export default {
3597
3710
  ? this.noObserver.batchLoadingState
3598
3711
  : this.batchLoadingState;
3599
3712
  // 清理防抖定时器和重置状态
3600
- if (centeringDebounceTimer) {
3601
- clearTimeout(centeringDebounceTimer);
3602
- centeringDebounceTimer = null;
3713
+ if (this.centeringDebounceTimer) {
3714
+ clearTimeout(this.centeringDebounceTimer);
3715
+ this.centeringDebounceTimer = null;
3603
3716
  }
3604
3717
 
3605
3718
  // 清理渲染暂停/恢复相关的定时器
@@ -3617,9 +3730,9 @@ export default {
3617
3730
  // 停止批量加载
3618
3731
  this.stopBatchLoading();
3619
3732
 
3620
- hasExecutedCentering = false;
3621
- needsCenteringAfterInteraction = false;
3622
- userInteracting = false;
3733
+ this.hasExecutedCentering = false;
3734
+ this.needsCenteringAfterInteraction = false;
3735
+ this.userInteracting = false;
3623
3736
 
3624
3737
  // 关闭视椎体裁切并清理防抖定时器
3625
3738
  this.modelStateManager.frustumCheckEnabled = false;
@@ -3629,36 +3742,36 @@ export default {
3629
3742
  }
3630
3743
  this.clearBypassCullingModelIds();
3631
3744
 
3632
- if (scene) {
3745
+ if (this.scene) {
3633
3746
  this.removeTraverse();
3634
- scene.clear();
3635
- scene = null;
3747
+ this.scene.clear();
3748
+ this.scene = null;
3636
3749
  }
3637
3750
 
3638
3751
  // 移除滚轮事件监听器
3639
- if (renderer && renderer.domElement && this._wheelHandler) {
3640
- renderer.domElement.removeEventListener('wheel', this._wheelHandler);
3752
+ if (this.renderer && this.renderer.domElement && this._wheelHandler) {
3753
+ this.renderer.domElement.removeEventListener('wheel', this._wheelHandler);
3641
3754
  this._wheelHandler = null;
3642
3755
  }
3643
3756
 
3644
3757
  // 移除鼠标点击/按下事件监听器
3645
- if (renderer && renderer.domElement) {
3646
- renderer.domElement.removeEventListener('mouseup', this.mouseClick, false);
3647
- renderer.domElement.removeEventListener('mousedown', this.mouseDown, false);
3648
- renderer.domElement.removeEventListener('pointerup', this.mouseClick, false);
3649
- renderer.domElement.removeEventListener('pointerdown', this.mouseDown, false);
3758
+ if (this.renderer && this.renderer.domElement) {
3759
+ this.renderer.domElement.removeEventListener('mouseup', this.mouseClick, false);
3760
+ this.renderer.domElement.removeEventListener('mousedown', this.mouseDown, false);
3761
+ this.renderer.domElement.removeEventListener('pointerup', this.mouseClick, false);
3762
+ this.renderer.domElement.removeEventListener('pointerdown', this.mouseDown, false);
3650
3763
  }
3651
3764
 
3652
3765
  // 取消 pointer lock 并移除相关键盘事件
3653
- if (pointControls) {
3766
+ if (this.pointControls) {
3654
3767
  if (this._onFirstPersonChange) {
3655
3768
  try {
3656
- pointControls.removeEventListener('change', this._onFirstPersonChange);
3769
+ this.pointControls.removeEventListener('change', this._onFirstPersonChange);
3657
3770
  } catch (e) {}
3658
3771
  this._onFirstPersonChange = null;
3659
3772
  }
3660
3773
  try {
3661
- pointControls.unlock && pointControls.unlock();
3774
+ this.pointControls.unlock && this.pointControls.unlock();
3662
3775
  } catch (e) {}
3663
3776
  }
3664
3777
  if (this.noObserver) {
@@ -3669,38 +3782,38 @@ export default {
3669
3782
  document.removeEventListener('keydown', this.handleMeasureKeyDown, false);
3670
3783
 
3671
3784
  // 关闭测量工具
3672
- if (threeMeasure) {
3785
+ if (this.threeMeasure) {
3673
3786
  try {
3674
- threeMeasure.close && threeMeasure.close();
3787
+ this.threeMeasure.close && this.threeMeasure.close();
3675
3788
  } catch (e) {}
3676
- threeMeasure = null;
3789
+ this.threeMeasure = null;
3677
3790
  }
3678
3791
 
3679
- // 移除 cameraControls 事件监听
3680
- if (cameraControls) {
3792
+ // 移除 this.cameraControls 事件监听
3793
+ if (this.cameraControls) {
3681
3794
  if (this._onControlStart)
3682
- cameraControls.removeEventListener('controlstart', this._onControlStart);
3795
+ this.cameraControls.removeEventListener('controlstart', this._onControlStart);
3683
3796
  if (this._onControlEnd)
3684
- cameraControls.removeEventListener('controlend', this._onControlEnd);
3797
+ this.cameraControls.removeEventListener('controlend', this._onControlEnd);
3685
3798
  this._onControlStart = null;
3686
3799
  this._onControlEnd = null;
3687
3800
  }
3688
3801
 
3689
3802
  // 清理 Label 渲染器 DOM
3690
- if (labelRenderer && labelRenderer.domElement && instructions) {
3803
+ if (this.labelRenderer && this.labelRenderer.domElement && this.instructions) {
3691
3804
  try {
3692
- instructions.removeChild(labelRenderer.domElement);
3805
+ this.instructions.removeChild(this.labelRenderer.domElement);
3693
3806
  } catch (e) {}
3694
3807
  }
3695
- labelRenderer = null;
3808
+ this.labelRenderer = null;
3696
3809
 
3697
- renderer.forceContextLoss();
3698
- renderer.dispose();
3699
- camera = null;
3700
- cameraControls = null;
3701
- pointControls = null;
3702
- renderer.domElement = null;
3703
- renderer = null;
3810
+ this.renderer.forceContextLoss();
3811
+ this.renderer.dispose();
3812
+ this.camera = null;
3813
+ this.cameraControls = null;
3814
+ this.pointControls = null;
3815
+ this.renderer.domElement = null;
3816
+ this.renderer = null;
3704
3817
  },
3705
3818
  // 绘制曲线
3706
3819
  /*
@@ -3718,8 +3831,10 @@ export default {
3718
3831
  route.push(new this.THREE.Vector3(element.x, element.y, element.z));
3719
3832
  });
3720
3833
  if (route.length > 1) {
3721
- curve = new this.THREE.CatmullRomCurve3(route);
3722
- const geometryLine = new this.THREE.BufferGeometry().setFromPoints(curve.getPoints(5000));
3834
+ this.curve = new this.THREE.CatmullRomCurve3(route);
3835
+ const geometryLine = new this.THREE.BufferGeometry().setFromPoints(
3836
+ this.curve.getPoints(5000)
3837
+ );
3723
3838
  const materialLine = new this.THREE.LineBasicMaterial({
3724
3839
  color: params.color,
3725
3840
  depthTest: false,
@@ -3727,7 +3842,7 @@ export default {
3727
3842
  });
3728
3843
  let line = new this.THREE.Line(geometryLine, materialLine);
3729
3844
  line.name = params.name;
3730
- if (scene) scene.add(line);
3845
+ if (this.scene) this.scene.add(line);
3731
3846
  }
3732
3847
  },
3733
3848
  // 绘制贴图曲线
@@ -3747,24 +3862,24 @@ export default {
3747
3862
  // 画曲线
3748
3863
  if (route.length > 1) {
3749
3864
  // 曲线 作为路径
3750
- curve = new this.THREE.CatmullRomCurve3(route, false, 'catmullrom', 0);
3751
- const geometry = new this.THREE.TubeGeometry(curve, 5000, 0.5, 4);
3865
+ this.curve = new this.THREE.CatmullRomCurve3(route, false, 'catmullrom', 0);
3866
+ const geometry = new this.THREE.TubeGeometry(this.curve, 5000, 0.5, 4);
3752
3867
  //加载纹理
3753
- lineTexture = new this.THREE.TextureLoader().load('/static/arrow/arrow-right.png');
3754
- lineTexture.wrapS = this.THREE.RepeatWrapping;
3755
- lineTexture.wrapT = this.THREE.RepeatWrapping;
3756
- lineTexture.repeat.set(20, 1); //水平重复20次
3757
- lineTexture.needsUpdate = true;
3758
- lineTexture.offset.y = 0.5;
3868
+ this.lineTexture = new this.THREE.TextureLoader().load('/static/arrow/arrow-right.png');
3869
+ this.lineTexture.wrapS = this.THREE.RepeatWrapping;
3870
+ this.lineTexture.wrapT = this.THREE.RepeatWrapping;
3871
+ this.lineTexture.repeat.set(20, 1); //水平重复20次
3872
+ this.lineTexture.needsUpdate = true;
3873
+ this.lineTexture.offset.y = 0.5;
3759
3874
  const material = new this.THREE.MeshBasicMaterial({
3760
- map: lineTexture,
3875
+ map: this.lineTexture,
3761
3876
  side: this.THREE.BackSide, //显示背面
3762
3877
  transparent: true,
3763
3878
  });
3764
3879
  let line = new this.THREE.Line(geometry, material);
3765
3880
  line.name = params.name;
3766
- if (scene) scene.add(line);
3767
- clock = new this.THREE.Clock();
3881
+ if (this.scene) this.scene.add(line);
3882
+ this.clock = new this.THREE.Clock();
3768
3883
  }
3769
3884
  },
3770
3885
  //
@@ -3777,8 +3892,8 @@ export default {
3777
3892
  name: '', 路径的名称,用来清除用的
3778
3893
  */
3779
3894
  startRoam(params) {
3780
- progress = 0;
3781
- roamConfig = {
3895
+ this.progress = 0;
3896
+ this.roamConfig = {
3782
3897
  loop: params.loop,
3783
3898
  speed: params.speed,
3784
3899
  };
@@ -3786,7 +3901,7 @@ export default {
3786
3901
  name: params.name,
3787
3902
  path: params.path,
3788
3903
  });
3789
- roaming = true;
3904
+ this.roaming = true;
3790
3905
  },
3791
3906
  // 更新漫游的配置
3792
3907
  /*
@@ -3794,52 +3909,52 @@ export default {
3794
3909
  */
3795
3910
  updateRoamConfig(params) {
3796
3911
  for (let key in params) {
3797
- roamConfig[key] = params[key];
3912
+ this.roamConfig[key] = params[key];
3798
3913
  }
3799
3914
  },
3800
3915
  // 结束/退出漫游
3801
3916
  endRoam() {
3802
- roaming = false;
3803
- progress = 0;
3804
- this.removeObjectByName(roamConfig.name);
3805
- roamConfig = {
3917
+ this.roaming = false;
3918
+ this.progress = 0;
3919
+ this.removeObjectByName(this.roamConfig.name);
3920
+ this.roamConfig = {
3806
3921
  loop: false,
3807
3922
  speed: 0,
3808
3923
  name: '',
3809
3924
  };
3810
- roaming = false;
3811
- lineTexture = null;
3925
+ this.roaming = false;
3926
+ this.lineTexture = null;
3812
3927
  },
3813
3928
  // 相机跟随轨道
3814
3929
  cameraTrack() {
3815
- if (roaming) {
3930
+ if (this.roaming) {
3816
3931
  // renderEnabled = true
3817
- lineTexture.offset.x -= 0.05;
3932
+ this.lineTexture.offset.x -= 0.05;
3818
3933
  // 相机和控制器的偏移
3819
- let offset = 10 / curve.getLength();
3820
- // progress 取值范围为0~1。getPoint(0)表示曲线起点,getPoint(1)表示曲线终点
3821
- if (progress <= 1 - offset) {
3934
+ let offset = 10 / this.curve.getLength();
3935
+ // this.progress 取值范围为0~1。getPoint(0)表示曲线起点,getPoint(1)表示曲线终点
3936
+ if (this.progress <= 1 - offset) {
3822
3937
  // this.timeRender()
3823
- const point = curve.getPointAt(progress);
3824
- const pointBox = curve.getPointAt(progress + offset);
3825
- camera.position.set(point.x, point.y + 5, point.z);
3826
- camera.lookAt(pointBox.x, pointBox.y + 5, pointBox.z);
3827
- cameraControls.setPosition(point.x, point.y + 5, point.z, false);
3828
- cameraControls.setTarget(pointBox.x, pointBox.y + 5, pointBox.z, false);
3829
- progress += roamConfig.speed / 300;
3938
+ const point = this.curve.getPointAt(this.progress);
3939
+ const pointBox = this.curve.getPointAt(this.progress + offset);
3940
+ this.camera.position.set(point.x, point.y + 5, point.z);
3941
+ this.camera.lookAt(pointBox.x, pointBox.y + 5, pointBox.z);
3942
+ this.cameraControls.setPosition(point.x, point.y + 5, point.z, false);
3943
+ this.cameraControls.setTarget(pointBox.x, pointBox.y + 5, pointBox.z, false);
3944
+ this.progress += this.roamConfig.speed / 300;
3830
3945
 
3831
3946
  if (typeof this._cameraChangeObserver === 'function') {
3832
3947
  this._cameraChangeObserver('roam');
3833
3948
  }
3834
3949
  } else {
3835
3950
  // 循环漫游
3836
- if (roamConfig.loop) {
3837
- progress = 0;
3951
+ if (this.roamConfig.loop) {
3952
+ this.progress = 0;
3838
3953
  }
3839
3954
  }
3840
3955
  } else {
3841
- lineTexture = null;
3842
- progress = 0;
3956
+ this.lineTexture = null;
3957
+ this.progress = 0;
3843
3958
  }
3844
3959
  },
3845
3960
  // 全局整体炸开
@@ -3847,10 +3962,10 @@ export default {
3847
3962
  * 参数val: 爆炸的值
3848
3963
  */
3849
3964
  globalBomb(val) {
3850
- if (scene.children.length === 0) return;
3851
- for (let i = 0; i < scene.children.length; i++) {
3852
- console.log('scene', scene);
3853
- scene.children[i].traverse(item => {
3965
+ if (this.scene.children.length === 0) return;
3966
+ for (let i = 0; i < this.scene.children.length; i++) {
3967
+ console.log('this.scene', this.scene);
3968
+ this.scene.children[i].traverse(item => {
3854
3969
  if (!item.isMesh || !item.worldDir) return;
3855
3970
  // 爆炸公式
3856
3971
  this.computedBomb(item, val);
@@ -3882,7 +3997,7 @@ export default {
3882
3997
  },
3883
3998
  getClippingPlanes() {
3884
3999
  let clippingPlanesConstant = [];
3885
- renderer.clippingPlanes.forEach(item => {
4000
+ this.renderer.clippingPlanes.forEach(item => {
3886
4001
  clippingPlanesConstant.push(item.constant);
3887
4002
  });
3888
4003
  return clippingPlanesConstant;
@@ -3893,7 +4008,7 @@ export default {
3893
4008
  剖切值变换时, 使用
3894
4009
  */
3895
4010
  setGlobalClipping(flag = true) {
3896
- const box3 = new this.THREE.Box3().setFromObject(scene.children[0]);
4011
+ const box3 = new this.THREE.Box3().setFromObject(this.scene.children[0]);
3897
4012
  let max = box3.max;
3898
4013
  let min = box3.min;
3899
4014
  const clippingPlanes = [
@@ -3923,10 +4038,10 @@ export default {
3923
4038
  ),
3924
4039
  ];
3925
4040
 
3926
- renderer.clippingPlanes = clippingPlanes;
4041
+ this.renderer.clippingPlanes = clippingPlanes;
3927
4042
  if (flag) {
3928
4043
  // 全局剖切的初始值
3929
- guiParams = {
4044
+ this.guiParams = {
3930
4045
  x轴: Math.ceil(clippingPlanes[0].constant),
3931
4046
  y轴: Math.ceil(clippingPlanes[2].constant),
3932
4047
  z轴: Math.ceil(clippingPlanes[1].constant),
@@ -3968,13 +4083,13 @@ export default {
3968
4083
  z: 2,
3969
4084
  };
3970
4085
  for (const key in params) {
3971
- renderer.clippingPlanes[axis[key]].constant = params[key];
4086
+ this.renderer.clippingPlanes[axis[key]].constant = params[key];
3972
4087
  }
3973
4088
  // this.timeRender()
3974
4089
  },
3975
4090
  // 取消全局剖切
3976
4091
  cancelGlobalClipping() {
3977
- guiParams = {
4092
+ this.guiParams = {
3978
4093
  x轴: 0,
3979
4094
  y轴: 0,
3980
4095
  z轴: 0,
@@ -3983,14 +4098,14 @@ export default {
3983
4098
  '-z轴': 0,
3984
4099
  };
3985
4100
  this.clippingPlanesConstants = null;
3986
- renderer.clippingPlanes = Object.freeze([]);
3987
- gui && gui.destroy();
4101
+ this.renderer.clippingPlanes = Object.freeze([]);
4102
+ this.gui && this.gui.destroy();
3988
4103
  },
3989
4104
  // 单个实体的剖切/ 局部剖切 obj 实体对象 flag 代表是否使用图形组件自带的剖切面板 默认为true
3990
4105
  setLocalClipping(obj, flag = true) {
3991
- clippingMesh.splice(0);
3992
- clippingMesh.push(obj);
3993
- renderer.localClippingEnabled = true;
4106
+ this.clippingMesh.splice(0);
4107
+ this.clippingMesh.push(obj);
4108
+ this.renderer.localClippingEnabled = true;
3994
4109
  // mesh的boundingBox 可以获取到该对象位置的最大最小值 但是在这里我们需要把y、z轴互换
3995
4110
  const boundingBox = new this.THREE.Box3().copy(obj.boundingBox);
3996
4111
  boundingBox.applyMatrix4(obj.matrixWorld);
@@ -4006,7 +4121,7 @@ export default {
4006
4121
  obj.material.needsUpdate = true;
4007
4122
  // 将局部剖切的对象记录下来
4008
4123
  if (flag) {
4009
- guiParams = {
4124
+ this.guiParams = {
4010
4125
  x轴: clippingPlanes[0].constant,
4011
4126
  y轴: clippingPlanes[2].constant,
4012
4127
  z轴: clippingPlanes[1].constant,
@@ -4041,7 +4156,7 @@ export default {
4041
4156
  y: 1,
4042
4157
  z: 2,
4043
4158
  };
4044
- let obj = clippingMesh[clippingMesh.length - 1];
4159
+ let obj = this.clippingMesh[this.clippingMesh.length - 1];
4045
4160
  for (const key in params) {
4046
4161
  obj.material.clippingPlanes[axis[key]].constant = params[key];
4047
4162
  obj.material.needsUpdate = true;
@@ -4050,7 +4165,7 @@ export default {
4050
4165
  },
4051
4166
  // 取消局部/单个实体的剖切
4052
4167
  cancelLocalClipping() {
4053
- guiParams = {
4168
+ this.guiParams = {
4054
4169
  x轴: 0,
4055
4170
  y轴: 0,
4056
4171
  z轴: 0,
@@ -4058,20 +4173,20 @@ export default {
4058
4173
  '-y轴': 0,
4059
4174
  '-z轴': 0,
4060
4175
  };
4061
- gui && gui.destroy();
4062
- clippingMesh.forEach(item => {
4176
+ this.gui && this.gui.destroy();
4177
+ this.clippingMesh.forEach(item => {
4063
4178
  item.material.clippingPlanes = Object.freeze([]);
4064
4179
  item.material.needsUpdate = true;
4065
4180
  });
4066
- clippingMesh.splice(0);
4181
+ this.clippingMesh.splice(0);
4067
4182
  },
4068
4183
  // 添加剖切轴工具
4069
4184
  addClippingGui(title, boudingValue, objClipp1, objClipp2) {
4070
- gui = new GUI({
4185
+ this.gui = new GUI({
4071
4186
  title: title,
4072
4187
  });
4073
- gui
4074
- .add(guiParams, 'x轴')
4188
+ this.gui
4189
+ .add(this.guiParams, 'x轴')
4075
4190
  .step(0.001)
4076
4191
  .min(boudingValue.min.x)
4077
4192
  .max(boudingValue.max.x)
@@ -4079,8 +4194,8 @@ export default {
4079
4194
  objClipp1[0].constant = d;
4080
4195
  objClipp2 && (objClipp2[0].constant = d);
4081
4196
  });
4082
- gui
4083
- .add(guiParams, '-x轴')
4197
+ this.gui
4198
+ .add(this.guiParams, '-x轴')
4084
4199
  .step(0.001)
4085
4200
  .min(boudingValue.min.x)
4086
4201
  .max(boudingValue.max.x)
@@ -4088,8 +4203,8 @@ export default {
4088
4203
  objClipp1[3].constant = -d;
4089
4204
  objClipp2 && (objClipp2[3].constant = -d);
4090
4205
  });
4091
- gui
4092
- .add(guiParams, 'y轴')
4206
+ this.gui
4207
+ .add(this.guiParams, 'y轴')
4093
4208
  .step(0.001)
4094
4209
  .min(boudingValue.min.y)
4095
4210
  .max(boudingValue.max.y)
@@ -4097,8 +4212,8 @@ export default {
4097
4212
  objClipp1[2].constant = d;
4098
4213
  objClipp2 && (objClipp2[2].constant = d);
4099
4214
  });
4100
- gui
4101
- .add(guiParams, '-y轴')
4215
+ this.gui
4216
+ .add(this.guiParams, '-y轴')
4102
4217
  .step(0.001)
4103
4218
  .min(boudingValue.min.y)
4104
4219
  .max(boudingValue.max.y)
@@ -4106,8 +4221,8 @@ export default {
4106
4221
  objClipp1[5].constant = -d;
4107
4222
  objClipp2 && (objClipp2[5].constant = -d);
4108
4223
  });
4109
- gui
4110
- .add(guiParams, 'z轴')
4224
+ this.gui
4225
+ .add(this.guiParams, 'z轴')
4111
4226
  .step(0.001)
4112
4227
  .min(boudingValue.min.z)
4113
4228
  .max(boudingValue.max.z)
@@ -4115,8 +4230,8 @@ export default {
4115
4230
  objClipp1[1].constant = d;
4116
4231
  objClipp2 && (objClipp2[1].constant = d);
4117
4232
  });
4118
- gui
4119
- .add(guiParams, '-z轴')
4233
+ this.gui
4234
+ .add(this.guiParams, '-z轴')
4120
4235
  .step(0.001)
4121
4236
  .min(boudingValue.min.z)
4122
4237
  .max(boudingValue.max.z)
@@ -4128,19 +4243,19 @@ export default {
4128
4243
  // 开启第一视角
4129
4244
  startFirstPer(options) {
4130
4245
  let { moveSpeed = 200, jumpSpeed = 200 } = options || {};
4131
- removeSpeed = moveSpeed;
4132
- upSpeed = jumpSpeed;
4246
+ this.removeSpeed = moveSpeed;
4247
+ this.upSpeed = jumpSpeed;
4133
4248
 
4134
- clock = new this.THREE.Clock();
4135
- downRaycaster = new this.THREE.Raycaster(
4249
+ this.clock = new this.THREE.Clock();
4250
+ this.downRaycaster = new this.THREE.Raycaster(
4136
4251
  new this.THREE.Vector3(),
4137
4252
  new this.THREE.Vector3(0, -1, 0),
4138
4253
  0,
4139
4254
  10
4140
4255
  );
4141
- velocity = new this.THREE.Vector3(); //移动速度变量
4142
- direction = new this.THREE.Vector3(); //移动的方向变量
4143
- firstPerSign = true;
4256
+ this.velocity = new this.THREE.Vector3(); //移动速度变量
4257
+ this.direction = new this.THREE.Vector3(); //移动的方向变量
4258
+ this.firstPerSign = true;
4144
4259
  this.initPointerLock();
4145
4260
  },
4146
4261
  // 页面是否锁定
@@ -4148,11 +4263,11 @@ export default {
4148
4263
  this.home();
4149
4264
  setTimeout(() => {
4150
4265
  // 开启第一视角时,将相机与水平面保持水平
4151
- camera.rotation.x = 0;
4152
- camera.rotation.z = 0;
4266
+ this.camera.rotation.x = 0;
4267
+ this.camera.rotation.z = 0;
4153
4268
 
4154
- pointControls = new PointerLockControls(camera, renderer.domElement);
4155
- pointControls.lock();
4269
+ this.pointControls = new PointerLockControls(this.camera, this.renderer.domElement);
4270
+ this.pointControls.lock();
4156
4271
  if (!this._onFirstPersonChange) {
4157
4272
  this._onFirstPersonChange = () => {
4158
4273
  if (typeof this._cameraChangeObserver === 'function') {
@@ -4161,28 +4276,28 @@ export default {
4161
4276
  };
4162
4277
  }
4163
4278
  try {
4164
- pointControls.addEventListener('change', this._onFirstPersonChange);
4279
+ this.pointControls.addEventListener('change', this._onFirstPersonChange);
4165
4280
  } catch (e) {}
4166
4281
  // 锁定
4167
- pointControls.addEventListener('lock', () => {
4168
- cameraControls.enabled = false;
4282
+ this.pointControls.addEventListener('lock', () => {
4283
+ this.cameraControls.enabled = false;
4169
4284
  window.addEventListener('keydown', this.onKeyDown, false);
4170
4285
  window.addEventListener('keyup', this.onKeyUp, false);
4171
- renderer.domElement.removeEventListener('mouseup', this.mouseClick, false);
4172
- renderer.domElement.removeEventListener('mousedown', this.mouseDown, false);
4286
+ this.renderer.domElement.removeEventListener('mouseup', this.mouseClick, false);
4287
+ this.renderer.domElement.removeEventListener('mousedown', this.mouseDown, false);
4173
4288
  if (typeof this._cameraChangeObserver === 'function') {
4174
4289
  this._cameraChangeObserver('firstPersonLock');
4175
4290
  }
4176
4291
  });
4177
4292
  // 解锁
4178
- pointControls.addEventListener('unlock', () => {
4179
- firstPerSign = false;
4180
- cameraControls.enabled = true;
4293
+ this.pointControls.addEventListener('unlock', () => {
4294
+ this.firstPerSign = false;
4295
+ this.cameraControls.enabled = true;
4181
4296
  // 返回初始视角
4182
4297
  this.home();
4183
4298
  try {
4184
- if (this._onFirstPersonChange && pointControls) {
4185
- pointControls.removeEventListener('change', this._onFirstPersonChange);
4299
+ if (this._onFirstPersonChange && this.pointControls) {
4300
+ this.pointControls.removeEventListener('change', this._onFirstPersonChange);
4186
4301
  }
4187
4302
  } catch (e) {}
4188
4303
  if (this.noObserver) {
@@ -4191,57 +4306,61 @@ export default {
4191
4306
  setTimeout(() => {
4192
4307
  window.removeEventListener('keydown', this.onKeyDown);
4193
4308
  window.removeEventListener('keyup', this.onKeyUp);
4194
- renderer.domElement.addEventListener('mouseup', this.mouseClick, false);
4195
- renderer.domElement.addEventListener('mousedown', this.mouseDown, false);
4309
+ this.renderer.domElement.addEventListener('mouseup', this.mouseClick, false);
4310
+ this.renderer.domElement.addEventListener('mousedown', this.mouseDown, false);
4196
4311
  // this.timeRender()
4197
4312
  }, 0);
4198
4313
  if (typeof this._cameraChangeObserver === 'function') {
4199
4314
  this._cameraChangeObserver('firstPersonUnlock');
4200
4315
  }
4201
4316
  });
4202
- if (scene) scene.add(pointControls.object);
4317
+ if (this.scene) this.scene.add(this.pointControls.object);
4203
4318
  }, 10);
4204
4319
  },
4205
4320
  // 第一视角运动
4206
4321
  firstPerspective() {
4207
- if (!cameraControls.enabled && firstPerSign) {
4322
+ if (!this.cameraControls.enabled && this.firstPerSign) {
4208
4323
  // 获取到控制器对象
4209
- let control = pointControls.object;
4324
+ let control = this.pointControls.object;
4210
4325
  // 获取刷新时间
4211
- let delta = clock.getDelta();
4326
+ let delta = this.clock.getDelta();
4212
4327
  // velocity每次的速度,为了保证有过渡
4213
- velocity.x -= velocity.x * 10.0 * delta;
4214
- velocity.z -= velocity.z * 10.0 * delta;
4215
- velocity.y -= 9.8 * 100.0 * delta; // 默认下降的速度
4328
+ this.velocity.x -= this.velocity.x * 10.0 * delta;
4329
+ this.velocity.z -= this.velocity.z * 10.0 * delta;
4330
+ this.velocity.y -= 9.8 * 100.0 * delta; // 默认下降的速度
4216
4331
  // 获取当前按键的方向并获取朝哪个方向移动
4217
- direction.z = Number(moveForward) - Number(moveBackward);
4218
- direction.x = Number(moveRight) - Number(moveLeft);
4332
+ this.direction.z = Number(this.moveForward) - Number(this.moveBackward);
4333
+ this.direction.x = Number(this.moveRight) - Number(this.moveLeft);
4219
4334
  // 将法向量的值归一化
4220
- direction.normalize();
4221
- if (moveForward || moveBackward) velocity.z -= direction.z * removeSpeed * delta;
4222
- if (moveLeft || moveRight) velocity.x -= direction.x * removeSpeed * delta;
4335
+ this.direction.normalize();
4336
+ if (this.moveForward || this.moveBackward)
4337
+ this.velocity.z -= this.direction.z * this.removeSpeed * delta;
4338
+ if (this.moveLeft || this.moveRight)
4339
+ this.velocity.x -= this.direction.x * this.removeSpeed * delta;
4223
4340
  // }
4224
4341
  // 复制相机的位置
4225
- downRaycaster.ray.origin.copy(control.position);
4342
+ this.downRaycaster.ray.origin.copy(control.position);
4226
4343
  // 获取相机靠下5的位置
4227
- downRaycaster.ray.origin.y += 5;
4344
+ this.downRaycaster.ray.origin.y += 5;
4228
4345
  // 判断是否停留在了立方体上面
4229
4346
  let intersections =
4230
- scene && scene.children ? downRaycaster.intersectObjects(scene.children, true) : [];
4347
+ this.scene && this.scene.children
4348
+ ? this.downRaycaster.intersectObjects(this.scene.children, true)
4349
+ : [];
4231
4350
  var onObject = intersections.length > 0;
4232
4351
  if (onObject === true) {
4233
- velocity.y = Math.max(0, velocity.y);
4234
- canJump = true;
4352
+ this.velocity.y = Math.max(0, this.velocity.y);
4353
+ this.canJump = true;
4235
4354
  }
4236
4355
  // 根据速度值移动控制器
4237
- pointControls.moveRight(-velocity.x * delta);
4238
- pointControls.moveForward(-velocity.z * delta);
4239
- control.position.y += velocity.y * delta;
4356
+ this.pointControls.this.moveRight(-this.velocity.x * delta);
4357
+ this.pointControls.this.moveForward(-this.velocity.z * delta);
4358
+ control.position.y += this.velocity.y * delta;
4240
4359
  // 保证控制器的y轴在平面上
4241
4360
  if (control.position.y < 3 - 0 / 10) {
4242
- velocity.y = -0 / 10;
4361
+ this.velocity.y = -0 / 10;
4243
4362
  control.position.y = 3 - 0 / 10;
4244
- canJump = true;
4363
+ this.canJump = true;
4245
4364
  }
4246
4365
  if (this.noObserver) {
4247
4366
  if (!this.noObserver._firstPersonLastPos) {
@@ -4267,27 +4386,27 @@ export default {
4267
4386
  // 前进
4268
4387
  case 38:
4269
4388
  case 87:
4270
- moveForward = true;
4389
+ this.moveForward = true;
4271
4390
  break;
4272
4391
  // 向左
4273
4392
  case 37:
4274
4393
  case 65:
4275
- moveLeft = true;
4394
+ this.moveLeft = true;
4276
4395
  break;
4277
4396
  // 后退
4278
4397
  case 40:
4279
4398
  case 83:
4280
- moveBackward = true;
4399
+ this.moveBackward = true;
4281
4400
  break;
4282
4401
  // 向右
4283
4402
  case 39:
4284
4403
  case 68:
4285
- moveRight = true;
4404
+ this.moveRight = true;
4286
4405
  break;
4287
4406
  // 跳跃
4288
4407
  case 32:
4289
- if (canJump && spaceUp) velocity.y += upSpeed;
4290
- canJump = false;
4408
+ if (this.canJump && spaceUp) this.velocity.y += this.upSpeed;
4409
+ this.canJump = false;
4291
4410
  spaceUp = false;
4292
4411
  break;
4293
4412
  }
@@ -4299,22 +4418,22 @@ export default {
4299
4418
  // 前进
4300
4419
  case 38:
4301
4420
  case 87:
4302
- moveForward = false;
4421
+ this.moveForward = false;
4303
4422
  break;
4304
4423
  // 向左
4305
4424
  case 37:
4306
4425
  case 65:
4307
- moveLeft = false;
4426
+ this.moveLeft = false;
4308
4427
  break;
4309
4428
  // 后退
4310
4429
  case 40:
4311
4430
  case 83:
4312
- moveBackward = false;
4431
+ this.moveBackward = false;
4313
4432
  break;
4314
4433
  // 向右
4315
4434
  case 39:
4316
4435
  case 68:
4317
- moveRight = false;
4436
+ this.moveRight = false;
4318
4437
  break;
4319
4438
  // 跳跃
4320
4439
  case 32:
@@ -4324,14 +4443,14 @@ export default {
4324
4443
  },
4325
4444
  // 返回主视角/恢复相机初始状态
4326
4445
  home() {
4327
- hasExecutedCentering = true;
4446
+ this.hasExecutedCentering = true;
4328
4447
 
4329
- if (roaming) {
4448
+ if (this.roaming) {
4330
4449
  this.endRoam();
4331
4450
  }
4332
4451
 
4333
- const center = sceneBoundingBox.getCenter(new this.THREE.Vector3());
4334
- const size = sceneBoundingBox.getSize(new this.THREE.Vector3());
4452
+ const center = this.sceneBoundingBox.getCenter(new this.THREE.Vector3());
4453
+ const size = this.sceneBoundingBox.getSize(new this.THREE.Vector3());
4335
4454
  const maxDim = Math.max(size.x, size.y, size.z);
4336
4455
 
4337
4456
  this.cameraLocation({
@@ -4343,8 +4462,8 @@ export default {
4343
4462
  roll: center.z,
4344
4463
  });
4345
4464
  this.setCameraConfig();
4346
- // cameraControls.reset(true);
4347
- // cameraControls.update(0);
4465
+ // this.cameraControls.reset(true);
4466
+ // this.cameraControls.update(0);
4348
4467
  // this.timeRender()
4349
4468
  },
4350
4469
  // 测量
@@ -4352,63 +4471,63 @@ export default {
4352
4471
  参数: type: '', distance、area、angle、height, 暂时只提供距离、面积、角度、高度这四种方式
4353
4472
  */
4354
4473
  openMeasure(type) {
4355
- if (threeMeasure) {
4356
- threeMeasure.close();
4357
- threeMeasure = null;
4474
+ if (this.threeMeasure) {
4475
+ this.threeMeasure.close(false);
4476
+ this.threeMeasure = null;
4358
4477
  }
4359
- measureFlag = true;
4478
+ this.measureFlag = true;
4360
4479
  // renderEnabled = true
4361
4480
  switch (type) {
4362
4481
  case 'distance':
4363
- threeMeasure = new MeasureDistance.MeasureDistance(
4364
- renderer,
4365
- scene,
4366
- camera,
4367
- instructions.offsetWidth,
4368
- instructions.offsetHeight
4482
+ this.threeMeasure = new MeasureDistance.MeasureDistance(
4483
+ this.renderer,
4484
+ this.scene,
4485
+ this.camera,
4486
+ this.instructions.offsetWidth,
4487
+ this.instructions.offsetHeight
4369
4488
  );
4370
- threeMeasure.start();
4489
+ this.threeMeasure.start();
4371
4490
  break;
4372
4491
  case 'area':
4373
- threeMeasure = new MeasureArea.MeasureArea(
4374
- renderer,
4375
- scene,
4376
- camera,
4377
- instructions.offsetWidth,
4378
- instructions.offsetHeight
4492
+ this.threeMeasure = new MeasureArea.MeasureArea(
4493
+ this.renderer,
4494
+ this.scene,
4495
+ this.camera,
4496
+ this.instructions.offsetWidth,
4497
+ this.instructions.offsetHeight
4379
4498
  );
4380
- threeMeasure.start();
4499
+ this.threeMeasure.start();
4381
4500
  break;
4382
4501
  case 'angle':
4383
- threeMeasure = new MeasureAngle.MeasureAngle(
4384
- renderer,
4385
- scene,
4386
- camera,
4387
- instructions.offsetWidth,
4388
- instructions.offsetHeight
4502
+ this.threeMeasure = new MeasureAngle.MeasureAngle(
4503
+ this.renderer,
4504
+ this.scene,
4505
+ this.camera,
4506
+ this.instructions.offsetWidth,
4507
+ this.instructions.offsetHeight
4389
4508
  );
4390
- threeMeasure.start();
4509
+ this.threeMeasure.start();
4391
4510
  break;
4392
4511
  case 'height':
4393
- threeMeasure = new MeasureHeight.MeasureHeight(
4394
- renderer,
4395
- scene,
4396
- camera,
4397
- instructions.offsetWidth,
4398
- instructions.offsetHeight
4512
+ this.threeMeasure = new MeasureHeight.MeasureHeight(
4513
+ this.renderer,
4514
+ this.scene,
4515
+ this.camera,
4516
+ this.instructions.offsetWidth,
4517
+ this.instructions.offsetHeight
4399
4518
  );
4400
- threeMeasure.start();
4519
+ this.threeMeasure.start();
4401
4520
  break;
4402
4521
  }
4403
4522
  // 添加键盘事件监听器
4404
4523
  document.addEventListener('keydown', this.handleMeasureKeyDown, false);
4405
4524
  },
4406
- // 关闭测量
4407
- closeMeasure() {
4408
- measureFlag = false;
4409
- if (threeMeasure) {
4410
- threeMeasure.close();
4411
- threeMeasure = null;
4525
+ // 关闭测量 isClear代表是否清除测量结果
4526
+ closeMeasure(isClear) {
4527
+ this.measureFlag = false;
4528
+ if (this.threeMeasure) {
4529
+ this.threeMeasure.close(isClear);
4530
+ !isClear && (this.threeMeasure = null);
4412
4531
  // this.timeRender()
4413
4532
  }
4414
4533
  // 移除键盘事件监听器
@@ -4482,9 +4601,9 @@ export default {
4482
4601
  参数: object, 目标实体,
4483
4602
  */
4484
4603
  isolate(object) {
4485
- if (!scene) return;
4604
+ if (!this.scene) return;
4486
4605
  // 隔离 将目标实体以外的实体隐藏掉
4487
- scene.traverse(item => {
4606
+ this.scene.traverse(item => {
4488
4607
  if (item.isMesh && item.name !== object.name) {
4489
4608
  const offsetMatrix = new this.THREE.Matrix4()
4490
4609
  .copy(item.userData.copyMatrix)
@@ -4496,8 +4615,8 @@ export default {
4496
4615
  },
4497
4616
  // 还原操作 将修改过的实体进行恢复
4498
4617
  restore() {
4499
- if (!scene) return;
4500
- scene.traverse(item => {
4618
+ if (!this.scene) return;
4619
+ this.scene.traverse(item => {
4501
4620
  if (item.isMesh) {
4502
4621
  const instanceId = item.userData ? item.userData.instanceId : null;
4503
4622
  item.setColorAt(item.userData.instanceIndex, item.material.userData.nColor);
@@ -4542,24 +4661,24 @@ export default {
4542
4661
  const loader = new GLTFLoader();
4543
4662
  let locationModel = null;
4544
4663
  loader.load(url, gltf => {
4545
- locationModel = gltf.scene;
4664
+ locationModel = gltf.this.scene;
4546
4665
  locationModel.scale.set(scale, scale, scale);
4547
4666
  locationModel.updateMatrixWorld();
4548
4667
  if (immediately) {
4549
- cameraControls.fitToSphere(gltf.scene, true);
4668
+ this.cameraControls.fitToSphere(gltf.this.scene, true);
4550
4669
  }
4551
4670
  // 动画混合器
4552
4671
  // 不参与裁剪
4553
4672
  locationModel.userData.cull = false;
4554
4673
  locationModel.name = name;
4555
- if (scene) scene.add(locationModel);
4674
+ if (this.scene) this.scene.add(locationModel);
4556
4675
  locationModel.position.copy(new this.THREE.Vector3(position.x, position.y, position.z));
4557
4676
  if (gltf.animations.length > 0) {
4558
- let actionMixer = new this.THREE.AnimationMixer(gltf.scene);
4677
+ let actionMixer = new this.THREE.AnimationMixer(gltf.this.scene);
4559
4678
  const walkActive = actionMixer.clipAction(gltf.animations[0]);
4560
4679
  walkActive.play();
4561
- modelActive.push(walkActive);
4562
- modelActions.push(actionMixer);
4680
+ this.modelActive.push(walkActive);
4681
+ this.modelActions.push(actionMixer);
4563
4682
  }
4564
4683
  callback && callback();
4565
4684
  });
@@ -4570,8 +4689,8 @@ export default {
4570
4689
  obj.forEach((item, index) => {
4571
4690
  if (item.animations > 0) {
4572
4691
  item.removeFromParent();
4573
- modelActions[index].uncacheRoot(item);
4574
- modelActions[index].uncacheRoot(modelActive[index]);
4692
+ this.modelActions[index].uncacheRoot(item);
4693
+ this.modelActions[index].uncacheRoot(this.modelActive[index]);
4575
4694
  }
4576
4695
  item.traverse(child => {
4577
4696
  if (child instanceof this.THREE.Mesh) {
@@ -4579,47 +4698,47 @@ export default {
4579
4698
  child.material.dispose();
4580
4699
  }
4581
4700
  });
4582
- if (scene) scene.remove(item);
4701
+ if (this.scene) this.scene.remove(item);
4583
4702
  });
4584
- modelActions.splice(0);
4585
- modelActive.splice(0);
4703
+ this.modelActions.splice(0);
4704
+ this.modelActive.splice(0);
4586
4705
  },
4587
4706
  // 修改天空背景
4588
4707
  skyBoxScene(url) {
4589
4708
  const textureCube = new this.THREE.CubeTextureLoader().load(url);
4590
- scene.background = textureCube;
4709
+ this.scene.background = textureCube;
4591
4710
  },
4592
4711
  // 清除天空背景
4593
4712
  clearSkyBoxScene() {
4594
- scene.background = null;
4713
+ this.scene.background = null;
4595
4714
  },
4596
4715
  // 下雨模拟
4597
4716
  sceneSimu(type) {
4598
- if (!outlineComposer) {
4599
- outlineComposer = new EffectComposer(renderer, renderTarget);
4600
- outlineComposer.addPass(new RenderPass(scene, camera));
4717
+ if (!this.outlineComposer) {
4718
+ this.outlineComposer = new EffectComposer(this.renderer, this.renderTarget);
4719
+ this.outlineComposer.addPass(new RenderPass(this.scene, this.camera));
4601
4720
  }
4602
4721
  if (type === 'rain') {
4603
- scenePass = new ShaderPass(RainShader);
4604
- scenePass.uniforms['iResolution'].value = new this.THREE.Vector2(
4722
+ this.scenePass = new ShaderPass(RainShader);
4723
+ this.scenePass.uniforms['iResolution'].value = new this.THREE.Vector2(
4605
4724
  window.innerWidth,
4606
4725
  window.innerHeight
4607
4726
  );
4608
- outlineComposer.addPass(scenePass);
4727
+ this.outlineComposer.addPass(this.scenePass);
4609
4728
  } else if (type === 'snow') {
4610
- scenePass = new ShaderPass(SnowShader);
4611
- scenePass.uniforms['iResolution'].value = new this.THREE.Vector2(
4729
+ this.scenePass = new ShaderPass(SnowShader);
4730
+ this.scenePass.uniforms['iResolution'].value = new this.THREE.Vector2(
4612
4731
  window.innerWidth,
4613
4732
  window.innerHeight
4614
4733
  );
4615
- outlineComposer.addPass(scenePass);
4734
+ this.outlineComposer.addPass(this.scenePass);
4616
4735
  }
4617
4736
  },
4618
4737
  clearSceneSim() {
4619
- if (scenePass && outlineComposer) {
4620
- outlineComposer.removePass(scenePass);
4738
+ if (this.scenePass && this.outlineComposer) {
4739
+ this.outlineComposer.removePass(this.scenePass);
4621
4740
  }
4622
- scenePass = null;
4741
+ this.scenePass = null;
4623
4742
  },
4624
4743
  // 获取中心点
4625
4744
  getCenter(obj, isBox3Info) {
@@ -4633,7 +4752,7 @@ export default {
4633
4752
  let center = new this.THREE.Vector3();
4634
4753
  (box3 && box3.getCenter(center)) || obj.boundingBox.getCenter(center);
4635
4754
  if (isBox3Info || obj.userData.is3D) {
4636
- center.applyMatrix4(bizToThreeMatrix);
4755
+ center.applyMatrix4(this.bizToThreeMatrix);
4637
4756
  }
4638
4757
  return center;
4639
4758
  },
@@ -4648,37 +4767,37 @@ export default {
4648
4767
  let size = new this.THREE.Vector3();
4649
4768
  (box3 && box3.getSize(size)) || obj.boundingBox.getSize(size);
4650
4769
  if (isBox3Info || obj.userData.is3D) {
4651
- size.applyMatrix4(bizToThreeMatrix);
4770
+ size.applyMatrix4(this.bizToThreeMatrix);
4652
4771
  }
4653
4772
  return size;
4654
4773
  },
4655
4774
  animate() {
4656
4775
  if (isDebug) {
4657
- stats && stats.begin(); // 开始帧率统计
4776
+ this.stats && this.stats.begin(); // 开始帧率统计
4658
4777
  }
4659
4778
 
4660
- const delta = fpsClock.getDelta();
4661
- timeStamp += delta;
4662
- animateId = requestAnimationFrame(this.animate);
4779
+ const delta = this.fpsClock.getDelta();
4780
+ this.timeStamp += delta;
4781
+ this.animateId = requestAnimationFrame(this.animate);
4663
4782
 
4664
4783
  // 1. 先重置计数器(关键!)
4665
- renderer.info.reset(); // 重置上一帧的统计数据
4784
+ this.renderer.info.reset(); // 重置上一帧的统计数据
4666
4785
 
4667
- if (timeStamp > singleFrameTime) {
4668
- if (modelActions.length > 0) {
4669
- modelActions.forEach(item => {
4670
- item.update(timeStamp);
4786
+ if (this.timeStamp > singleFrameTime) {
4787
+ if (this.modelActions.length > 0) {
4788
+ this.modelActions.forEach(item => {
4789
+ item.update(this.timeStamp);
4671
4790
  });
4672
4791
  }
4673
- cameraControls.enabled && cameraControls.update(timeStamp);
4792
+ this.cameraControls.enabled && this.cameraControls.update(this.timeStamp);
4674
4793
  // 计算相机近裁面到包围盒当前面的距离
4675
4794
  this.cameraTrack();
4676
4795
  this.firstPerspective();
4677
- if (scenePass) {
4678
- let d = sceneClock.getDelta();
4679
- scenePass.uniforms['iTime'].value += d;
4796
+ if (this.scenePass) {
4797
+ let d = this.sceneClock.getDelta();
4798
+ this.scenePass.uniforms['iTime'].value += d;
4680
4799
  }
4681
- labelRenderer.render(scene, camera);
4800
+ this.labelRenderer.render(this.scene, this.camera);
4682
4801
  renderedThisFrame.clear();
4683
4802
 
4684
4803
  // 增强的渲染中断逻辑:在用户交互期间强制跳过渲染
@@ -4686,31 +4805,33 @@ export default {
4686
4805
  ? this.noObserver.batchLoadingState
4687
4806
  : this.batchLoadingState;
4688
4807
  const shouldSkipRendering =
4689
- skipNextRenderFrame || forceSkipRendering || loadingState.interactionState.isInteracting;
4808
+ this.skipNextRenderFrame ||
4809
+ this.forceSkipRendering ||
4810
+ loadingState.interactionState.isInteracting;
4690
4811
 
4691
4812
  if (shouldSkipRendering) {
4692
4813
  // 重置单次跳过标记
4693
- if (skipNextRenderFrame) {
4694
- skipNextRenderFrame = false;
4814
+ if (this.skipNextRenderFrame) {
4815
+ this.skipNextRenderFrame = false;
4695
4816
  }
4696
4817
 
4697
4818
  // 统计跳过的帧数
4698
4819
  // interactionFrameCount++;
4699
4820
  // 在交互期间,每隔几帧强制渲染一次以保持基本的视觉反馈
4700
4821
  // if (interactionFrameCount % 2 === 0 && this.batchLoadingState.interactionState.isInteracting) {
4701
- renderer.clear(true, true, true);
4702
- renderer.render(scene, camera);
4822
+ this.renderer.clear(true, true, true);
4823
+ this.renderer.render(this.scene, this.camera);
4703
4824
  // }
4704
4825
  // 可选:添加调试信息(生产环境可注释掉)
4705
4826
  // console.log(`跳过渲染帧 #${interactionFrameCount}, 交互类型: ${this.batchLoadingState.interactionState.interactionType}`);
4706
4827
  } else {
4707
4828
  // 正常渲染
4708
- if (outlineComposer) {
4829
+ if (this.outlineComposer) {
4709
4830
  // 使用后处理管线渲染,避免重复渲染
4710
- outlineComposer.render();
4831
+ this.outlineComposer.render();
4711
4832
  } else {
4712
- renderer.clear(true, true, true);
4713
- renderer.render(scene, camera);
4833
+ this.renderer.clear(true, true, true);
4834
+ this.renderer.render(this.scene, this.camera);
4714
4835
  }
4715
4836
  // 重置交互帧计数
4716
4837
  // if (interactionFrameCount > 0) {
@@ -4727,15 +4848,15 @@ export default {
4727
4848
  // try {
4728
4849
  // const frustum = new this.THREE.Frustum();
4729
4850
  // const vpMatrix = new this.THREE.Matrix4().multiplyMatrices(
4730
- // camera.projectionMatrix,
4731
- // camera.matrixWorldInverse
4851
+ // this.camera.projectionMatrix,
4852
+ // this.camera.matrixWorldInverse
4732
4853
  // );
4733
4854
  // frustum.setFromProjectionMatrix(vpMatrix);
4734
4855
 
4735
- // // 优先使用 modelGroup 的子节点作为“模型”统计单位;否则回退到 scene.children
4736
- // const children = (typeof modelGroup !== 'undefined' && modelGroup && modelGroup.children && modelGroup.children.length)
4737
- // ? modelGroup.children
4738
- // : scene.children;
4856
+ // // 优先使用 this.modelGroup 的子节点作为“模型”统计单位;否则回退到 this.scene.children
4857
+ // const children = (typeof this.modelGroup !== 'undefined' && this.modelGroup && this.modelGroup.children && this.modelGroup.children.length)
4858
+ // ? this.modelGroup.children
4859
+ // : this.scene.children;
4739
4860
 
4740
4861
  // for (let i = 0; i < children.length; i++) {
4741
4862
  // const obj = children[i];
@@ -4762,29 +4883,29 @@ export default {
4762
4883
  // if ((perfLogFrameCount++ % 120 === 0) || (Date.now() - lastPerfLogTime >= 3000)) {
4763
4884
  // lastPerfLogTime = Date.now();
4764
4885
  // console.log(`frame info`, {
4765
- // drawCalls: renderer.info.render.calls,
4766
- // triangles: renderer.info.render.triangles,
4767
- // textures: renderer.info.memory.textures,
4886
+ // drawCalls: this.renderer.info.render.calls,
4887
+ // triangles: this.renderer.info.render.triangles,
4888
+ // textures: this.renderer.info.memory.textures,
4768
4889
  // });
4769
4890
  // }
4770
4891
  // }
4771
4892
 
4772
- // renderer.setRenderTarget(null)
4773
- timeStamp = timeStamp % singleFrameTime;
4893
+ // this.renderer.setRenderTarget(null)
4894
+ this.timeStamp = this.timeStamp % singleFrameTime;
4774
4895
  }
4775
4896
 
4776
4897
  if (isDebug) {
4777
- stats && stats.end(); // 结束帧率统计
4898
+ this.stats && this.stats.end(); // 结束帧率统计
4778
4899
  }
4779
4900
  },
4780
4901
  // 暴露个别参数让业务自己做特殊业务。
4781
4902
  // 后续若多个业务有相同使用场景,再抽象至公共组件中。
4782
4903
  exportParmas() {
4783
4904
  return {
4784
- renderer,
4785
- camera,
4786
- cameraControls,
4787
- scene,
4905
+ renderer: this.renderer,
4906
+ camera: this.camera,
4907
+ cameraControls: this.cameraControls,
4908
+ scene: this.scene,
4788
4909
  };
4789
4910
  },
4790
4911
  isSourceVisible(instanceId, object) {
@@ -4828,14 +4949,14 @@ export default {
4828
4949
  return true;
4829
4950
  },
4830
4951
  removeOutlineObject(object, instanceIndex) {
4831
- if (!outlinePass || !object) return;
4952
+ if (!this.outlinePass || !object) return;
4832
4953
  if (object.isInstancedMesh) {
4833
4954
  this.removeOutlineInstanceProxy(object, instanceIndex);
4834
4955
  return;
4835
4956
  }
4836
4957
 
4837
- const idx = outlinePass.selectedObjects.indexOf(object);
4838
- if (idx !== -1) outlinePass.selectedObjects.splice(idx, 1);
4958
+ const idx = this.outlinePass.selectedObjects.indexOf(object);
4959
+ if (idx !== -1) this.outlinePass.selectedObjects.splice(idx, 1);
4839
4960
  },
4840
4961
 
4841
4962
  /**
@@ -4853,9 +4974,9 @@ export default {
4853
4974
  };
4854
4975
  const { left, right, middle } = btn;
4855
4976
 
4856
- left && (cameraControls.mouseButtons.left = ACTION_ENUM[left]);
4857
- right && (cameraControls.mouseButtons.right = ACTION_ENUM[right]);
4858
- middle && (cameraControls.mouseButtons.middle = ACTION_ENUM[middle]);
4977
+ left && (this.cameraControls.mouseButtons.left = ACTION_ENUM[left]);
4978
+ right && (this.cameraControls.mouseButtons.right = ACTION_ENUM[right]);
4979
+ middle && (this.cameraControls.mouseButtons.middle = ACTION_ENUM[middle]);
4859
4980
  },
4860
4981
 
4861
4982
  // 分帧加载相关方法
@@ -4899,8 +5020,8 @@ export default {
4899
5020
 
4900
5021
  // 去重处理
4901
5022
  // const existingDrawObjectIds = new Set();
4902
- // if (modelGroup && modelGroup.children && modelGroup.children.length) {
4903
- // modelGroup.children.forEach(child => {
5023
+ // if (this.modelGroup && this.modelGroup.children && this.modelGroup.children.length) {
5024
+ // this.modelGroup.children.forEach(child => {
4904
5025
  // if (child && child.userData && child.userData.isInstancedMeshGroup && child.name) {
4905
5026
  // existingDrawObjectIds.add(child.name);
4906
5027
  // }
@@ -5107,11 +5228,11 @@ export default {
5107
5228
 
5108
5229
  isDebug && performance.mark('handleInstancedMeshModel-start');
5109
5230
  await handleInstancedMeshModel(
5110
- modelGroup,
5231
+ this.modelGroup,
5111
5232
  batch.instances,
5112
5233
  batch.drawObjs,
5113
5234
  '',
5114
- scene,
5235
+ this.scene,
5115
5236
  loadingState.color,
5116
5237
  loadingState.meshNameConfig,
5117
5238
  '',
@@ -5135,31 +5256,31 @@ export default {
5135
5256
  : this.batchLoadingState;
5136
5257
  if (!loadingState.isLoading) return;
5137
5258
 
5138
- // if (modelGroup) {
5259
+ // if (this.modelGroup) {
5139
5260
  // if (firstDraw) {
5140
- // if (scene) scene.add(modelGroup);
5261
+ // if (this.scene) this.scene.add(this.modelGroup);
5141
5262
  // firstDraw = false;
5142
5263
  // }
5143
5264
  // if (!rotatedSceneFlag) {
5144
5265
  // rotatedSceneFlag = true;
5145
5266
  // }
5146
- if (modelGroup) {
5147
- if (!modelGroup.userData.initDone) {
5148
- scene.add(modelGroup);
5149
- modelGroup.applyMatrix4(bizToThreeMatrix);
5150
- modelGroup.updateMatrixWorld();
5151
- this.smartModelCenter(sceneBoundingBox);
5267
+ if (this.modelGroup) {
5268
+ if (!this.modelGroup.userData.initDone) {
5269
+ this.scene.add(this.modelGroup);
5270
+ this.modelGroup.applyMatrix4(this.bizToThreeMatrix);
5271
+ this.modelGroup.updateMatrixWorld();
5272
+ this.smartModelCenter(this.sceneBoundingBox);
5152
5273
  this.setCameraConfig();
5153
- modelGroup.userData.initDone = true;
5274
+ this.modelGroup.userData.initDone = true;
5154
5275
  }
5155
- modelGroup.updateMatrixWorld();
5276
+ this.modelGroup.updateMatrixWorld();
5156
5277
  let modelBox3 = new this.THREE.Box3();
5157
- modelBox3.expandByObject(modelGroup);
5278
+ modelBox3.expandByObject(this.modelGroup);
5158
5279
  let modelWorldPs = new this.THREE.Vector3()
5159
5280
  .addVectors(modelBox3.max, modelBox3.min)
5160
5281
  .multiplyScalar(0.5);
5161
- modelGroup.userData.modelWorldPs = modelWorldPs;
5162
- modelGroup.traverse(child => {
5282
+ this.modelGroup.userData.modelWorldPs = modelWorldPs;
5283
+ this.modelGroup.traverse(child => {
5163
5284
  if (child.isMesh && !child.userData.batchInitDone) {
5164
5285
  markRendered(child);
5165
5286
  const json = this.getMeshCenterAndVolume(child);
@@ -5204,7 +5325,7 @@ export default {
5204
5325
 
5205
5326
  if (isDebug) {
5206
5327
  var axesHelper = new this.THREE.AxesHelper(10000);
5207
- scene.add(axesHelper);
5328
+ this.scene.add(axesHelper);
5208
5329
  }
5209
5330
  // this.showSceneBoundingBox();
5210
5331
  }
@@ -5245,8 +5366,8 @@ export default {
5245
5366
  }
5246
5367
 
5247
5368
  // 如果是第一次调用drawModel且用户正在交互,标记需要在交互结束后居中
5248
- if (modelGroups.length === 0 && userInteracting) {
5249
- needsCenteringAfterInteraction = true;
5369
+ if (this.modelGroups.length === 0 && this.userInteracting) {
5370
+ this.needsCenteringAfterInteraction = true;
5250
5371
  }
5251
5372
 
5252
5373
  // 启动分帧加载
@@ -5256,7 +5377,7 @@ export default {
5256
5377
  /**
5257
5378
  * 统一的中断控制中心 - 设置中断状态
5258
5379
  * @param {boolean} active - 是否激活中断
5259
- * @param {string} reason - 中断原因 ('wheel', 'camera', 'user_interaction' 等)
5380
+ * @param {string} reason - 中断原因 ('wheel', 'this.camera', 'user_interaction' 等)
5260
5381
  * @param {Object} options - 配置项 { immediate: boolean }
5261
5382
  */
5262
5383
  setSystemInterruption(active, reason = 'user_interaction', options = {}) {
@@ -5269,8 +5390,8 @@ export default {
5269
5390
 
5270
5391
  // 1. 更新交互状态标记
5271
5392
  // 如果是相机操作,更新全局标记
5272
- if (reason === 'camera') {
5273
- userInteracting = true;
5393
+ if (reason === 'this.camera') {
5394
+ this.userInteracting = true;
5274
5395
  }
5275
5396
 
5276
5397
  // 更新 batchLoadingState 中的交互状态
@@ -5287,8 +5408,8 @@ export default {
5287
5408
  loadingState.pauseReason = reason;
5288
5409
 
5289
5410
  // 交互期间强制跳过渲染帧,提升响应速度
5290
- forceSkipRendering = true;
5291
- skipNextRenderFrame = true;
5411
+ this.forceSkipRendering = true;
5412
+ this.skipNextRenderFrame = true;
5292
5413
 
5293
5414
  // 3. 清理之前的恢复定时器(防止在交互中途意外恢复)
5294
5415
  if (loadingState.resumeTimer) {
@@ -5314,8 +5435,8 @@ export default {
5314
5435
 
5315
5436
  // 关键修复:立即更新交互状态,不依赖 doResume 的执行时机
5316
5437
  // 这样可以避免当一种交互(如 camera)结束但另一种(如 wheel)仍活跃时,状态无法正确复位的问题
5317
- if (reason === 'camera') {
5318
- userInteracting = false;
5438
+ if (reason === 'this.camera') {
5439
+ this.userInteracting = false;
5319
5440
  }
5320
5441
 
5321
5442
  // 定义恢复执行逻辑
@@ -5324,11 +5445,11 @@ export default {
5324
5445
  // 1. 如果有滚轮定时器未结束,说明还在连续滚动中,不恢复
5325
5446
  if (loadingState.interactionState.wheelTimeout) return;
5326
5447
 
5327
- // 2. 如果是 wheel 结束,但 userInteracting (camera) 还在进行,则不恢复
5328
- if (reason === 'wheel' && userInteracting) return;
5448
+ // 2. 如果是 wheel 结束,但 this.userInteracting (this.camera) 还在进行,则不恢复
5449
+ if (reason === 'wheel' && this.userInteracting) return;
5329
5450
 
5330
5451
  // 3. 检查是否有强制跳过标记 (可能由其他逻辑触发)
5331
- // if (forceSkipRendering && reason !== 'user_interaction') return; // 视情况而定
5452
+ // if (this.forceSkipRendering && reason !== 'user_interaction') return; // 视情况而定
5332
5453
 
5333
5454
  // --- 执行恢复 ---
5334
5455
 
@@ -5338,7 +5459,7 @@ export default {
5338
5459
  loadingState.interactionState.isInteracting = false;
5339
5460
  loadingState.interactionState.interactionType = '';
5340
5461
 
5341
- forceSkipRendering = false;
5462
+ this.forceSkipRendering = false;
5342
5463
 
5343
5464
  // 恢复本地批量加载 (如果未完成)
5344
5465
  if (loadingState.isLoading && loadingState.currentBatch < loadingState.totalBatches) {
@@ -5383,7 +5504,8 @@ export default {
5383
5504
  * 交互开始
5384
5505
  */
5385
5506
  beginInteraction(type = 'user') {
5386
- const reason = type === 'wheel' ? 'wheel' : type === 'camera' ? 'camera' : 'user_interaction';
5507
+ const reason =
5508
+ type === 'wheel' ? 'wheel' : type === 'this.camera' ? 'this.camera' : 'user_interaction';
5387
5509
  this.setSystemInterruption(true, reason);
5388
5510
  },
5389
5511
 
@@ -5440,10 +5562,10 @@ export default {
5440
5562
  * 隐藏场景包围盒
5441
5563
  */
5442
5564
  hideSceneBoundingBox() {
5443
- if (sceneBoundingBoxHelper && scene) {
5444
- scene.remove(sceneBoundingBoxHelper);
5445
- sceneBoundingBoxHelper = null;
5446
- boundingBoxVisible = false;
5565
+ if (this.sceneBoundingBoxHelper && this.scene) {
5566
+ this.scene.remove(this.sceneBoundingBoxHelper);
5567
+ this.sceneBoundingBoxHelper = null;
5568
+ this.boundingBoxVisible = false;
5447
5569
  console.log('场景包围盒已隐藏');
5448
5570
  }
5449
5571
  },
@@ -5452,7 +5574,7 @@ export default {
5452
5574
  * 切换场景包围盒显示状态
5453
5575
  */
5454
5576
  toggleSceneBoundingBox() {
5455
- if (boundingBoxVisible) {
5577
+ if (this.boundingBoxVisible) {
5456
5578
  this.hideSceneBoundingBox();
5457
5579
  } else {
5458
5580
  this.showSceneBoundingBox();
@@ -5462,7 +5584,12 @@ export default {
5462
5584
  };
5463
5585
  </script>
5464
5586
  <style lang="scss" scoped>
5465
- #fl-model {
5587
+ // #fl-model {
5588
+ // width: 100%;
5589
+ // height: 100%;
5590
+ // cursor: pointer;
5591
+ // }
5592
+ .fl-model-containor {
5466
5593
  width: 100%;
5467
5594
  height: 100%;
5468
5595
  cursor: pointer;
@@ -5564,7 +5691,7 @@ export default {
5564
5691
  margin-bottom: 20px;
5565
5692
  }
5566
5693
 
5567
- .loading-progress-bar {
5694
+ .loading-this.progress-bar {
5568
5695
  width: 100%;
5569
5696
  height: 8px;
5570
5697
  background-color: #f0f0f0;
@@ -5573,7 +5700,7 @@ export default {
5573
5700
  margin-bottom: 15px;
5574
5701
  }
5575
5702
 
5576
- .loading-progress-fill {
5703
+ .loading-this.progress-fill {
5577
5704
  height: 100%;
5578
5705
  background: linear-gradient(90deg, #409eff 0%, #67c23a 100%);
5579
5706
  border-radius: 4px;
@@ -5588,7 +5715,7 @@ export default {
5588
5715
  </style>
5589
5716
  <style>
5590
5717
  /* 自定义lil-gui样式 - 浅色主题 */
5591
- .lil-gui {
5718
+ .lil-this.gui {
5592
5719
  background: rgba(255, 255, 255, 0.95) !important;
5593
5720
  border: 1px solid #e0e0e0 !important;
5594
5721
  border-radius: 8px !important;
@@ -5597,7 +5724,7 @@ export default {
5597
5724
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
5598
5725
  }
5599
5726
 
5600
- .lil-gui .title {
5727
+ .lil-this.gui .title {
5601
5728
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important;
5602
5729
  color: #495057 !important;
5603
5730
  border-bottom: 1px solid #dee2e6 !important;
@@ -5606,50 +5733,50 @@ export default {
5606
5733
  border-radius: 8px 8px 0 0 !important;
5607
5734
  }
5608
5735
 
5609
- .lil-gui .controller {
5736
+ .lil-this.gui .controller {
5610
5737
  border-bottom: 1px solid #f1f3f4 !important;
5611
5738
  background: transparent !important;
5612
5739
  }
5613
5740
 
5614
- .lil-gui .controller:last-child {
5741
+ .lil-this.gui .controller:last-child {
5615
5742
  border-bottom: none !important;
5616
5743
  }
5617
5744
 
5618
- .lil-gui .controller .name {
5745
+ .lil-this.gui .controller .name {
5619
5746
  color: #495057 !important;
5620
5747
  font-weight: 500 !important;
5621
5748
  font-size: 12px !important;
5622
5749
  }
5623
5750
 
5624
- .lil-gui .controller .widget {
5751
+ .lil-this.gui .controller .widget {
5625
5752
  background: #f8f9fa !important;
5626
5753
  border: 1px solid #ced4da !important;
5627
5754
  border-radius: 4px !important;
5628
5755
  color: #495057 !important;
5629
5756
  }
5630
5757
 
5631
- .lil-gui .controller .widget:hover {
5758
+ .lil-this.gui .controller .widget:hover {
5632
5759
  border-color: #80bdff !important;
5633
5760
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25) !important;
5634
5761
  }
5635
5762
 
5636
- .lil-gui .controller .widget:focus {
5763
+ .lil-this.gui .controller .widget:focus {
5637
5764
  border-color: #80bdff !important;
5638
5765
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25) !important;
5639
5766
  outline: none !important;
5640
5767
  }
5641
5768
 
5642
- .lil-gui .controller input[type='range'] {
5769
+ .lil-this.gui .controller input[type='range'] {
5643
5770
  background: #e9ecef !important;
5644
5771
  height: 4px !important;
5645
5772
  -webkit-appearance: none !important;
5646
5773
  appearance: none !important;
5647
5774
  border-radius: 2px !important;
5648
5775
  }
5649
- .lil-gui .controller.number .fill {
5776
+ .lil-this.gui .controller.number .fill {
5650
5777
  border-right: solid #008de9;
5651
5778
  }
5652
- .lil-gui .controller input[type='range']::-webkit-slider-thumb {
5779
+ .lil-this.gui .controller input[type='range']::-webkit-slider-thumb {
5653
5780
  background: #007bff !important;
5654
5781
  border: 2px solid #ffffff !important;
5655
5782
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) !important;
@@ -5661,7 +5788,7 @@ export default {
5661
5788
  cursor: pointer !important;
5662
5789
  }
5663
5790
 
5664
- .lil-gui .controller input[type='range']::-moz-range-thumb {
5791
+ .lil-this.gui .controller input[type='range']::-moz-range-thumb {
5665
5792
  background: #007bff !important;
5666
5793
  border: 2px solid #ffffff !important;
5667
5794
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) !important;
@@ -5671,23 +5798,23 @@ export default {
5671
5798
  cursor: pointer !important;
5672
5799
  }
5673
5800
 
5674
- .lil-gui .controller .option {
5801
+ .lil-this.gui .controller .option {
5675
5802
  background: #ffffff !important;
5676
5803
  color: #495057 !important;
5677
5804
  border-bottom: 1px solid #f1f3f4 !important;
5678
5805
  }
5679
5806
 
5680
- .lil-gui .controller .option:hover {
5807
+ .lil-this.gui .controller .option:hover {
5681
5808
  background: #f8f9fa !important;
5682
5809
  }
5683
5810
 
5684
- .lil-gui .controller .option:last-child {
5811
+ .lil-this.gui .controller .option:last-child {
5685
5812
  border-bottom: none !important;
5686
5813
  }
5687
- .lil-gui input:active {
5814
+ .lil-this.gui input:active {
5688
5815
  background: #e6eff4;
5689
5816
  }
5690
- .lil-gui .controller button {
5817
+ .lil-this.gui .controller button {
5691
5818
  background: linear-gradient(135deg, #007bff 0%, #0056b3 100%) !important;
5692
5819
  color: #ffffff !important;
5693
5820
  border: none !important;
@@ -5696,51 +5823,51 @@ export default {
5696
5823
  transition: all 0.2s ease !important;
5697
5824
  }
5698
5825
 
5699
- .lil-gui .controller button:hover {
5826
+ .lil-this.gui .controller button:hover {
5700
5827
  background: linear-gradient(135deg, #0056b3 0%, #004085 100%) !important;
5701
5828
  transform: translateY(-1px) !important;
5702
5829
  box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3) !important;
5703
5830
  }
5704
5831
 
5705
- .lil-gui .controller .color {
5832
+ .lil-this.gui .controller .color {
5706
5833
  border: 2px solid #ffffff !important;
5707
5834
  border-radius: 4px !important;
5708
5835
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
5709
5836
  }
5710
- .lil-gui .controller.number .slider {
5837
+ .lil-this.gui .controller.number .slider {
5711
5838
  background-color: #e6eff4;
5712
5839
  }
5713
- .lil-gui .controller.number .slider:hover {
5840
+ .lil-this.gui .controller.number .slider:hover {
5714
5841
  background-color: #e6eff4;
5715
5842
  }
5716
- .lil-gui input:hover {
5843
+ .lil-this.gui input:hover {
5717
5844
  background: #e6eff4;
5718
5845
  }
5719
- .lil-gui input[type='number']:focus,
5720
- .lil-gui input[type='text']:focus,
5721
- .lil-gui input {
5846
+ .lil-this.gui input[type='number']:focus,
5847
+ .lil-this.gui input[type='text']:focus,
5848
+ .lil-this.gui input {
5722
5849
  background: #e6eff4;
5723
5850
  }
5724
- .lil-gui .controller > .name {
5851
+ .lil-this.gui .controller > .name {
5725
5852
  min-width: 25px;
5726
5853
  }
5727
- .lil-gui .controller.number input {
5854
+ .lil-this.gui .controller.number input {
5728
5855
  color: #2e3136;
5729
5856
  }
5730
- .lil-gui .controller.number .slider:active {
5857
+ .lil-this.gui .controller.number .slider:active {
5731
5858
  background-color: #e6eff4;
5732
5859
  }
5733
- .lil-gui .folder > .title {
5860
+ .lil-this.gui .folder > .title {
5734
5861
  background: linear-gradient(135deg, #f1f3f4 0%, #e9ecef 100%) !important;
5735
5862
  color: #495057 !important;
5736
5863
  border-bottom: 1px solid #dee2e6 !important;
5737
5864
  }
5738
5865
 
5739
- .lil-gui .folder > .title:before {
5866
+ .lil-this.gui .folder > .title:before {
5740
5867
  color: #6c757d !important;
5741
5868
  }
5742
5869
 
5743
- .lil-gui .folder.closed > .children {
5870
+ .lil-this.gui .folder.closed > .children {
5744
5871
  display: none !important;
5745
5872
  }
5746
5873
  </style>