前端效果积累 | 酷炫、实用3D地球路径飞行效果实现
📌个人主页:个人主页
🧀 推荐专栏:前端开发成神之路 --【这是一个为想要入门和进阶前端开发专门开启的精品专栏
!从个人到商业的全套开发教程
,实打实的干货分享,确定不来看看? 😻😻】
📝作者简介:从web开发,再到大数据算法,踩过了无数的坑,用心总结经验教训,助你在技术生涯一臂之力!若想获取更多精彩内容,敬请订阅专栏或者关注😁😂🤣😃😆😉😊😋😍😘🥰
⭐️您的小小关注是我持续输出的动力!
⭐️
干货内容推荐
🥇入门和进阶小程序开发,不可错误的精彩内容🥇 :
- 《小程序开发必备功能的吐血整理【个人中心界面样式大全】》
- 《微信小程序 | 动手实现双十一红包雨》
- 《微信小程序 | 人脸识别的最终解决方案》
- 《来接私活吧?小程序接私活必备功能-婚恋交友【附完整代码】》
- 《吐血整理的几十款小程序登陆界面【附完整代码】》
文章目录
- 干货内容推荐
- 一、效果预览
- 二、技术要点及用途
- 三、完整代码
一、效果预览
二、技术要点及用途
这样的3D地球飞行线效果,其主要的应用场景在于酷炫的数据报表大屏!
在日常的开发工作中,搭建数据大屏其实是很多前端工程师都必须经历的一个基础需求!可能你觉得这不就是拿百度的Eachart组件搭建一下不就可以了!这放在过去几年,这个需求说不定还存在!
大哥,现在都是2023年了,数字孪生
这个热词被炒的沸沸扬扬。可以见的现在的数据大屏报表需求逐步往3D化、场景可视化、从操控一体化的方向发展了!所以说,现在要搞定酷炫的数据可视化仅仅是一个前端工程师加一个UI设计可能已经是跟不上配置了!一个数字孪生效果的建立,可能需要用到建模师、工程测量、电子工程师等依托于场景的各类专业技术人才!详细的数据可视化技术积累可以看我另一篇文章:超干货!数据可视化最全解决方案!酷炫效果分分钟拿捏!【附全网高质量学习资料】
本文中的技术要点无非是:Three.js
。通过three.js构建3D空间效果,同时设置不同的点,再将不同的点用线进行连接!
三、完整代码
<div id="container"></div>
<script src="https://wow.techbrood.com/uploads/user_upload/56822/three.r111.js"></script>
<script src="https://wow.techbrood.com/uploads/user_upload/56822/OrbitControls.js"></script>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute float percent;
uniform float time;
uniform float number;
uniform float speed;
uniform float length;
varying float opacity;
uniform float size;
void main()
{
float l = clamp(1.0-length, 0.0, 1.0);
gl_PointSize = clamp(fract(percent*number + l - time*number*speed)-l, 0.0, 1.) * size * (1./length);
opacity = gl_PointSize/size;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
varying float opacity;
uniform vec3 color;
void main(){
if (opacity <=0.2){
discard;
}
gl_FragColor = vec4(color, 1.0);
}
</script>
<script type="text/javascript">
var container = document.getElementById('container');
var scene, camera, renderer;
var globeMesh = undefined;
var groupDots, groupLines, groupAnimDots;
var controls;
var _PI2 = Math.PI * 2; // 弧度的取值为0-2π
// 配置参数
var params = {
globeRadius: 100, // 地球半径
pointsLength: 20, // 点数
}
var commonUniforms = {
time: {
value: 0
},
number: {
value: 1
},
speed: {
value: 0.5
},
length: {
value: 0.4
},
size: {
value: 4
}
};
var vertexShader = document.getElementById('vertex-shader').innerHTML;
var fragmentShader = document.getElementById('fragment-shader').innerHTML;
// 预制件
var Prefab = {
Sphere: (function() {
var instance;
return function(clone = true) {
if (!instance) {
instance = new createSphere();
}
if (clone) return instance.clone();
else return instance;
}
})()
}
init();
update();
function init() {
// 场景
scene = new THREE.Scene();
groupDots = new THREE.Group();
groupLines = new THREE.Group();
groupAnimDots = new THREE.Group();
scene.add(groupDots, groupLines, groupAnimDots);
// 相机
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.x = -200;
camera.position.y = 200;
camera.position.z = -200;
camera.lookAt(scene.position);
// 渲染器
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
// 光
var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
scene.add(light);
// 控制器
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 200;
controls.maxDistance = 400;
controls.rotateSpeed = 0.5;
controls.enableDamping = true;
controls.enablePan = false;
initGlobe();
initFlyLines();
window.addEventListener('resize', onWindowResize, false);
}
function update() {
requestAnimationFrame(update);
renderer.render(scene, camera);
commonUniforms.time.value += 0.01;
controls.update();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function initGlobe() {
// 地球
var geo = new THREE.SphereGeometry(params.globeRadius, 32, 32);
var texture = new THREE.TextureLoader().load('https://wow.techbrood.com/uploads/user_upload/56822/earth.jpg');
texture.minFilter = THREE.LinearFilter;
var material = new THREE.MeshPhongMaterial({
map: texture,
// wireframe: true
});
globeMesh = new THREE.Mesh(geo, material);
scene.add(globeMesh);
}
// 地球飞线和点
function initFlyLines() {
// 球面随机点
for (let i = 0; i < params.pointsLength; i++) {
addPoints(groupDots, params.globeRadius);
}
// 点到点生成曲线
groupDots.children.forEach(function(elem, index) {
if (elem != groupDots.children[0]) {
addLines(groupDots.children[index - 1].position, elem.position);
}
});
}
// 3d球面取点
function getEarthPos(radius, a, b) {
var x = radius * Math.sin(a) * Math.cos(b);
var y = radius * Math.sin(a) * Math.sin(b);
var z = radius * Math.cos(a);
return {
x, y, z
};
}
// 添加随机点
function addPoints(group, radius) {
var mesh = new Prefab.Sphere();
var pos = getEarthPos(radius, _PI2 * Math.random(), _PI2 * Math.random());
mesh.position.set(pos.x, pos.y, pos.z);
group.add(mesh);
}
function addLines(v0, v3) {
var angle = v0.angleTo(v3);
var vtop = v0.clone().add(v3);
vtop = vtop.normalize().multiplyScalar(params.globeRadius);
var n;
if (angle <= 1) {
n = params.globeRadius / 5 * angle;
} else if (angle > 1 && angle < 2) {
n = params.globeRadius / 5 * Math.pow(angle, 2);
} else {
n = params.globeRadius / 5 * Math.pow(angle, 1.5);
}
var v1 = v0.clone().add(vtop).normalize().multiplyScalar(params.globeRadius + n);
var v2 = v3.clone().add(vtop).normalize().multiplyScalar(params.globeRadius + n);
// addLineHelper(globeMesh.position, v1);
// addLineHelper(globeMesh.position, v2);
// addLineHelper(globeMesh.position, vtop)
// 三维三次贝塞尔曲线(v0起点,v1第一个控制点,v2第二个控制点,v3终点)
var curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);
var points = curve.getPoints(500);
var geometry = new THREE.BufferGeometry().setFromPoints(points);
let length = points.length;
var percents = new Float32Array(length);
for (let i = 0; i < length; i += 1) {
percents[i] = (i / length);
}
geometry.setAttribute('percent', new THREE.BufferAttribute(percents, 1));
var material = createLineMaterial();
var flyLine = new THREE.Points(geometry, material);
groupLines.add(flyLine);
}
function createLineMaterial() {
let uniforms = {
time: commonUniforms.time,
number: commonUniforms.number,
speed: commonUniforms.speed,
length: commonUniforms.length,
size: commonUniforms.size,
color: {
value: new THREE.Color(Math.random(), Math.random(), Math.random())
}
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
transparent: true,
blending: THREE.AdditiveBlending,
});
return material;
}
// 小球
function createSphere() {
var geometry = new THREE.SphereBufferGeometry(1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ffff
});
var mesh = new THREE.Mesh(geometry, material);
return mesh;
}
// 贝塞尔曲线辅助点
function addPointHelper(pos) {
var mesh = new Prefab.Sphere();
mesh.material = new THREE.MeshBasicMaterial({
color: 0xff0000
});
mesh.position.copy(pos);
scene.add(mesh);
}
// 贝塞尔曲线辅助线
function addLineHelper(pos1, pos2) {
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var geometry = new THREE.Geometry();
geometry.vertices.push(pos1, pos2);
var line = new THREE.Line(geometry, material);
scene.add(line);
}
</script>
<style type="text/css">
html,
body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>