Threejs实现滴滴官⽹⾸页地球动画功能
偶然翻滴滴官⽹看到⾸页下翻第六栏(⼤概)有个绚丽的地球的三维动画,试着⽤there.js实现了下,基本实现了动画效果,不过还是有些问题;这个动画看似简单,但也⽤到好的绘制⽅法和计算,这⾥先写⼀下主要功能的实现;
(lice这个截图⼯具也是很不好⽤,每次都截到⼀半╮(╯﹏╰)╭)
⼀、 3d绘制场景的构建
绘制⼀个3d程序⾸先需要添加渲染器,场景,照相机这些元素,这⾥补充⼀个灯光;
1、渲染器
⾸先创建⼀个渲染器,参数为页⾯中的canvas元素,
渲染器的作⽤就是把3d场景的内容结合照相机渲染到页⾯中,
最后将画布背景设为⽩⾊。
const renderer = new Three.WebGLRenderer({canvas: this.$refs.thr});
renderer.setClearColor(0x000000);
2、场景
场景顾名思义,就是添加⼀个你发挥(绘制)的场地,后⾯所有绘制的元素都要添加到场景中;
cosnt scene = new Three.Scene();
3、照相机
照相机就像⼈的视觉或说从什么⾓度去看场景,看场景的位置和视线的⽅向决定了渲染到页⾯的内容。故⼀般需要设置两个参数相机位置position、视线⽅向lookAt,,在webgl其实是需要三组参数视点,观察点,和上⽅向。thresjs中好像是默认Y轴为上⽅向了,这⾥使⽤透视相机。
const camera = new THREE.PerspectiveCamera(45, 500 / 500, 1, 1500);
camera.position.set(100, 100, 1000);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(this.camera);
4、灯光
这⾥使⽤THREE.HemisphereLight光,可以更加贴近⾃然的户外光照效;
let light = new THREE.HemisphereLight(0xffffff);
light.position.set(0, 0, 200);
scene.add(light)
以上我们基本的绘制要素已添加完成,下⾯开始绘制各个⼏何体内容;
⼏何体的绘制有三部:创建⼏何体,创建材料,添加⽹格模型;
⼆、地球的绘制
threejs中提供了球体的绘制,我们只需创建⼀个球体,材料使⽤纹理贴图⽅式添加地图;
贴图图⽚资源是我从官⽹上的
const geometry = new THREE.SphereGeometry(this.radius, 100, 100); // 球体
const textureLoader = new THREE.TextureLoader(); // 创建纹理贴图
const texture = textureLoader.load(require("@/assets/map.jpg"),texture => {
let material = new THREE.MeshLambertMaterial({map: texture,transparent: true,});
let mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
});
由于图⽚加载是异步的,这⾥需等图⽚加载完成后才能创建材质;
这⾥我们就创建好了⼀个地球模型,接着还要让其转动起来;(中间为xyz坐标轴)
threejs提供了⼏何体的基本3d变换,直接使⽤rotateY(angleChange)根据时间设置其绕y轴(绿⾊轴)旋转⾓度即可;
三、球⾯坐标点的绘制
1、在绘制球⾯位置点时,需先前先看下球坐标系,确定点的位置,webgl中坐标⽅向与下图不⼀致,但是对点的表⽰⽅法是⼀致的;
球⾯上任意点可以⽤r,θ,φ表⽰,也可通过以下公式转化到直⾓坐标系中
x=rsinθcosφ.
y=rsinθsinφ.
z=rcosθ
但实际中地球位置我们⼀般也会使⽤经纬度表⽰。。。下⾯写个经纬度转坐标的⽅法。
threejs提供了THREE.Math.degToRad⽅法可以将经纬度转化为θ,φ,转化⽅法如下,这⾥为了⽅便后⾯使⽤,我直接返回⼀个3维向量;getPosition(longitude, latitude, radius = this.radius) { // 经度,纬度转换为坐标
let lg = THREE.Math.degToRad(longitude);
let lt = THREE.Math.degToRad(latitude); // 获取x,y,z坐标
let temp = radius * s(lt);
let x = temp * Math.sin(lg);
let y = radius * Math.sin(lt);
let z = temp * s(lg);
return new THREE.Vector3(x, y, z);
}
2、知道了位置的表⽰⽅法后开始绘制表⽰位置的点
根据⽰例位置点的由点和⼀个圆环组成,我们先绘制⼀个⼆维平⾯内的点和圆弧,在通过设置其位置和旋转⽅式移动到⽬标坐标位置点;(这⾥也可以绘制⼏何⼩球体来模拟)
(1)点的绘制
THREE.Shape是⽤来绘制⼆维平⾯内的形状的,设置其形状为圆弧,即可实现⼀个原点;
let shapePoint = new THREE.Shape();
shapePoint.absarc(0, 0, r - 4, 0, 2 * Math.PI, false);
let arcGeometry = new THREE.ShapeGeometry(shapePoint);
let arcMaterial = new THREE.MeshBasicMaterial({ color: 0x008080 });
let point = new THREE.Mesh(arcGeometry, arcMaterial);
(2)圆弧的绘制
let geometryLine = new THREE.Geometry();
let arc = new THREE.ArcCurve(0, 0, r, 0, 2 * Math.PI);
let points = Points(40);
geometryLine.setFromPoints(points);
let LineMateri = new THREE.LineBasicMaterial({ color: 0x20b2aa });
let line = new THREE.Line(geometryLine, LineMateri);
(3)位置的设置
position.set(pos.x, pos.y, pos.z);
(4)⼆维图形旋转⾄球⾯
THREE.Spherical()⽅法,可将坐标点转化为坐标点转化回球坐标系的偏移⾓度;
let spherical = new THREE.Spherical();
spherical.setFromCartesianCoords(pos.x, pos.y, pos.z);
设置位置点旋转
这⾥为什么要 - Math.PI / 2 是因为开始我们绘制时,平⾯是垂直于y轴的平⾯,参考下⾯这幅图;
四、接着绘制链接球⾯两点间的连线
连接两点的曲线需在球⾯上⽅,
两点间可以坐出⽆数条曲线,那么如何确定曲线,我们需⾃⼰再选择合适的参数来确定;
⾸先想的是⼆阶贝塞尔曲线,取两点的中点为控制点,但如果链接两点刚好位于球⾯的两个对称端点(两点间连线为直径)时,控制点需在⽆穷远处;故考虑使⽤三阶贝塞尔曲线,连接球⾯两点和球⼼,三点确定的⼀个平⾯如下图,
链接v1 v1,取中点c,链接oc,做⼀条射线,在射线取⼀点p,链接v1p,v2p,在v1,v2上取两点作为控制点;
js控制css3动画触发求两点的中点
getVCenter(v1, v2) {
let v = v1.add(v2);
return v.divideScalar(2);
}
获取两点间指定⽐例位置坐标
getLenVcetor(v1, v2, len) {
let v1v2Len = v1.distanceTo(v2);
return v1.lerp(v2, len / v1v2Len);
}
获取贝塞尔控制点
getBezierPoint(v0, v3) {
let angle = (v0.angleTo(v3) * 180) / Math.PI; // 0 ~ Math.PI // 计算向量夹⾓
let aLen = angle * 2.5,
hLen = angle * angle * 50;
let p0 = new THREE.Vector3(0, 0, 0); // 法线向量
let rayLine = new THREE.Ray(p0, VCenter(v0.clone(), v3.clone())); // 顶点坐标 let vtop = rayLine.at(hLen / rayLine.at(1).distanceTo(p0), vtop); // 位置
// 控制点坐标
let v1 = LenVcetor(v0.clone(), vtop, aLen);
let v2 = LenVcetor(v3.clone(), vtop, aLen);
return {
v1: v1,
v2: v2
};
},
绘制三次贝塞尔曲线
drawLine(longitude, latitude, longitude2, latitude2) {
let geometry = new THREE.Geometry(); //声明⼀个⼏何体对象Geometry
let v0 = Position(longitude, latitude, this.radius);
let v3 = Position(longitude2, latitude2, this.radius);
let { v1, v2 } = BezierPoint(v0, v3); // 三维⼆次贝赛尔曲线
let curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);
let curvePoints = Points(100);
geometry.setFromPoints(curvePoints);
let material = new THREE.LineBasicMaterial({
color: 0xff7e41
});
let line = new THREE.Line(geometry, material);
this.sport(curvePoints);
},
五、⼩球的运动轨迹
⼩球的动画我们使⽤three的帧动画,路径可以直接使⽤上⼀步中的曲线;
1、绘制⼩球
drawSportPoint(position, name) {
let box = new THREE.SphereGeometry(6, 6, 6);
let material = new THREE.MeshLambertMaterial({
color: 0x00bfff
}); //材质对象
let mesh = new THREE.Mesh(box, material);
mesh.name = name;
mesh.position.set(position.x, position.y, position.z);
return mesh;
}
2、让⼩球动起来
sport(curvePoints, index) {
const Ball = this.drawSportPoint(curvePoints[0]);
let arr = Array.from(Array(101), (v, k) => k); // ⽣成⼀个时间序列
let times = new Float32Array(arr);
let posArr = [];
curvePoints.forEach(elem => {
posArr.push(elem.x, elem.y, elem.z);
}); // 创建⼀个和时间序列相对应的位置坐标系列
let values = new Float32Array(posArr); // 创建⼀个帧动画的关键帧数据,曲线上的位置序列对应⼀个时间序列
let posTrack = new THREE.KeyframeTrack("Ball.position", times, values);
let duration = 101;
let clip = new THREE.AnimationClip("default", duration, [posTrack]);
this.mixer = new THREE.AnimationMixer(Ball);
let AnimationAction = this.mixer.clipAction(clip);
AnimationAction.timeScale = 20;
AnimationAction.play();
}
3、在requestAnimationFrame中添加触发动画
this.mixer.update(Delta());
到此这篇关于Threejs实现滴滴官⽹⾸页地球动画的⽂章就介绍到这了,更多相关Threejs滴滴官⽹⾸页地球动画内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论