创建场景:
this.scene = new THREE.Scene()
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000)
this.renderer = new THREE.WebGLRenderer()
this.renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
this.camera.position.z = 5
在场景中根据所给的点数据画线
drawLine() {
this.lines = new THREE.Object3D()
let routeLine = new THREE.LineBasicMaterial({
linewidth: 100,
color: 0x2342fe,
depthTest: false
})
this.routeList = []
for (let i = 0; i < route.length - 1; i++) { // route是线上每一个点的数据
let position = []
let v1 = [route[i].x, route[i].y, route[i].z]
let v2 = [route[i + 1].x, route[i + 1].y, route[i + 1].z]
position.push(...v1)
position.push(...v2)
this.routeList.push({
start: v1,
end: v2
})
let geometry = new THREE.BufferGeometry()
geometry.addAttribute('position', new THREE.Float32BufferAttribute(position, 3))
let line = new THREE.Line(geometry, routeLine)
this.lines.add(line)
}
scene.add(this.lines)
this.drawMark()
},
在线上添加marker,实现marker在线上移动的效果
drawMark() {
let imgUrl = this.$config.staticFileUrl + 'img/arrow.png' // staticFileUrl是静态资源的地址
let img = new THREE.TextureLoader().load(imgUrl)
let material = new THREE.SpriteMaterial({ // 精灵材质
map: img,
up: (0, 0, 1),
depthTest: false
})
this.spriteMarker = new THREE.Sprite(material)
let scale = 1000 // 因为精灵图片很小,所以要缩放好多倍才能看得到
this.spriteMarker.scale.set(scale, scale, scale)
this.spriteMarker.position.set(0, 0, 0)
this.lines.add(this.spriteMarker)
this.moveOnLine(this.spriteMarker, this.routeList); // marker在线上移动的动画
}
使用了Tween.js来做动画效果,具体使用查看api,Tween.js和TweenJs是不一样的,现在使用vue来安装的包是@tweenjs/tween.js
moveOnroute(mark, routes, duration = 300) {
this.results = []
for (let i = 0; i < routes.length; i++) {
const varg = this.addTween(mark, routes[i].start, routes[i].end, duration)
this.results.push(varg)
}
if (this.results.length > 0) {
for (let i = 0; i < this.results.length; i++) {
if (this.results[i + 1]) {
this.results[i].chain(this.results[i + 1])
}
}
this.results[0].start()
}
this.results[this.results.length - 1].onComplete(() => {
this.stopRender = true
})
}
添加动画,设置Sprite对象中的rotation属性是没有作用的,只有改变SpriteMaterial中的rotation才行,它接受的是一个弧度值
addTween(mark, from, to, duration) {
this.stopRender = false
const start = {
x: from[0],
y: from[1],
z: from[2]
}
const end = {
x: to[0],
y: to[1],
z: to[2]
}
const position = JSON.parse(JSON.stringify(start))
const render = () => {
if (this.stopRender) {
return
}
TWEEN.update()
requestAnimationFrame(render)
};
this.tweenObj = new TWEEN.Tween(position).to(end, duration)
let obj = this.tweenObj.onUpdate(() => {
mark.position.set(position.x, position.y, position.z)
let coord1 = this.get2DCoordFrom3DCoord(from) // 将三维坐标转为平面坐标
let coord2 = this.get2DCoordFrom3DCoord(to)
let angle = this.getAngle(coord1, coord2)
// 当处于第一个线段或者下一个角度与上一次保存的角度大于1°时就改变旋转角
if (!this.latestAngle || Math.abs(this.latestAngle - angle) > (Math.PI / 180)) {
this.latestAngle = angle
mark.material.rotation = this.latestAngle
}
}).easing(TWEEN.Easing.Linear.None).onStop(() => {
this.stopRender = true
})
render()
return obj
}
这里参考了经纬度求距离求与正北方向的夹角(方向角)的博客,其实这里是不是经纬度没关系,可以传入屏幕坐标来计算
getAngle(v1, v2) {
let vector1 = this.initObj(v1[0], v1[1])
let vector2 = this.initObj(v2[0], v2[1])
let dx = (vector2.m_RadLo - vector1.m_RadLo) * vector1.Ed
let dy = (vector2.m_RadLa - vector1.m_RadLa) * vector1.Ec
let angle = 0.0
angle = Math.atan(Math.abs(dx / dy)) * 180 / Math.PI
let dLo = vector2.m_Longitude - vector1.m_Longitude
let dLa = vector2.m_Latitude - vector1.m_Latitude
if (dLo > 0 && dLa <= 0) {
angle = (90 - angle) + 90
} else if (dLo <= 0 && dLa < 0) {
angle = angle + 180
} else if (dLo < 0 && dLa >= 0) {
angle = (90 - angle) + 270
}
// 所有的角度算出后都会加上180并模360,因为一开始的方向是相反的
angle = (angle + 180) % 360
return angle * (Math.PI / 180)
}
initObj(longitude, latitude) {
let obj = {
Rc: 6378137,
Rj: 6356725,
m_LoDeg: 0,
m_LoMin: 0,
m_LoSec: 0,
m_LaDeg: 0,
m_LaMin: 0,
m_LaSec: 0,
m_Longitude: 0,
m_Latitude: 0,
m_RadLo: 0,
m_RadLa: 0,
Ec: 0,
Ed: 0
}
obj.m_LoDeg = Math.round(longitude)
obj.m_LoMin = Math.round((longitude - obj.m_LoDeg) * 60)
obj.m_LoSec = (longitude - obj.m_LoDeg - obj.m_LoMin / 60) * 3600
obj.m_LaDeg = Math.round(latitude)
obj.m_LaMin = Math.round((latitude - obj.m_LaDeg) * 60)
obj.m_LaSec = (latitude - obj.m_LaDeg - obj.m_LaMin / 60) * 3600
obj.m_Longitude = longitude
obj.m_Latitude = latitude
obj.m_RadLo = longitude * Math.PI / 180
obj.m_RadLa = latitude * Math.PI / 180
obj.Ec = obj.Rj + (obj.Rc - obj.Rj) * (90 - obj.m_Latitude) / 90
obj.Ed = obj.Ec * Math.cos(obj.m_RadLa)
return obj
}
这个是将三维坐标转为平面坐标
get2DCoordFrom3DCoord(p) {
let t
let bornIdList = []
if (p.isVector3) {
p.distanceTo(this.camera.position)
p.project(this.camera)
bornIdList[0] = (p.x + 1) * this.renderer.domElement.clientWidth / 2
bornIdList[1] = (1 - p.y) * this.renderer.domElement.clientHeight / 2
bornIdList[2] = t
return t = bornIdList
}
if ($.isArray(p)) {
let a = new THREE.Vector3(p[0], p[1], p[2])
a.distanceTo(this.camera.position)
a.project(this.camera)
bornIdList[0] = (a.x + 1) * self.renderer.domElement.clientWidth / 2
bornIdList[1] = (1 - a.y) * self.renderer.domElement.clientHeight / 2
bornIdList[2] = t
a = null
return t = bornIdList
}
}