555 lines
16 KiB
HTML
555 lines
16 KiB
HTML
<!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 = new THREE.Scene()
|
||
//创建相机
|
||
var camera = createdCamera(30, size.w, size.h, [-4, -3.5, 4], 1)
|
||
//创建光源
|
||
createdLight()
|
||
//创建渲染器
|
||
var renderer = createdRender()
|
||
//信息窗2D渲染器
|
||
let labelRenderer = createTurbineLabel()
|
||
|
||
// 辅助坐标系 参数250表示坐标系大小,可以根据场景大小去设置
|
||
// var axesHelper = new THREE.AxesHelper(250)
|
||
// scene.add(axesHelper)
|
||
|
||
//加载地板
|
||
loadPlane()
|
||
//加载内部结构
|
||
loadEquipment()
|
||
//加载外壳涡轮
|
||
loadTurbine()
|
||
|
||
//执行渲染
|
||
render()
|
||
//鼠标操作
|
||
mouseMove()
|
||
|
||
|
||
/*
|
||
*创建相机
|
||
*/
|
||
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
|
||
// labelCSS2D.element.addEventListener('click',()=>{
|
||
// consoe.log("dianji")
|
||
// labelCSS2D.visible = false
|
||
// })
|
||
}
|
||
|
||
//更新信息窗位置
|
||
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);
|
||
}
|
||
|
||
/*
|
||
*三维场景自适应
|
||
*/
|
||
window.addEventListener('resize',onWindowResize,false)
|
||
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> |