3D-Demo/1_wind/index.html

576 lines
17 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>风机Demo</title>
<style>
body {
margin:0;
overflow: hidden;
color: #FFF;
position: relative
/* 隐藏body窗口区域滚动条 */
}
.header-box{
width: 100%;
height: 126px;
position:absolute;
background: url(./assets/images/header-bg.png) no-repeat;
background-size: 100% 100%;
top: -30px;
z-index: 200;
}
.titile{
font-size: 36px;
margin-top: 40px;
font-weight: 500;
font-family: FZLanTingHeiS-B-GB;
text-align: center;
}
.echarts-box{
height: 28vh;
width: 35.08vw;
left: 20px;
bottom: 40px;
position:absolute
}
.equipmentLabel {
z-index: 999;
width: 988px;
height: 451px;
}
.equipmentLabel > li:nth-child(1) {
color: transparent;
width: 191px;
height: 225px;
background-image: url("./assets/images/1.png");
background-size: 191px auto;
position: absolute;
right: 302px;
top: 0;
}
.equipmentLabel .labelInfo {
color: transparent;
width: 302px;
height: 225px;
background-image: url("./assets/images/2.png");
background-size: 302px auto;
position: absolute;
right: 0;
top: 0;
padding: 10px;
box-sizing: border-box;
}
.equipmentLabel .labelInfo > div {
width: 100%;
height: 100%;
background-color: #04669e73;
border: 1px solid #15c5e8;
box-sizing: border-box;
padding: 20px 20px;
}
.equipmentLabel .labelInfo > div header {
width: 100%;
text-align: left;
font-size: 14px;
line-height: 20px;
color: #fff;
border-bottom: 1px dashed aqua;
padding-bottom: 14px;
}
.equipmentLabel .labelInfo > div header .en {
font-size: 12px;
color: aqua;
}
.equipmentLabel .labelInfo > div ul {
width: 100%;
color: #fff;
}
.equipmentLabel .labelInfo > div ul li {
line-height: 30px;
font-size: 14px;
display: flex;
text-align: left;
align-items: center;
}
.equipmentLabel .labelInfo > div ul li span:nth-child(1) {
width: 40%;
}
.equipmentLabel .labelInfo > div ul li span:nth-child(2) {
width: 15%;
color: #f0c002;
text-align: right;
margin-right: 10px;
}
.equipmentLabel .labelInfo > div ul li span:nth-child(3) {
width: 30%;
}
</style>
<!-- 引入echarts文件 -->
<script src="./assets/js/echarts.min.js"></script>
</head>
<body style="background-color: #030B1A;">
<div class="header-box">
<div class="titile">智慧风机监测系统</div>
</div>
<div class="echarts-box"></div>
<!-- 我们将把three.js渲染的效果显示在这个div中div为容器 -->
<div id="puiedu-webgl-output" ></div>
<!-- 2D信息弹窗 -->
<ul id='equipmentLabelRef' class='equipmentLabel' >
<li></li>
<li class='labelInfo' >
<div>
<header>
<div id="cn">{labelData.cn}</div>
<span id="en">{labelData.en}</span>
</header>
<ul id="valueUl"></ul>
</div>
</li>
</ul>
<script type="module">
import * as THREE from "../src/Three.js"
import { GLTFLoader } from '../jsm/loaders/GLTFLoader.js';
import { OrbitControls } from '../jsm/controls/OrbitControls.js';
//后处理js
// EffectComposer(效果组合器)对象
import { EffectComposer } from '../jsm/postprocessing/EffectComposer.js'
// RenderPass该通道在指定的场景和相机的基础上渲染出一个新场景
import { RenderPass } from '../jsm/postprocessing/RenderPass.js'
import { OutlinePass } from '../jsm/postprocessing/OutlinePass.js'
//2D信息
import {CSS2DRenderer,CSS2DObject} from "../jsm/renderers/CSS2DRenderer.js";
//导入模拟数据
import {labelData} from './assets/labelData/labelData.js'
/*
*定义变量
*/
const scale = 0.0003
let size = {
w: window.innerWidth,
h: window.innerHeight
}
//存储各个部件
let equipmentMaterialMap = new Map()
//风机涡轮转动参数
let matrixTurbine = null
//颜色材质
let metal = null
//线框材质
let wireframe = null
//动画帧
let mixers = new Map()
//创建一个动画时钟对象Clock
let clock = new THREE.Clock()
let mixer = null
/*
*点击事件相关变量
*/
//获取所有内部结构网格对象,用于判断射线相交对象
let equipment = null
//鼠标点击-屏幕坐标
let mouse = new THREE.Vector2();
//用于检测的射线
let raycaster = new THREE.Raycaster();
//效果组合
let compose = null
//弹出信息框总数据
let equipmentLabelData = labelData()[0]
//当前选中模型的数据
let nowLabelData = null
let labelCSS2D = null
var scene = null
var camera = null
var renderer = null
let labelRenderer = null
//基本组件准备
initBaseElement()
//导入模型
loadModel()
//执行渲染
run()
/*
*基本组件准备(场景、相机、渲染器、灯光)
*/
function initBaseElement(){
//创建场景对象【场景:是一个容器,主要用于保存、跟踪所要渲染的物体和使用的光源】
scene = new THREE.Scene()
//创建相机【摄像机:决定了能够在场景中看到什么】
camera = createdCamera(30, size.w, size.h, [-4, -3.5, 4], 1)
//创建光源
createdLight()
//创建渲染器【渲染器:会基于摄像机的角度来计算场景对象在浏览器中会渲染成什么样子】
renderer = createdRender()
}
/*
*导入模型及渲染元素(地板、风机内部结构、风机外部涡轮)
*/
function loadModel(){
//加载地板
loadPlane()
//加载内部结构
loadEquipment()
//加载外壳涡轮
loadTurbine()
//2D信息窗注入3D场景
labelRenderer = createTurbineLabel()
}
/*
*执行(执行渲染、鼠标操作)
*/
function run(){
// //辅助坐标系 参数250表示坐标系大小可以根据场景大小去设置
// var axesHelper = new THREE.AxesHelper(250)
// scene.add(axesHelper)
//执行渲染
render()
//鼠标操作
mouseMove()
//浏览器窗口监听,实现场景的自适应
window.addEventListener('resize',onWindowResize,false)
}
/*
*创建相机
*/
function createdCamera(fov,width,height,position,zoom){
var camera = new THREE.PerspectiveCamera(fov,width/height,0.1,1000,zoom)
camera.position.set(position[0],position[1],position[2])
return camera
}
/*
*创建光源
*/
function createdLight(){
const arr = [
[100,100,100],
[-100,100,100],
[100,-100,100]
]
arr.forEach(lightArr=>{
let spotLight = new THREE.DirectionalLight(0xffffff,3)
let [x,y,z] = lightArr
spotLight.position.set(x, y, z);
scene.add(spotLight);
})
}
//渲染器
function createdRender() {
let renderer = new THREE.WebGLRenderer({antialias: true, alpha: true})
renderer.shadowMap.enabled = true
renderer.setSize(size.w,size.h)
renderer.setClearColor(0x030B1A)
//获取容器,并加入渲染器
document.getElementById('puiedu-webgl-output').appendChild(renderer.domElement)
return renderer
}
//加载地板
function loadPlane(){
const loader = new GLTFLoader();
loader.load('assets/models/plane.glb',(obj)=>{
let mesh = obj.scene
mesh.scale.set(scale,scale,scale)
mesh.position.set(0,-2,0)
scene.add(mesh)
})
}
//加载内部结构
function loadEquipment(){
const loader = new GLTFLoader();
loader.load('assets/models/equipment.glb',(obj)=>{
let mesh = obj.scene
mesh.name = 'equipment'
//将网格对象给equipment用于鼠标点击时检测与射线相交的物体
equipment = mesh
console.log("内部结构equipment",equipment)
mesh.traverse(child =>{
if (child.isMesh) {
child.material = child.material.clone()
equipmentMaterialMap.set(child.name,child)
}
})
mesh.scale.set(scale,scale,scale)
mesh.position.set(0,-2,0)
scene.add(mesh)
})
}
//加载外壳涡轮
function loadTurbine(){
const loader = new GLTFLoader();
if (scene.getObjectByName('turbine')) {
let removeTurbine = scene.getObjectByName('turbine')
scene.remove(removeTurbine)
}
loader.load('assets/models/turbine.glb',(obj)=>{
console.log("模型",obj)
matrixTurbine = obj
let mesh = obj.scene
mesh.name = 'turbine'
metal = mesh.getObjectByName('颜色材质')
wireframe = mesh.getObjectByName('线框材质')
//设置颜色材质的显示和隐藏
metal.visible = false
mesh.scale.set(scale, scale, scale)
mesh.position.set(0,-2,0)
scene.add(mesh)
changeAnimation(mesh,'Anim_0')
})
}
//风机旋转动画
function changeAnimation(turbine,animationName){
const animations = matrixTurbine.animations
//创建混合器播放turbine包含的帧动画数据
mixer = new THREE.AnimationMixer(turbine)
//AnimationClip 是一组可重复使用的关键帧轨迹,代表一个动画
const clip = THREE.AnimationClip.findByName(animations,animationName)
const key = "AA"
if (clip) {
const action = mixer.clipAction(clip)
action.play()
mixers.set(key, mixer)
}else{
mixers.delete(key)
}
}
//鼠标点击事件
function onPointerClick(event) {
const [w,h] = [window.innerWidth,window.innerHeight]
mouse.x = (event.clientX / w) * 2 - 1
mouse.y = -(event.clientY / h) * 2 + 1
//更新射线
raycaster.setFromCamera(mouse, camera)
console.log('参数',equipment)
//返回被击中的信息
const intersects = raycaster.intersectObject(equipment, true)
if (intersects.length <= 0) {
return false;
}
const selectedObject = intersects[0].object
if (selectedObject.isMesh) {
outline([selectedObject])
//显示弹窗
updateLabel(intersects[0],equipmentLabelData[selectedObject.name])
}
}
//点击后高亮线框渲染
function outline(selectedObjects,color = 0x15c5e8) {
const [w,h] = [window.innerWidth,window.innerHeight]
//RenderPass这个通道会渲染场景但不会将渲染结果输出到屏幕上
let renderPass = new RenderPass(scene,camera)
//高亮数据
let outlinePass = new OutlinePass(
new THREE.Vector2(w,h),//分辨率
scene,
camera,
selectedObjects//选中的物体对象,传入需要边界线进行高亮处理的对象
)
outlinePass.renderToScreen = true
outlinePass.selectedObjects = selectedObjects
outlinePass.edgeStrength = 3//粗
outlinePass.edgeGlow = 0//发光
outlinePass.visibleEdgeColor.set(color);//设置显示的颜色
outlinePass.hiddenEdgeColor.set(color);//设置隐藏的颜色
//创建效果组合器对象,可以在该对象上添加后期处理通道,通过配置该对象,
//使它可以渲染我们的场景并应用额外的后期处理步骤在render循环中
//使用EffectComposer渲染场景、应用通道并输出结果
compose = new EffectComposer(renderer)
compose.addPass(renderPass)
compose.addPass(outlinePass)
compose.render(scene,camera)
}
//创建2D信息窗
function createTurbineLabel(){
let labelRenderer = new CSS2DRenderer();
labelRenderer.setSize( window.innerWidth, window.innerHeight );
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
document.body.appendChild( labelRenderer.domElement );
labelCSS2D = new CSS2DObject(document.getElementById('equipmentLabelRef'));
scene.add(labelCSS2D);
labelCSS2D.visible = false
return labelRenderer
}
//更新信息窗位置
function updateLabel(intersect,labelData){
var cnDiv = document.getElementById('cn')
cnDiv.innerHTML = labelData.cn
var enDiv = document.getElementById('en')
enDiv.innerHTML = labelData.en
var str = ""
labelData.list.forEach(item=>{
str+= `<li>
<span>${item.name}</span>
<span>${item.value}</span>
<span>${item.unit}</span>
</li>`
})
document.getElementById('valueUl').innerHTML=str
const point = intersect.point;
labelCSS2D.position.set(point.x-0.01, point.y+0.01, point.z-0.03);
labelCSS2D.visible = true
}
/*
*执行渲染
*/
function render(){
//执行渲染操作
if (scene && camera) {
renderer.render(scene,camera)
labelRenderer.render(scene,camera)
}
// 用于跟踪时间的对象
const delta = new THREE.Clock().getDelta(); //获取自设置 oldTime 时间以来经过的秒数,并将 oldTime 设置为当前时间在此delta基本为0
if (compose) {
compose.render(delta);
}
requestAnimationFrame(render)
//获得两帧的时间间隔
const mixerUpdateDelta = clock.getDelta();
//更新混合器相关的时间
mixers.forEach(mixer =>{
mixer.update(mixerUpdateDelta)
})
}
/*
*鼠标操作-放大、缩小、旋转
*/
function mouseMove(){
//创建控件对象
var controls = new OrbitControls(camera,labelRenderer.domElement)
//初始化鼠标点击事件
document.addEventListener("click", onPointerClick);
}
/*
*三维场景自适应
*/
function onWindowResize(){
camera.aspect = window.innerWidth/window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth,window.innerHeight)
labelRenderer.setSize( window.innerWidth, window.innerHeight );
}
</script>
<script type="module">
//初始化echarts实例
var myChart = echarts.init(document.querySelector(".echarts-box"))
//制定图表的配置项和数据
var options = {
title: {
text: '风机功率和风速折线图',
textStyle:{
color:'#fffff0',
fontSize : 16,
},
left : 5
},
tooltip: {
trigger: "axis",
},
legend: {
textStyle:{
color:'#ffffff'
},
data: ['功率', '风速']
},
grid: {
left: 10,
right: 10,
bottom: 20,
top: 30,
containLabel: true,
},
xAxis: {
name: "时间/小时",
type: "category",
// boundaryGap: false,
data: ["1", "3", "5", "7", "9", "11", "13","15", "17", "19", "21", "23"],
axisLine: {
// show: false,
lineStyle: {
color: "#028ab5ad",
},
},
},
yAxis: {
// name: "风速/功率",
type: "value",
axisLine: {
show: false,
lineStyle: {
color: "#028ab5ad",
},
},
splitLine: {
lineStyle: {
color: ["#028ab545"],
},
},
},
series: [
{
name: "功率",
type: "line",
data: [12, 6, 13, 5, 18, 15, 8,2, 4, 12, 15, 10],
lineStyle: {
color: "#15c5e8",
},
itemStyle: {
normal: {
color: "#15c5e8",
},
},
},
{
name: "风速",
type: "line",
// stack: "总量",
data: [2, 4, 12, 15, 10, 11, 5,12, 6, 13, 5, 18],
lineStyle: {
color: "#c8a818",
},
itemStyle: {
normal: {
color: "#c8a818",
},
},
},
],
}
//使用刚指定的配置项和数据显示图表
myChart.setOption(options)
</script>
</body>
</html>