初始提交:全国牛产业大数据中心大屏项目
This commit is contained in:
223
src/hooks/map/useMapMarkedLightPillar.js
Normal file
223
src/hooks/map/useMapMarkedLightPillar.js
Normal 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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user