// 从这里开始 var h1 = null var h2 = null var h3 = null var oldtransformNodex = null var oldMeshOther = null var drag = null var isRotating = true // 控制整体旋转状态:true=自动旋转,false=停止 var clickParentRotate = false // 仅标记「手动暂停」状态,不干扰鼠标划出逻辑 // 新增:标记鼠标是否悬停在模型上(核心控制旋转恢复的开关) var isPointerOverModel = false var targetAngles = { default: { // 初始视角 alpha: 2.5809, beta: 0.9804, radius: 3 }, alarm: { // 报警视角(示例) alpha: 3.1213, beta: 0.5855, radius: 3 } } var camera BABYLON.DefaultLoadingScreen.prototype.displayLoadingUI = function() { if (document.getElementById('customLoadingScreenDiv')) { document.getElementById('customLoadingScreenDiv').style.display = 'initial' return } this._loadingDiv = document.createElement('div') this._loadingDiv.id = 'customLoadingScreenDiv' thecss = 'lohingifpc' thecss2 = 'zcpc' this._loadingDiv.innerHTML = "
" this._resizeLoadingUI() window.addEventListener('resize', this._resizeLoadingUI) document.body.appendChild(this._loadingDiv) } // 重构通用的模型交互绑定函数(核心优化鼠标划入/划出逻辑) function bindModelPointerEvents(transformNodex, clickCallback, highlightColor) { var mesheses = null if (transformNodex.getClassName() === 'TransformNode') { mesheses = transformNodex.getChildMeshes(false) } else { mesheses = [transformNodex] } for (var i = 0; i < mesheses.length; i++) { const mesh = mesheses[i] mesh.actionManager = new BABYLON.ActionManager(scene) // 鼠标划入:停止旋转(无论之前是否在旋转) mesh.actionManager.registerAction( new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOverTrigger, function() { isPointerOverModel = true isRotating = false // 标记为停止状态 camera.useAutoRotationBehavior = false // 立即停止旋转 // 高亮逻辑(如果需要) if (highlightColor && h2) { h2.addMesh(mesh, highlightColor) } }) ) // 鼠标划出:无条件恢复旋转(核心修改!只要移出,就恢复,除非手动暂停) mesh.actionManager.registerAction( new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOutTrigger, function() { isPointerOverModel = false // 关键:只要不是「手动暂停」,就恢复旋转(没有其他额外条件) if (!clickParentRotate) { isRotating = true camera.useAutoRotationBehavior = true // 立即恢复旋转 } // 移除高亮(如果需要) if (highlightColor && h2) { h2.removeMesh(mesh) } }) ) // 点击事件(不影响旋转逻辑) mesh.actionManager.registerAction( new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, function() { try { clickCallback && clickCallback(transformNodex) } catch (error) { console.warn('点击回调执行失败:', error) } }) ) } } // 原prepareGroupButton2改为调用通用函数 function prepareGroupButton2(transformNodex, color, qu) { bindModelPointerEvents(transformNodex, cameraClick, color || new BABYLON.Color3(0, 0, 1)) } // 原prepareGroupButton3改为调用通用函数 function prepareGroupButton3(transformNodex, color, qu) { bindModelPointerEvents(transformNodex, archCabinetsClick, color || new BABYLON.Color3(0, 1, 0)) } BABYLON.DefaultLoadingScreen.prototype.hideLoadingUI = function() { show = 50 document.getElementById('customLoadingScreenDiv').style.display = 'none' document.getElementById('customLoadingScreenDiv_first').style.display = 'none' document.title = '档案室' var prepareGroupButton2ByMesh = function(transformNodex, color, qu) { var mesheses = null if (transformNodex.getClassName() === 'Mesh') { mesheses = [transformNodex] } for (var i = 0; i < mesheses.length; i++) { const mesh = mesheses[i] mesh.actionManager = new BABYLON.ActionManager(scene) mesh.actionManager.registerAction( new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOverTrigger, function() { isPointerOverModel = true isRotating = false camera.useAutoRotationBehavior = false if (oldMeshOther != null) { oldMeshOther.removeBehavior(drag) var mesheses2 = oldMeshOther.getClassName() === 'Mesh' ? [oldMeshOther] : [] mesheses2.forEach(m => { try { h2.removeMesh(m) } catch (e) {} }) } oldMeshOther = transformNodex var mesheses3 = transformNodex.getClassName() === 'Mesh' ? [transformNodex] : [] mesheses3.forEach(m => { try { h2.addMesh(m, color) } catch (e) {} }) }) ) // 同样优化这个函数里的鼠标划出逻辑 mesh.actionManager.registerAction( new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPointerOutTrigger, function() { isPointerOverModel = false if (!clickParentRotate) { // 仅排除「手动暂停」的情况 isRotating = true camera.useAutoRotationBehavior = true } var mesheses4 = transformNodex.getClassName() === 'Mesh' ? [transformNodex] : [] mesheses4.forEach(m => { try { h2.removeMesh(m) } catch (e) {} }) }) ) mesh.actionManager.registerAction( new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, function() { try { cameraClick(transformNodex) } catch (error) { console.warn('点击回调执行失败:', error) } }) ) } } // 绑功能点(烟感) var smokeAlarms = [] scene.transformNodes.forEach(function(node) { if (node.name.startsWith('smokealarm')) { smokeAlarms.push(node) } }) var length = smokeAlarms.length console.log('以 smokealarm 开头的对象数量:', length) smokeAlarms.forEach(function(jk01, index) { console.log('smokeAlarms', jk01) var parts = jk01.name.split('smokealarm') jk01.nameID = parts.length > 1 ? `S${String(index + 1).padStart(3, '0')}` : 'S000' jk01.baojing = false prepareGroupButton2(jk01) }) // 绑监控设备 const cameraMeshes = [] scene.transformNodes.forEach(function(node) { if (node.name.startsWith('有害生物去除器')) { cameraMeshes.push(node) } }) const cameraCount = cameraMeshes.length console.log('以 有害生物去除器 开头的 Mesh 数量:', cameraCount) const colorBlue = new BABYLON.Color3(0, 0, 1) cameraMeshes.forEach((cam, index) => { cam.nameID = `cam_${String(index + 1).padStart(3, '0')}` prepareGroupButton2(cam, colorBlue) }) // 绑档案柜 const archivesCabinets = []; scene.transformNodes.forEach(function(node) { if (node.name.startsWith('档案柜')) { archivesCabinets.push(node); } }); const archivesCabinetsCount = archivesCabinets.length; console.log('档案柜数量:', archivesCabinetsCount); if (archivesCabinets.length > 0) { const children = archivesCabinets[0]._children; const filteredChildren = children.filter(cam => cam.name !== "克隆_1"); for (let i = filteredChildren.length - 1; i >= 0; i--) { const cam = filteredChildren[i]; const paddedIndex = String(filteredChildren.length - i); cam.nameID = `cabinet-${paddedIndex}`; prepareGroupButton3(cam); } } var light = new BABYLON.HemisphericLight('HemiLight', new BABYLON.Vector3(0, 1, 0), scene) light.intensity = 2 light.diffuse = new BABYLON.Color3(0.92, 0.92, 0.92) // 给vue页面传值,加载完成之后 parent.getIframeLoading('false') } BABYLON.DracoCompression.Configuration.decoder.wasmUrl = './js/draco_wasm_wrapper_gltf.js' BABYLON.DracoCompression.Configuration.decoder.wasmBinaryUrl = './js/draco_decoder_gltf.wasm' BABYLON.DracoCompression.Configuration.decoder.fallbackUrl = './js/draco_decoder_gltf.js' // 3D模型页面(web3D/index.html)的createScene函数后添加适配逻辑 var canvas = document.getElementById('renderCanvas') var engine = new BABYLON.Engine(canvas, true, { stencil: true }) // ========== 新增:画布自适应iframe尺寸 ========== function resizeCanvas() { // 获取iframe的容器尺寸(如果iframe有父容器,也可以取父容器尺寸) const iframeWidth = window.innerWidth || document.documentElement.clientWidth const iframeHeight = window.innerHeight || document.documentElement.clientHeight // 设置canvas尺寸为iframe的100%(保证填满iframe,且跟随iframe缩放) canvas.width = iframeWidth canvas.height = iframeHeight // 通知引擎更新尺寸 engine.resize() } // 初始化时执行一次 resizeCanvas() // 监听窗口大小变化(iframe缩放时触发) window.addEventListener('resize', resizeCanvas) // createScene function that creates and return the scene var createScene = function() { engine.displayLoadingUI() var scene = new BABYLON.Scene(engine) camera = new BABYLON.ArcRotateCamera( 'Camera', 4.02, 0.98, 3.4, new BABYLON.Vector3(1.2, 0.08, 0.03), scene ) camera.setTarget(new BABYLON.Vector3(0, 0, 0)) camera.attachControl(canvas, true) // 相机参数(保持原优化) camera.lowerRadiusLimit = 0.1 camera.upperRadiusLimit = 500 camera.inertia = 0.1 camera.minZ = 0.01 camera.maxZ = 1000 camera.fov = 1.0 camera.useAutoRotationBehavior = isRotating // 初始启用自动旋转 // 透明背景 scene.clearColor = new BABYLON.Color4(0, 0, 0, 0) scene.activeCamera = camera scene.activeCamera.useInputToRestoreState = true var assetsManager = new BABYLON.AssetsManager(scene) // 加载GLB模型 BABYLON.SceneLoader.LoadAssetContainerAsync('asset/', 'F5.glb', scene).then((container) => { container.addAllToScene() }).catch((error) => { console.error('加载模型时出错:', error) }) var hdrTexture = new BABYLON.CubeTexture.CreateFromPrefilteredData('textures/environmentSpecular.env', scene) scene.environmentTexture = hdrTexture var spriteManagerPlayer = new BABYLON.SpriteManager('playerManager', './img/bl9.png', 10, { width: 100, height: 100 }, scene) spriteManagerPlayer.isPickable = true scene.onPointerDown = function(evt) { var pickResult = scene.pickSprite(this.pointerX, this.pointerY) var pick = scene.pick(scene.pointerX, scene.pointerY) console.log('pick', pick.pickedPoint) if (pickResult.pickedSprite != null && pickResult.hit) { alert(pickResult.pickedSprite.name) } } assetsManager.load() drag = new BABYLON.PointerDragBehavior({ dragPlaneNormal: new BABYLON.Vector3(0, 0, 1) }) drag.validateDrag = (targetPosition) => { if (targetPosition.x > 10.5 || targetPosition.x < -10.5) return false if (targetPosition.z > 10.5 || targetPosition.z < -10.5) return false return oldtransformNodex != null } // GUI高亮层 h1 = new BABYLON.HighlightLayer('hl1', scene) h2 = new BABYLON.HighlightLayer('hl2', scene) h3 = new BABYLON.HighlightLayer('hl3', scene) var step = 0.1 var currentx = 1 h1.blurHorizontalSize = 0.1 scene.registerAfterRender(() => { h1.blurHorizontalSize = h1.blurVerticalSize + currentx // 报警高亮逻辑 var nodealert = scene.getTransformNodeByName('yan') if (nodealert != null) { var mesheses3 = nodealert.getClassName() === 'TransformNode' ? nodealert.getChildMeshes(false) : [nodealert] mesheses3.forEach(mesh => { try { if (nodealert.baojing === true) { h3.addMesh(mesh, new BABYLON.Color3(1, 0, 0)) } else { h3.removeMesh(mesh) } } catch (error) {} }) } // 修复高亮动画速度控制 if (currentx > 0.5) step *= -1 if (currentx < 0) step *= -1 currentx += step }) return scene } var canvas = document.getElementById('renderCanvas') var engine = new BABYLON.Engine(canvas, true, { stencil: true }) var scene = createScene() scene.autoClear = true scene.imageProcessingConfiguration.exposure = 1 scene.imageProcessingConfiguration.contrast = 1 scene.environmentIntensity = 0.4 engine.runRenderLoop(function() { scene.render() }) window.addEventListener('resize', function() { engine.resize() }) // 设备点击回调 function cameraClick(TheCamera) { window.parent.postMessage({ type: 'cameraClick', data: TheCamera.nameID }, '*') } function archCabinetsClick(TheCamera) { window.parent.postMessage({ type: 'archCabinetsClick', data: TheCamera.nameID }, '*') } // 报警状态处理 function handleAlarm(deviceId, isAlarm) { var cl = new BABYLON.Color3(0, 0, 1) var c2 = new BABYLON.Color3(1, 0, 0) const targetDevice = scene.transformNodes.find(node => node.nameID === deviceId) if (targetDevice) { targetDevice.baojing = isAlarm var mesheses = targetDevice.getClassName() === 'TransformNode' ? targetDevice.getChildMeshes(false) : [targetDevice] mesheses.forEach(mesh => { if (isAlarm) { h2.addMesh(mesh, c2) } else { h2.removeMesh(mesh) h2.addMesh(mesh, cl) } }) } else { console.log(`未找到设备 ID 为 ${deviceId} 的设备`) } } // 接收父页面消息(手动控制旋转) window.addEventListener('message', function(e) { if (e.data.type === 'deviceState') { console.log('ddd', e.data.data) const deviceData = e.data.data if (Array.isArray(deviceData)) { deviceData.forEach((device) => { handleAlarm(device.deviceId, device.isAlarm) }) } else { handleAlarm(deviceData.deviceId, deviceData.isAlarm) } } else if (e.data.type === 'isGetRotate') { const isGetRotate = e.data.value console.log('isGetRotate:', isGetRotate) toggleAutoRotation(isGetRotate) } }, false) // 手动控制旋转开关(仅影响「手动暂停」状态) function toggleAutoRotation(isAutoRotating) { clickParentRotate = isAutoRotating // 标记:true=手动暂停,false=手动启用 if (isAutoRotating) { // 手动暂停:无论鼠标是否在模型上,都停止旋转 isRotating = false camera.useAutoRotationBehavior = false camera.angularSpeed = 0 } else { // 手动启用:如果鼠标不在模型上,立即恢复旋转 if (!isPointerOverModel) { isRotating = true camera.useAutoRotationBehavior = true } } } // 相机动画跳转 function moveCameraTo(targetPreset) { var target = targetAngles[targetPreset] if (!target) return console.error('无效的视角预设') var camera = scene.activeCamera camera.panningInertia = 0 var animationAlpha = new BABYLON.Animation( 'cameraAlphaAnimation', 'alpha', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT ) var animationBeta = new BABYLON.Animation( 'cameraBetaAnimation', 'beta', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT ) var animationRadius = new BABYLON.Animation( 'cameraRadiusAnimation', 'radius', 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT ) var keysAlpha = [ { frame: 0, value: camera.alpha }, { frame: 120, value: target.alpha } ] var keysBeta = [ { frame: 0, value: camera.beta }, { frame: 120, value: target.beta } ] var keysRadius = [ { frame: 0, value: camera.radius }, { frame: 120, value: target.radius } ] animationAlpha.setKeys(keysAlpha) animationBeta.setKeys(keysBeta) animationRadius.setKeys(keysRadius) var easingFunction = new BABYLON.CubicEase() easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT) animationAlpha.setEasingFunction(easingFunction) animationBeta.setEasingFunction(easingFunction) animationRadius.setEasingFunction(easingFunction) scene.beginDirectAnimation(camera, [animationAlpha, animationBeta, animationRadius], 0, 120, false, 1, function() { camera.panningInertia = 0.1 toggleAutoRotation(false) // 动画结束后,恢复自动旋转(除非手动暂停) window.parent.postMessage({ type: 'autoRotationStatus', value: true }, '*') }) }