初始提交:全国牛产业大数据中心大屏项目

This commit is contained in:
2025-10-27 17:29:42 +08:00
commit b47ec8eb88
694 changed files with 35059 additions and 0 deletions

View File

@@ -0,0 +1,223 @@
import * as THREE from 'three'
import TWEEN from '@tweenjs/tween.js'
import useCoord from '@/hooks/useCoord'
import { deepMerge, random } from '@/utils'
/**
*
* @param {object} {
* pointTextureUrl:标记点的图片url
* lightHaloTextureUrl:光圈的URL
* lightPillarUrl:光柱的URL
* scaleFactor:1 缩放系数,用来调整标记点和光圈的缩放大小
* }
*
* @returns
*/
export default function useMarkedLightPillar(options) {
const { geoSphereCoord } = useCoord()
// 默认参数
let defaultOptions = {
pointTextureUrl: './assets/texture/标注.png',
lightHaloTextureUrl: './assets/texture/标注光圈.png',
lightPillarUrl: './assets/texture/光柱.png',
scaleFactor: 1, // 缩放系数
}
defaultOptions = deepMerge(defaultOptions, options)
// 纹理加载器
const textureLoader = new THREE.TextureLoader()
// 射线拾取对象
const raycaster = new THREE.Raycaster()
let containerWidth = window.width
let containerHeight = window.height
// 对象属性
let getBoundingClientRect = null
/**
* 创建标记点
* @param {*} R 地球半径根据R来进行缩放
* @returns
*/
const createPointMesh = () => {
// 标记点:几何体,材质,
const geometry = new THREE.PlaneBufferGeometry(1, 1)
const material = new THREE.MeshBasicMaterial({
map: textureLoader.load(defaultOptions.pointTextureUrl),
color: 0x00ffff,
side: THREE.DoubleSide,
transparent: true,
depthWrite: false, //禁止写入深度缓冲区数据
})
let mesh = new THREE.Mesh(geometry, material)
mesh.name = 'createPointMesh'
// 缩放
const scale = 0.15 * defaultOptions.scaleFactor
mesh.scale.set(scale, scale, scale)
return mesh
}
/**
* 创建光圈
* @param {*} R 地球半径根据R来进行缩放
* @returns
*/
const createLightHalo = () => {
// 标记点:几何体,材质,
const geometry = new THREE.PlaneBufferGeometry(1, 1)
const material = new THREE.MeshBasicMaterial({
map: textureLoader.load(defaultOptions.lightHaloTextureUrl),
color: 0x00ffff,
side: THREE.DoubleSide,
opacity: 0,
transparent: true,
depthWrite: false, //禁止写入深度缓冲区数据
})
let mesh = new THREE.Mesh(geometry, material)
mesh.name = 'createLightHalo'
// 缩放
const scale = 0.3 * defaultOptions.scaleFactor
mesh.scale.set(scale, scale, scale)
// 动画延迟时间
const delay = random(0, 2000)
// 动画:透明度缩放动画
mesh.tween1 = new TWEEN.Tween({ scale: scale, opacity: 0 })
.to({ scale: scale * 1.5, opacity: 1 }, 1000)
.delay(delay)
.onUpdate(params => {
let { scale, opacity } = params
mesh.scale.set(scale, scale, scale)
mesh.material.opacity = opacity
})
mesh.tween2 = new TWEEN.Tween({ scale: scale * 1.5, opacity: 1 })
.to({ scale: scale * 2, opacity: 0 }, 1000)
.onUpdate(params => {
let { scale, opacity } = params
mesh.scale.set(scale, scale, scale)
mesh.material.opacity = opacity
})
mesh.tween1.chain(mesh.tween2)
mesh.tween2.chain(mesh.tween1)
mesh.tween1.start()
return mesh
}
/**
* 创建光柱
* @param {*} lon
* @param {*} lat
* @param {*} heightScaleFactor 光柱高度的缩放系数
* @returns
*/
const createLightPillar = (lon, lat, heightScaleFactor = 1) => {
let group = new THREE.Group()
// 柱体高度
const height = heightScaleFactor
// 柱体的geo,6.19=柱体图片高度/宽度的倍数
const geometry = new THREE.PlaneBufferGeometry(height / 6.219, height)
// 柱体旋转90度垂直于Y轴
geometry.rotateX(Math.PI / 2)
// 柱体的z轴移动高度一半对齐中心点
geometry.translate(0, 0, height / 2)
// 柱子材质
const material = new THREE.MeshBasicMaterial({
map: textureLoader.load(defaultOptions.lightPillarUrl),
color: 0x00ffff,
transparent: true,
depthWrite: false,
side: THREE.DoubleSide,
})
// 光柱01
let light01 = new THREE.Mesh(geometry, material)
light01.name = 'createLightPillar01'
// 光柱02复制光柱01
let light02 = light01.clone()
light02.name = 'createLightPillar02'
// 光柱02旋转90°跟 光柱01交叉
light02.rotateZ(Math.PI / 2)
// 创建底部标点
const bottomMesh = createPointMesh()
// 创建光圈
const lightHalo = createLightHalo()
// 将光柱和标点添加到组里
group.add(bottomMesh, lightHalo, light01, light02)
// 设置组对象的姿态
// group = setMeshQuaternion(group, R, lon, lat)
group.position.set(lon, lat, 0)
return group
}
/**
* 设置光柱颜色
* @param {*} group
* @param {*} color
*/
const setLightPillarColor = (group, color) => {
group.children.forEach(item => {
item.material.color = new THREE.Color(color)
})
}
/**
* 设置网格的位置及姿态
* @param {*} mesh
* @param {*} R
* @param {*} lon
* @param {*} lat
* @returns
*/
const setMeshQuaternion = (mesh, R, lon, lat) => {
const { x, y, z } = geoSphereCoord(R, lon, lat)
mesh.position.set(x, y, z)
// 姿态设置
// mesh在球面上的法线方向(球心和球面坐标构成的方向向量)
let meshVector = new THREE.Vector3(x, y, z).normalize()
// mesh默认在XOY平面上法线方向沿着z轴new THREE.Vector3(0, 0, 1)
let normal = new THREE.Vector3(0, 0, 1)
// 四元数属性.quaternion表示mesh的角度状态
//.setFromUnitVectors();计算两个向量之间构成的四元数值
mesh.quaternion.setFromUnitVectors(normal, meshVector)
return mesh
}
/**
* 射线拾取返回选中的mesh
* @param {*} event
* @param {*} container
* @param {*} camera
* @param {*} mesh // 光柱group
* @returns
*/
const getRaycasterObj = (event, container, camera, mesh) => {
//屏幕坐标转WebGL标准设备坐标
if (!getBoundingClientRect) {
getBoundingClientRect = container.getBoundingClientRect()
containerWidth = container.offsetWidth
containerHeight = container.offsetHeight
}
var x = ((event.clientX - getBoundingClientRect.left) / containerWidth) * 2 - 1
var y = -((event.clientY - getBoundingClientRect.top) / containerHeight) * 2 + 1
//通过鼠标单击位置标准设备坐标和相机参数计算射线投射器`Raycaster`的射线属性.ray
raycaster.setFromCamera(new THREE.Vector2(x, y), camera)
//返回.intersectObjects()参数中射线选中的网格模型对象
// 未选中对象返回空数组[],选中一个数组1个元素选中两个数组两个元素
var intersects = raycaster.intersectObjects(mesh)
return intersects
}
/**
* 选择光柱,返回选择的对象
* @param {*} event
* @param {*} container
* @param {*} camera
* @param {*} mesh
* @returns 返回选择的对象
*/
const chooseLightPillar = (event, container, camera, mesh) => {
event.preventDefault()
// 获取拾取的对象数组
let intersects = getRaycasterObj(event, container, camera, mesh)
// 大于0说明说明选中了mesh,返回对象
if (intersects.length > 0) {
return intersects[0]
}
return null
}
return {
createLightPillar,
setLightPillarColor,
chooseLightPillar,
}
}