Files
datav---Cattle-Industry/src/hooks/map/useMapMarkedLightPillar.js

224 lines
7.3 KiB
JavaScript
Raw 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.

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,
}
}