This commit is contained in:
nicomacbookpro
2025-08-21 09:29:02 +08:00
parent fe5efe98e4
commit c3549f9278
3 changed files with 169 additions and 638 deletions

161
src/utils/plane_grass.js Normal file
View File

@@ -0,0 +1,161 @@
import * as THREE from "three";
const BLADE_WIDTH = 0.1
const BLADE_HEIGHT = 0.8
const BLADE_HEIGHT_VARIATION = 0.6
const BLADE_VERTEX_COUNT = 5
const BLADE_TIP_OFFSET = 0.1
function interpolate(val, oldMin, oldMax, newMin, newMax) {
return ((val - oldMin) * (newMax - newMin)) / (oldMax - oldMin) + newMin
}
class GrassGeometry extends THREE.BufferGeometry {
constructor(size, count) {
super()
const positions = []
const uvs = []
const indices = []
for (let i = 0; i < count; i++) {
const surfaceMin = (size / 2) * -1
const surfaceMax = size / 2
// ✅ 直接在正方形范围内取随机点
const x = surfaceMin + Math.random() * size
const y = surfaceMin + Math.random() * size
uvs.push(
...Array.from({ length: BLADE_VERTEX_COUNT }).flatMap(() => [
interpolate(x, surfaceMin, surfaceMax, 0, 1),
interpolate(y, surfaceMin, surfaceMax, 0, 1)
])
)
const blade = this.computeBlade([x, 0, y], i)
positions.push(...blade.positions)
indices.push(...blade.indices)
}
this.setAttribute(
'position',
new THREE.BufferAttribute(new Float32Array(positions), 3)
)
this.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2))
this.setIndex(indices)
this.computeVertexNormals()
}
computeBlade(center, index = 0) {
const height = BLADE_HEIGHT + Math.random() * BLADE_HEIGHT_VARIATION
const vIndex = index * BLADE_VERTEX_COUNT
// Randomize blade orientation and tip angle
const yaw = Math.random() * Math.PI * 2
const yawVec = [Math.sin(yaw), 0, -Math.cos(yaw)]
const bend = Math.random() * Math.PI * 2
const bendVec = [Math.sin(bend), 0, -Math.cos(bend)]
// Calc bottom, middle, and tip vertices
const bl = yawVec.map((n, i) => n * (BLADE_WIDTH / 2) * 1 + center[i])
const br = yawVec.map((n, i) => n * (BLADE_WIDTH / 2) * -1 + center[i])
const tl = yawVec.map((n, i) => n * (BLADE_WIDTH / 4) * 1 + center[i])
const tr = yawVec.map((n, i) => n * (BLADE_WIDTH / 4) * -1 + center[i])
const tc = bendVec.map((n, i) => n * BLADE_TIP_OFFSET + center[i])
// Attenuate height
tl[1] += height / 2
tr[1] += height / 2
tc[1] += height
return {
positions: [...bl, ...br, ...tr, ...tl, ...tc],
indices: [
vIndex,
vIndex + 1,
vIndex + 2,
vIndex + 2,
vIndex + 4,
vIndex + 3,
vIndex + 3,
vIndex,
vIndex + 2
]
}
}
}
class Grass extends THREE.Mesh {
constructor(size, count) {
const geometry = new GrassGeometry(size, count)
const material = new THREE.ShaderMaterial({
uniforms: {
uCloud: { value: "" },
offsetX: { value: 0.5 },
offsetY: { value: 0.3 },
uTime: { value: 0 },
},
side: THREE.DoubleSide,
vertexShader: ` uniform float uTime;
uniform float offsetX;
uniform float offsetY;
varying vec3 vPosition;
varying vec2 vUv;
varying vec3 vNormal;
float wave(float waveSize, float tipDistance, float centerDistance) {
// Tip is the fifth vertex drawn per blade
bool isTip = (gl_VertexID + 1) % 5 == 0;
float waveDistance = isTip ? tipDistance : centerDistance;
return sin((uTime / 500.0) + waveSize) * waveDistance;
}
void main() {
vPosition = position;
vUv = uv;
// Cloud shadow move
vUv.x += uTime * 0.0001 * offsetX;
vUv.y += uTime * 0.0001 * offsetY;
vNormal = normalize(normalMatrix * normal);
if (vPosition.y < 0.0) {
vPosition.y = 0.0;
} else {
vPosition.x += wave(uv.x * 10.0, 0.3, 0.1);
}
gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
}`,
fragmentShader: ` uniform sampler2D uCloud;
uniform float uTime;
varying vec3 vPosition;
varying vec2 vUv;
varying vec3 vNormal;
vec3 green = vec3(0.2, 0.6, 0.3);
void main() {
vec3 color = mix(green * 0.7, green, vPosition.y);
color = mix(color, texture2D(uCloud, vUv).rgb, 0.4);
float lighting = normalize(dot(vNormal, vec3(10)));
gl_FragColor = vec4(color + lighting * 0.03, 1.0);
}`,
})
super(geometry, material)
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(50, 50).rotateX(Math.PI / 2),
material
)
floor.position.y = -Number.EPSILON
this.add(floor)
}
update(time) {
this.material.uniforms.uTime.value = time
}
}
const planeGrass = new Grass(50, 100000)
export { planeGrass }

