javascript
html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
overflow: hidden;
margin: 0px;
}
.bu {
background: rgba(0, 0, 0, 0.3);
width: 60px;
height: 60px;
line-height: 60px;
text-align: center;
color: #fff;
display: inline-block;
border-radius: 30px;
}
.bu:hover {
cursor: pointer;
}
.pos {
position: absolute;
left: 50%;
margin-left: -135px;
bottom: 100px;
}
#close:hover {
cursor: pointer;
}
</style>
</head>
<!-- 具体路径配置,你根据自己文件目录设置,我的是课件中源码形式 -->
<body>
<!-- backface-visibility: hidden;标签对应的HTML元素背面显示 -->
<div id="tag" style="display: none;">
<!-- position:relative;约束子元素绝对定位参照点 -->
<div style="position:relative;width:400px;height:322px;color: #fff;">
<!-- 图片绝对定位100%填充父元素,作为标签的背景 -->
<img src="./model/信息背景.png" alt="" style="width:100%;position: absolute;left: 0px;top: 0px;">
<!-- 名称、存储量、设备状态、等信息叠加到背景图上即可 -->
<div style="position:absolute;left:48px;top:36px;font-size:16px;">
<div style="font-size:20px;font-weight: 400;">
<span id="name">设备A</span>
</div>
<div style="margin-top: 30px;">
<span style="font-weight: 400;margin-left: 80px;font-size: 40px;color: #00ffff;">276559 L</span>
</div>
<div style="margin-top: 20px;">
<span style="color: #ccc;font-weight: 300;">管理</span><span
style="font-weight: 400;margin-left: 30px;">快乐的小青蛙</span>
</div>
<div style="margin-top: 10px;">
<span style="color: #ccc;font-weight: 300;">工号</span><span
style="font-weight: 400;margin-left: 30px;">666</span>
</div>
</div>
<div style="position:absolute;left:285px;top:35px;">
<span style="color: #ffff00;">异常</span>
</div>
<div style="position:absolute;left:350px;top:20px;">
<img id="close" src="./model/关闭.png" width="32" style="pointer-events: auto;">
</div>
</div>
</div>
<div id="webgl"></div>
<script type="importmap">
{
"imports": {
"three": "../../../three.js/build/three.module.js",
"three/addons/": "../../../three.js/examples/jsm/",
"@tweenjs/tween.js":"./tween.esm.js"
}
}
</script>
<!-- 配置type="importmap",.html文件也能和项目开发环境一样方式引入threejs -->
<script type="module" src="./04.工厂设备点击放大动画、标签动画.js">
</script>
</body>
</html>
04.工厂设备点击放大动画、标签动画.js
javascript
import gui from './gui.js'
import tag from "./tag.js"
import * as THREE from 'three'
import { OrbitControls } from "three/addons/controls/OrbitControls.js"
// 引入stats性能监视器
import Stats from "three/addons/libs/stats.module.js"
// tween.js安装 npm i @tweenjs/tween.js@^18
import TWEEN from "@tweenjs/tween.js"
// 引入CSS2渲染器CSS2DRenderer
import { CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js';
// 1.引入后处理扩展库
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js"
// 2.引入渲染器通道
import { RenderPass } from "three/addons/postprocessing/RenderPass.js"
// 3.引入OutlinePass通道
import { OutlinePass } from "three/addons/postprocessing/OutlinePass.js"
// 伽马矫正后处理shader
import { GammaCorrectionShader } from "three/addons/shaders/GammaCorrectionShader.js"
// ShaderPass功能:使用后处理Shader创建后处理通道
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js"
import { model } from "./mesh-modelDemo04.js"
const stats = new Stats()
stats.setMode(1)//修改渲染模式,默认为0
document.getElementById("webgl").appendChild(stats.domElement);
// 创建一个三维场景
const scene = new THREE.Scene();
scene.add(model)
// 坐标格辅助对象. 坐标格实际上是2维线数组.
// const gridHelper = new THREE.GridHelper(600, 50, 0x00ffffff);
// scene.add(gridHelper);
// gridHelper.position.y = -1;
// 创建辅助观察三维坐标
const axesHelper = new THREE.AxesHelper(100)
scene.add(axesHelper)//坐标轴对象添加到三维场景中
// axesHelper.position.set(113, 33, 0)
// 创建点光源
// const pointLight = new THREE.PointLight(0xffffff, 1.0);
// pointLight.decay = 0.0//不随距离的改变而衰减
// pointLight.position.set(400, 200, 300)//设置光源位置
// scene.add(pointLight)//点光源添加到场景中
// 光源辅助观察
// const pointLightHelper = new THREE.PointLightHelper(pointLight, 10)
// scene.add(pointLightHelper)
// 添加一个环境光源
const ambient = new THREE.AmbientLight(0xffffff, 0.9)
scene.add(ambient)
console.log('光照强度', ambient.intensity);
// 添加一个平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 2)
directionalLight.position.set(100, 60, 100);
// directionalLight.target = mesh//默认坐标原点
scene.add(directionalLight)
// 可视化平行光
const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 10, 0xff0000)
scene.add(dirLightHelper)
const ambientFolder = gui.addFolder('环境光')
ambientFolder.close()
// 环境光强度
ambientFolder.add(ambient, 'intensity', 0, 2).name('环境光.intensity')
const dirFolder = gui.addFolder('平行光')
// dirFolder.close()
// 平行光强度
dirFolder.add(directionalLight, 'intensity', 0, 4)
// const dirFolder2 = dirFolder.addFolder('位置')
// dirFolder2.close()
const width = window.innerWidth
const height = window.innerHeight
// 创建一个透视投影相机对象
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
// 设置相机位置
camera.position.set(292, 223, 185);
// 相机视线观察目标点坐标
camera.lookAt(0, 0, 0)//坐标原点
// const R = 100
// new TWEEN.Tween({ angle: 0 })
// .to({ angle: Math.PI * 2 }, 16000)
// .onUpdate(function (obj) {
// camera.position.x = R * Math.cos(obj.angle);
// camera.position.z = R * Math.sin(obj.angle);
// camera.lookAt(0, 0, 0)//坐标原点
// })
// .start()
// 渲染器
// 创建一个WebGL渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true,//锯齿模糊
logarithmicDepthBuffer: true,//设置对数深度缓冲区,优化深度冲突问题
// preserveDrawingBuffer: true,//想要把cavas画布上内容下载到本地,需要设置为true
});
renderer.setSize(width, height);//canvas画布宽高度
renderer.shadowMap.enabled = true;//设置渲染器,允许光源阴影渲染
renderer.shadowMap.type = THREE.VSMShadowMap;//阴影条纹问题解决
// renderer.setClearColor(0x00ffff)
// 告诉thressjs你的屏幕的设备像素比
renderer.setPixelRatio(window.devicePixelRatio);//pixel设备像素比
console.log('查看当前屏幕设备像素比', window.devicePixelRatio);
document.getElementById("webgl").appendChild(renderer.domElement);
// 1.创建后处理对象EffectComposer,WebGL渲染器作为参数
const composer = new EffectComposer(renderer)
// 2.创建一个渲染器通道,场景和相机作为参数
const renderPass = new RenderPass(scene, camera)
composer.addPass(renderPass)
//3. OutlinePass第一个参数V2的尺寸和cavas画布尺寸保持一致
const v2 = new THREE.Vector2(window.innerWidth, window.innerHeight);
const outlinePass = new OutlinePass(v2, scene, camera)
// outlinePass.selectedObjects = [model.children[0]]
composer.addPass(outlinePass)
// 模型描边颜色,默认是白色
outlinePass.visibleEdgeColor.set(0x00ffff)
// 高亮发光厚度
outlinePass.edgeThickness = 4
// 高亮描边发光强度
outlinePass.edgeStrength = 6
// 模型闪烁频率控制
outlinePass.pulsePeriod = 2
// 创建伽马校正通道(设置EffectComposer)
const gammaPass = new ShaderPass(GammaCorrectionShader)
composer.addPass(gammaPass)
// 创建一个css2d渲染器
const css2DRenderer = new CSS2DRenderer();
css2DRenderer.setSize(width, height);
css2DRenderer.domElement.style.position = "absolute";
css2DRenderer.domElement.style.top = "0px";
document.body.appendChild(css2DRenderer.domElement);
css2DRenderer.domElement.style.pointerEvents = "none";
renderer.domElement.style.zIndex = -1
css2DRenderer.domElement.style.zIndex = 1
function render() {
TWEEN.update()
composer.render();
css2DRenderer.render(scene, camera);
stats.update()
// TWEEN.update()
// renderer.render(scene, camera);//周期性执行相机的渲染功能,更新canvas画布上的内容
requestAnimationFrame(render)
}
render()
// 创建一个相机控件对象
const controls = new OrbitControls(camera, renderer.domElement);
controls.update()
controls.addEventListener('change', function () {
renderer.render(scene, camera)//执行一个渲染操作,类比相机拍照咔
})
// onresize事件会在窗口被调整大小时发生
window.onresize = function () {
const width = window.innerWidth
const height = window.innerHeight
css3DRenderer.setSize(width, height);
camera.aspect = width / height;//全屏情况下:设置观察范围长宽比aspect为窗口宽高比
camera.updateProjectionMatrix();//相机属性发生变化,需要执行updateProjectionMatrix()方法更新
renderer.setSize(width, height);//重置渲染器输出画布canvas尺寸
renderer.render(scene, camera);
}
// 声明存储选中的模型
let chooseObj = null
const span = document.getElementById('name')
const cameraPos0 = new THREE.Vector3(292, 223, 185)
const target0 = new THREE.Vector3(0, 0, 0);
addEventListener('click', function (event) {
const px = event.offsetX
const py = event.offsetY
// 屏幕坐标px、py转换为三维坐标x、y
// width、height表示canvas画布的宽高
const x = (px / width) * 2 - 1
const y = -(py / height) * 2 + 1
console.log('x', x)
console.log('y', y)
// 创建一个射线投射器
const raycaster = new THREE.Raycaster()
raycaster.setFromCamera(new THREE.Vector2(x, y), camera)
const cunchu = model.getObjectByName('存储罐')
console.log(cunchu);
for (let i = 0; i < cunchu.children.length; i++) {
const group = cunchu.children[i]
group.traverse(function (obj) {
if (obj.isMesh) {
obj.ancestors = group
}
})
}
const intersects = raycaster.intersectObjects(cunchu.children)
console.log(intersects);
if (intersects.length > 0) {
span.innerHTML = intersects[0].object.ancestors.name
const obj = intersects[0].object.ancestors
const pos = new THREE.Vector3();
// 获取三维场景中某个对象世界坐标
obj.getWorldPosition(pos);
// 向量x、y、z坐标分别在pos基础上增加30
const pos2 = pos.clone().addScalar(30);
createCameraTween(pos2, pos)
obj.add(tag)
// 可视化模型
// const axesHelper = new THREE.AxesHelper(100);
// obj.add(axesHelper);
console.log(intersects[0].object.ancestors);
outlinePass.selectedObjects = [intersects[0].object.ancestors];
chooseObj = intersects[0].object.ancestors
// 获取模型对象对应的标注点
// const obj = model.getObjectByName(intersects[0].object.ancestors.name + '标注')
// console.log(obj);
// obj.add(tag)
} else {
if (chooseObj) {//把原来选中模型对应的标签和发光描边隐藏
outlinePass.selectedObjects = []//无发光描边
console.log(chooseObj);
chooseObj.remove(tag)//从场景移除
// 相机从当前位置camera.position回到整体预览状态
createCameraTween(cameraPos0, target0)
}
}
})
// 相机动画函数,从A点飞行到B点,A点表示相机当前所处状态
// pos: 三维向量Vector3,表示动画结束相机位置
// target: 三维向量Vector3,表示相机动画结束lookAt指向的目标观察点
function createCameraTween(endPos, endTarget) {
new TWEEN.Tween({
// 不管相机此刻处于什么状态,直接读取当前的位置和目标观察点
x: camera.position.x,
y: camera.position.y,
z: camera.position.z,
tx: controls.target.x,
ty: controls.target.y,
tz: controls.target.z,
})
.to({
// 动画结束相机位置坐标
x: endPos.x,
y: endPos.y,
z: endPos.z,
// 动画结束相机指向的目标观察点
tx: endTarget.x,
ty: endTarget.y,
tz: endTarget.z,
}, 2000)
.onUpdate(function (obj) {
// 动态改变相机位置
camera.position.set(obj.x, obj.y, obj.z);
// 动态计算相机视线
// camera.lookAt(obj.tx, obj.ty, obj.tz);
controls.target.set(obj.tx, obj.ty, obj.tz);
controls.update();//内部会执行.lookAt()
})
.start();
}
document.getElementById('close').addEventListener('click', function () {
if (chooseObj) {//把原来选中模型对应的标签和发光描边隐藏
outlinePass.selectedObjects = []//无发光描边
console.log(chooseObj);
chooseObj.remove(tag)//从场景移除
// 相机从当前位置camera.position回到整体预览状态
createCameraTween(cameraPos0, target0)
}
})
tag.js
javascript
// 引入CSS2渲染器CSS2DRenderer
import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
const div = document.getElementById('tag')
div.style.top = '-161px'//指示线端点和标注点重合
// HTML元素转化为threejs的css2模型对象
const tag = new CSS2DObject(div)
export default tag
mesh-modelDemo04.js
javascript
import * as THREE from 'three'
// 引入gltf加载器
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
// 实例化一个加载器对象
const loader = new GLTFLoader()
const model = new THREE.Group()
loader.load('./model/工厂.glb', function (gltf) {
console.log('gltg', gltf);
model.add(gltf.scene)
})
export { model }