View File

@@ -1,638 +0,0 @@
<template>
<div class="container">
<div ref="threeDiv" id="webgl" class="webgl"></div>
<div class="legend_box">
<div class="legend_head"><span class="text_gradual head_text">显示选择</span></div>
<div class="legend_content">
<div class="legend_item" v-for="(v, k) in legendList" :key="k">
<div :class="{ icon_check: true, icon_checked: v.visible }" @click="handleCheck(v, k)"></div>
<!-- <img src="@/assets/bigScreen/point/icon_check.png" alt="" @click="handleCheck(v, k)">
<img src="@/assets/bigScreen/point/icon_check.png" alt="" @click="handleCheck(v,k)"> -->
<span :class="{ span_checked: v.visible }">{{ v.name }}</span>
</div>
</div>
</div>
</div>
</template>
<script>
// 引入three.js
import * as THREE from 'three';
// // 引入扩展库OrbitControls.js
// import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// // 引入扩展库GLTFLoader.js
// import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';
import { Water } from 'three/examples/jsm/objects/Water.js';
import { drawAxes } from "@/utils/three.js";
import Scene from "@/components/Scene/index.vue";
export default {
name: 'Home',
props: {
},
components: {
Scene
},
data(){
return {
scene: null, // 场景
camera: null, // 相机
renderer: null, // 渲染器
// 从图片中提取的数据
xAxisLabels: ['A', 'B', 'C', 'D', 'E'],
zAxisLabels: ['A', 'B', 'C', 'D', 'E'],
labelRenderer: null,
legendList: [
{ label: '红色通道', val: '1', checked: true },
{ label: '绿色通道', val: '2', checked: true },
{ label: '蓝色通道', val: '3', checked: true },
{ label: '灰色通道', val: '4', checked: true },],
}
},
mounted(){
// console.log(THREE.Scene);
// this.init()
const sceneBox = document.getElementById('webgl');
const options = {
gridSize: { x: 50, y: 50, z: 50 }, // 三维网格尺寸
// gridSize: { x: 100, y: 100, z: 100 }, // 三维网格尺寸
// grid_uv_up = [5, 5],
// grid_uv_down = [5, 5],
grid_uv_left: [10, 10],
// grid_uv_right = [5, 4],
// grid_uv_front = [5, 4],
grid_uv_back: [10, 10],
show_water: true, // 是否显示水面
water_height: 5, // 水面厚度
data: {
红球: [
[20, 40, 0],
[20, 40, 10],
[20, 40, 20],
[20, 40, 30],
[20, 40, 40],
],
绿球: [
[10, 10, 0],
[10, 10, 10],
[10, 10, 20],
],
蓝球: [
[20, 20, 0],
[20, 20, 10],
[20, 20, 20],
[40, 20, 20],
[40, 20, 0],
],
灰球: [
[30, 30, 0],
[30, 30, 10],
[30, 30, 20],
[40, 30, 0],
[40, 30, 10],
],
白球: [
[20, 50, 0],
[20, 50, 10],
[30, 50, 20],
[30, 50, 0],
[30, 50, 10],
],
},
// labels_left: ["", "A", "B", "C", "D", "E"],
labels_left: ["", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"],
// labels_back: ["", "A", "B", "C", "D", "E"],
labels_back: ["", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"],
};
const demo1 = drawAxes(sceneBox, options,(val)=> {
this.legendList = val
})
},
methods: {
handleCheck(v,k){
if (this.legendList[k]) this.legendList[k].visible = !this.legendList[k].visible;
},
init(){
// 创建场景
this.scene = new THREE.Scene();
// // 网格辅助线 -- 地表格(网格宽度、等分数、中心线颜色,网格线颜色)
// const grid = new THREE.GridHelper(12, 12, 0x3a506b, 0x1a3a5a);
// scene.add(grid);
// this.createGrids()
// 3. 创建网格辅助平面(只显示正方向)
this.createPositivePlane(this.scene, 'XY', 0x6db1fc, 5, 5); // 红色XY平面
this.createPositivePlane(this.scene, 'XZ', 0x6db1fc, 5, 5); // 绿色XZ平面
this.createPositivePlane(this.scene, 'YZ', 0x6db1fc, 5, 5); // 蓝色YZ平面
// 三维辅助线(长度)-- 三维坐标系
// const axisHelper = new THREE.AxesHelper(5);
// this.scene.add(axisHelper);
this.addPositiveAxes(6)
this.createWaterPool()
//定义一个几何体
// const geometry = new THREE.SphereGeometry(2, 30, 30);
//创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(100, 100, 100);
// //创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
color: 0xff0000,//0xff0000设置材质颜色为红色
});
//定义一种材质,显示为线框
// const material = new THREE.MeshBasicMaterial({ color: 0xB3DD, wireframe: true });
//网孔(Mesh)是用来承载几何模型的一个对象,可以把材料应用到它上面
const ball = new THREE.Mesh(geometry, material);
//把几何模型添加到场景中,对象被添加到原点(0,0,0)坐标。
this.scene.add(ball);
this.scene.position.set(-2.5, -2.5, -2.5); // 修改 场景 中心点
//创建透视投影相机视角45度画幅比例 宽比高近平面距离0.1远平面1000
this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
//移动相机位置
// this.camera.position.z = 8
this.camera.position.set(8, 6, 5);
//相机观察目标指向Threejs 3D空间中某个位置
this.camera.lookAt(0, 0, 0); //坐标原点;
//创建渲染器
this.renderer = new THREE.WebGLRenderer({
alpha: true, // 启用透明度
// antialias: true
});
this.renderer.setClearColor(0x000000, 0); // 设置透明背景
//渲染器canvas宽高设为与窗口一致
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.render(this.scene, this.camera);
//将渲染器对应的dom元素添加到body中
const sceneBox = document.getElementById('webgl');
sceneBox.appendChild(this.renderer.domElement);
// function render() {
// //渲染循环以每秒60次的频率来绘制场景
// requestAnimationFrame(render);
// //设置球体绕y轴旋转
// ball.rotation.y += 0.005;
// this.renderer.render(this.scene, this.camera);
// }
// render();
// // 创建CSS2D渲染器用于标签
// this.labelRenderer = new CSS2DRenderer();
// this.labelRenderer.setSize(
// sceneBox.clientWidth,
// sceneBox.clientHeight
// );
// this.labelRenderer.domElement.style.position = 'absolute';
// this.labelRenderer.domElement.style.top = '0';
// this.labelRenderer.domElement.style.pointerEvents = 'none';
// sceneBox.appendChild(this.labelRenderer.domElement);
// this.addGridHelpers()
/**
* 创建轨道控制器OrbitControls
* 可以使得相机围绕目标进行轨道运动
*/
const controls = new OrbitControls(this.camera, this.renderer.domElement);
controls.addEventListener('change',()=> {
this.renderer.render(this.scene, this.camera);
})
// controls.autoRotate = true; // 相机是否自动旋转
// controls.autoRotateSpeed = 3; // 自转速度
// // // 设置控制器阻尼让控制器更有真实效果必须在动画循环render()中调用controls.update()()
// controls.enableDamping = true;
// 监听尺寸变化实现自适应画面
window.addEventListener("resize", () => {
// console.log("画面变化了");
// 更新摄像头
this.camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
this.camera.updateProjectionMatrix();
// 更新渲染器
this.renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比
this.renderer.setPixelRatio(window.devicePixelRatio);
});
},
// 创建坐标轴
async addPositiveAxes(axisLength) {
// axisLength 坐标轴长度
const tickInterval = 1; // 刻度间隔
// 加载刻度图片(替换为你的图片路径)
// const tickTexture = await loadTexture('/path/to/your/tick-image.png');
const tickTextureX = [require('@/assets/icon_xz/x01.png'), require('@/assets/icon_xz/x02.png'),
require('@/assets/icon_xz/x03.png'), require('@/assets/icon_xz/x04.png'), require('@/assets/icon_xz/x05.png')
]
const tickTextureZ = [require('@/assets/icon_xz/z01.png'), require('@/assets/icon_xz/z02.png'),
require('@/assets/icon_xz/z03.png'), require('@/assets/icon_xz/z04.png'), require('@/assets/icon_xz/z05.png')
]
// const tickTextureX = ['@/assets/icon/x01.png', '@/assets/icon/x02.png',
// '@/assets/icon/x03.png', '@/assets/icon/x04.png', '@/assets/icon/x05.png'
// ]
// const tickTextureZ = ['@/assets/icon/z01.png', '@/assets/icon/z02.png',
// '@/assets/icon/z03.png', '@/assets/icon/z04.png', '@/assets/icon/z05.png'
// ]
// 创建X轴红色只显示正向
const xAxis = new THREE.ArrowHelper(
new THREE.Vector3(1, 0, 0), // 方向
new THREE.Vector3(0, 0, 0), // 起点
axisLength, // 长度
0xff0000, // 颜色(红色)
0.2, // 头部长度
0.1 // 头部宽度
);
this.scene.add(xAxis);
// 创建Y轴绿色只显示正向
const yAxis = new THREE.ArrowHelper(
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(0, 0, 0),
axisLength,
0x00ff00, // 颜色(绿色)
0.2,
0.1
);
this.scene.add(yAxis);
// 创建Z轴蓝色只显示正向
const zAxis = new THREE.ArrowHelper(
new THREE.Vector3(0, 0, 1),
new THREE.Vector3(0, 0, 0),
axisLength,
0x0000ff, // 颜色(蓝色)
0.2,
0.1
);
this.scene.add(zAxis);
const texLoader = new THREE.TextureLoader();
// 添加X轴刻度图片
for (let i = tickInterval; i <= axisLength; i += tickInterval) {
// .load()方法加载图像返回一个纹理对象Texture
// const texture = await new Promise(resolve => {
// texLoader.load(tickTextureX[i - 1], resolve);
// });
const texture = texLoader.load(tickTextureX[i - 1]);
const tick = this.createImageTick(texture, i, 0, 0, 'x');
this.scene.add(tick);
}
// 添加Z轴刻度图片
for (let j = tickInterval; j <= axisLength; j += tickInterval) {
// .load()方法加载图像返回一个纹理对象Texture
const texture = texLoader.load(tickTextureZ[j -1])
const tick = this.createImageTick(texture, 0, 0, j, 'z');
this.scene.add(tick);
}
// 添加轴标签
this.addAxisLabel('X', new THREE.Vector3(axisLength * 1.1, 0, 0), 0xff0000);
this.addAxisLabel('Y', new THREE.Vector3(0, axisLength * 1.1, 0), 0x00ff00);
this.addAxisLabel('Z', new THREE.Vector3(0, 0, axisLength * 1.1), 0x0000ff);
this.addAxisLabel('A', new THREE.Vector3(1, 5.3, 0), 0x6db1fc);
this.addAxisLabel('B', new THREE.Vector3(2, 5.3, 0), 0x6db1fc);
this.addAxisLabel('C', new THREE.Vector3(3, 5.3, 0), 0x6db1fc);
this.addAxisLabel('D', new THREE.Vector3(4, 5.3, 0), 0x6db1fc);
this.addAxisLabel('E', new THREE.Vector3(5, 5.3, 0), 0x6db1fc);
this.addAxisLabel('A', new THREE.Vector3(0, 5.3, 1), 0x6db1fc);
this.addAxisLabel('B', new THREE.Vector3(0, 5.3, 2), 0x6db1fc);
this.addAxisLabel('C', new THREE.Vector3(0, 5.3, 3), 0x6db1fc);
this.addAxisLabel('D', new THREE.Vector3(0, 5.3, 4), 0x6db1fc);
this.addAxisLabel('E', new THREE.Vector3(0, 5.3, 5), 0x6db1fc);
},
// 创建图片刻度
createImageTick(texture, x, y, z, axis){
const tickSize = 0.8; // 刻度大小
const material = new THREE.SpriteMaterial({ map: texture, transparent: true });
const sprite = new THREE.Sprite(material);
// 根据轴调整位置和旋转
if (axis === 'x') {
sprite.position.set(x, y, z);
} else if (axis === 'z') {
sprite.position.set(x, y, z);
}
sprite.scale.set(tickSize, tickSize, 1);
return sprite;
},
// 添加轴标签
addAxisLabel(text, position, color){
const canvas = document.createElement('canvas');
canvas.width = 128;
canvas.height = 128;
const context = canvas.getContext('2d');
context.fillStyle = 'rgba(0, 0, 0, 0)';
context.fillRect(0, 0, canvas.width, canvas.height);
context.font = 'Bold 48px Arial';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillStyle = `#${color.toString(16).padStart(6, '0')}`;
context.fillText(text, canvas.width / 2, canvas.height / 2);
const texture = new THREE.CanvasTexture(canvas);
const material = new THREE.SpriteMaterial({ map: texture, transparent: true });
const sprite = new THREE.Sprite(material);
sprite.position.copy(position);
sprite.scale.set(0.8, 0.8, 0.8);
this.scene.add(sprite);
},
// 创建水池几何体 - 宽度和深度可以根据需要调整
createWaterPool() {
const width = 5;
const depth = 5;
const height = 0.5; // 水池高度0.5
const geometry = new THREE.BoxGeometry(width, height, depth);
// 创建半透明材质
const material = new THREE.MeshPhongMaterial({
color: 0x8ddefe,
transparent: true,
opacity: 0.3,
side: THREE.DoubleSide,
specular: 0x111111, // 高光
shininess: 100, // 光泽度
refractionRatio: 0.85 // 折射率
});
// 创建水池网格
const pool = new THREE.Mesh(geometry, material);
pool.position.set(2.5, -0.2, 2.5);
pool.receiveShadow = true; // 接收阴影
pool.castShadow = true; // 投射阴影
// // 将水池放置在XZ平面Y轴位置为高度的一半因为几何体中心在原点
// pool.position.y = height / 2;
this.scene.add(pool);
// 添加一些光照
const ambientLight = new THREE.AmbientLight(0x404040);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(1, 1, 1);
directionalLight.castShadow = true;
this.scene.add(directionalLight);
// this.addWaterEffect(pool)
// this.createAdvancedWater()
},
// 水流动效果实现
addWaterEffect(pool) {
// 创建着色器材质来实现水波效果
const waterUniforms = {
time: { type: "f", value: 0.0 },
waterColor: { type: "c", value: new THREE.Color(0x8ddefe) }
};
const waterMaterial = new THREE.ShaderMaterial({
uniforms: waterUniforms,
vertexShader: `
uniform float time;
varying vec3 vPosition;
varying vec2 vUv;
void main() {
vUv = uv;
vPosition = position;
// 创建波浪效果
float waveHeight = sin(position.x * 2.0 + time) * 0.05;
waveHeight += sin(position.z * 1.5 + time * 1.5) * 0.03;
vec3 newPosition = position;
newPosition.y += waveHeight;
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`,
fragmentShader: `
uniform vec3 waterColor;
varying vec3 vPosition;
varying vec2 vUv;
void main() {
// 创建水的颜色变化
float ripple = sin(vPosition.x * 10.0 + vPosition.z * 10.0) * 0.1;
vec3 color = waterColor + vec3(ripple * 0.2);
// 透明度随波浪变化
float alpha = 0.3 + ripple * 0.1;
gl_FragColor = vec4(color, alpha);
}
`,
transparent: true,
side: THREE.DoubleSide,
wireframe: false
});
// 替换原来的材质
pool.material = waterMaterial;
// 动画循环中更新time uniform
const animate = () => {
requestAnimationFrame(animate);
waterUniforms.time.value += 0.05;
};
animate();
},
// 水流动效果实现 方式 2
createAdvancedWater() {
const waterGeometry = new THREE.PlaneGeometry(5, 5);
const water = new Water(
waterGeometry,
{
textureWidth: 512,
textureHeight: 512,
waterNormals: new THREE.TextureLoader().load('textures/waternormals.jpg', function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
}),
sunDirection: new THREE.Vector3(),
sunColor: 0xffffff,
waterColor: 0x8ddefe,
distortionScale: 3.7,
fog: this.scene.fog !== undefined
}
);
water.rotation.x = -Math.PI / 2;
// water.position.y = -0.2;
water.position.set(2.5, -0.2, 2.5);
this.scene.add(water);
// 在动画循环中更新水
const animate = () => {
requestAnimationFrame(animate);
water.material.uniforms.time.value += 0.05;
};
animate();
},
/**
* 创建正方向网格平面
* @param {THREE.Scene} scene - 场景对象
* @param {string} planeType - 平面类型: 'XY', 'XZ', 'YZ'
* @param {number} color - 网格颜色
* @param {number} size - 网格尺寸
* @param {number} divisions - 网格分割数
*/
createPositivePlane(scene, planeType, color, size, divisions) {
const step = size / divisions;
const vertices = [];
// 根据平面类型生成顶点数据
switch (planeType) {
case 'XY': // Z=0 平面 (X正方向, Y正方向)
for (let i = 0; i <= divisions; i++) {
// 平行于X轴的线 (Y变化)
vertices.push(i * step , 0, 0, i * step, size, 0);
// 平行于Y轴的线 (X变化)
vertices.push(0, i * step, 0, size, i * step, 0);
}
break;
case 'XZ': // Y=0 平面 (X正方向, Z正方向)
for (let i = 0; i <= divisions; i++) {
// 平行于X轴的线 (Z变化)
vertices.push(i * step, 0, 0, i * step, 0, size);
// 平行于Z轴的线 (X变化)
vertices.push(0, 0, i * step, size, 0, i * step);
}
break;
case 'YZ': // X=0 平面 (Y正方向, Z正方向)
for (let i = 0; i <= divisions; i++) {
// 平行于Y轴的线 (Z变化)
vertices.push(0, i * step, 0, 0, i * step, size);
// 平行于Z轴的线 (Y变化)
vertices.push(0, 0, i * step, 0, size, i * step);
}
break;
}
// 创建几何体和材质
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
const material = new THREE.LineBasicMaterial({
color: color,
transparent: true,
opacity: 0.6
});
// 添加到场景
const grid = new THREE.LineSegments(geometry, material);
scene.add(grid);
},
}
}
</script>
<style lang="scss" scoped>
.container{
width: 100%;
height: 100%;
position: relative;
.webgl{
width: 100%;
height: 100%;
}
.legend_box{
position: absolute;
top: 120px;
right: 150px;
width: 195px;
height: fit-content;
.legend_head{
width: 100%;
height: 38px;
background-image: url('~@/assets/bigScreen/point/icon_bg_1.png');
background-repeat: no-repeat;
background-size: 100% 100%;
text-align: center;
line-height: 38px;
user-select: none;
.head_text{
font-weight: 700;
font-size: 16px;
}
}
.legend_content{
width: 100%;
height: fit-content;
// height: 316px;
background-image: url('~@/assets/bigScreen/point/icon_bg_2.png');
background-repeat: no-repeat;
background-size: 100% 100%;
padding: 30px 20px;
.legend_item{
width: 100%;
height: 42px;
display: flex;
align-items: center;
justify-content: center;
color: #99bbd7;
.icon_check{
width: 20px;
height: 20px;
cursor: pointer;
background-image: url('~@/assets/bigScreen/point/icon_check.png');
background-repeat: no-repeat;
background-size: 100% 100%;
}
.icon_checked{
background-image: url('~@/assets/bigScreen/point/icon_checked.png');
}
span{
margin-left: 15px;
font-size: 16px;
}
.span_checked{
color: #69ffdd;
}
}
}
}
}
</style>

View File

@@ -55,6 +55,7 @@ import { lastData } from "@/api/bigScreen/api.js";
import { drawAxes } from "@/utils/three.js"; import { drawAxes } from "@/utils/three.js";
import { connectWebsocket, closeWebsocket } from "@/utils/websocket.js"; import { connectWebsocket, closeWebsocket } from "@/utils/websocket.js";
import { planeGrass } from "@/utils/plane_grass.js";
export default { export default {
name: 'Home', name: 'Home',
props: { props: {
@@ -259,6 +260,13 @@ export default {
this.onBallClick(falg, val) this.onBallClick(falg, val)
}) })
this.legendList = this.demo1.getGroups() this.legendList = this.demo1.getGroups()
planeGrass.position.set(25, 1, 25)
this.demo1.scene.add(planeGrass)
function animate(time) {
requestAnimationFrame(animate)
planeGrass.update(time)
}
animate()