这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。下面这篇文章主要给大家介绍了利用 three.js 如何绘制地球、飞机与轨迹的效果,文中给出了详细的示例代码,需要的朋友们可以参考借鉴,下面来一起看看吧。
对于 three.js 不太熟悉的朋友们可以参考这篇文章,threejs 官网:https://threejs.org/
首先我们来看下要实现的效果
这个缩小后的图片,下面我们来看下近距离的动态效果。。
效果比较简陋,需要后期再处理。。。
下面进入主题,代码篇。。
HTML 部分:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>
- 全球航班
- </title>
- <style>
- html{overflow: hidden;} body { margin: 0;}
- </style>
- <script src="js/jquery.min.js">
- </script>
- </head>
- <body>
- <!-- 地国 -->
- <div id="zh_globe_container">
- </div>
- <!-- 容器 -->
- <script src="js/threejs/Detector.js">
- </script>
- <!-- webGL浏览器支持检测 -->
- <script src="js/threejs/three.min.js">
- </script>
- <!-- 核心js -->
- <script src="js/threejs/stats.min.js">
- </script>
- <!-- 性能测试 -->
- <script src="js/threejs/OrbitControls.js">
- </script>
- <!-- 地球控制 -->
- <script src="js/socketio-1.4.5.js">
- </script>
- <!-- socket -->
- <script src="js/globe.js">
- </script>
- <!-- -->
- </body>
- </html>
JS 部分 (globe.js)
1、实现地球
地球贴图 (可以在网上下载)
- // 地球
- function globe() {
- var globeTextureLoader = new THREE.TextureLoader();
- globeTextureLoader.load('images/textures/earth.jpg',
- function(texture) {
- var globeGgeometry = new THREE.SphereGeometry(200, 100, 100);
- var globeMaterial = new THREE.MeshStandardMaterial({
- map: texture
- });
- var globeMesh = new THREE.Mesh(globeGgeometry, globeMaterial);
- group.add(globeMesh);
- group.rotation.x = THREE.Math.degToRad(35);
- group.rotation.y = THREE.Math.degToRad(170);
- });
- }
2、添加球面光源 (这里使用的是半球光)
- // 光
- function lights() {
- var hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x333333, 2);
- hemisphereLight.position.x = 0;
- hemisphereLight.position.y = 0;
- hemisphereLight.position.z = -200;
- group.add(hemisphereLight);
- }
3、添加星点
- // 星点
- function stars() {
- var starsGeometry = new THREE.Geometry();
- for (var i = 0; i < 2000; i ++) {
- var starVector = new THREE.Vector3(
- THREE.Math.randFloatSpread(2000),
- THREE.Math.randFloatSpread(2000),
- THREE.Math.randFloatSpread(2000)
- );
- starsGeometry.vertices.push(starVector);
- }
- var starsMaterial = new THREE.PointsMaterial({color: 0x888888})
- var starsPoint = new THREE.Points(starsGeometry, starsMaterial);
- group.add(starsPoint);
- }
4、添加飞机
这里需要我们把 经纬度坐标 转成 xyz 坐标
- // 获取position
- function getPosition(lng, lat, alt) {
- var phi = (90 - lat) * (Math.PI / 180),
- theta = (lng + 180) * (Math.PI / 180),
- radius = alt + 200,
- x = -(radius * Math.sin(phi) * Math.cos(theta)),
- z = (radius * Math.sin(phi) * Math.sin(theta)),
- y = (radius * Math.cos(phi));
- return {
- x: x,
- y: y,
- z: z
- };
- }
画飞机
- // 飞机形状(不想画的,可以下载个 飞机模型 使用加载器加载进来)
- var planeShape = new THREE.Shape();
- planeShape.moveTo(0, 0);
- planeShape.lineTo(0.2, -0.2);
- planeShape.lineTo(0.2, -1.3);
- planeShape.lineTo(1.6, -2.7);
- planeShape.lineTo(1.6, -3);
- planeShape.lineTo(0.2, -2.1);
- planeShape.lineTo(0.2, -3);
- planeShape.lineTo(0.5, -3.4);
- planeShape.lineTo(0.5, -3.7);
- planeShape.lineTo(0, -3.3);
- planeShape.lineTo( - 0.5, -3.7);
- planeShape.lineTo( - 0.5, -3.4);
- planeShape.lineTo( - 0.2, -3);
- planeShape.lineTo( - 0.2, -2.1);
- planeShape.lineTo( - 1.6, -3);
- planeShape.lineTo( - 1.6, -2.7);
- planeShape.lineTo( - 0.2, -1.3);
- planeShape.lineTo( - 0.2, -0.2);
- var planeGeometry = new THREE.ShapeGeometry(planeShape);
- // 飞机材质
- var planeMaterial = new THREE.MeshPhongMaterial({
- color: 0x0FB4DD,
- side: THREE.DoubleSide,
- depthTest: true
- });
depthTest 作用是能否透过球体看到飞机,如果是 false 则旋转到球体另一面也能看到飞机
添加飞机
- // 添加飞机
- function addPlane(item) {
- if(item.anum && item.lng && item.lat) {
- var plane = new THREE.Mesh(planeGeometry, planeMaterial);
- // 旋转
- plane.rotation.z = THREE.Math.degToRad(item.ang);
- // 定位
- var position = getPosition(item.lng, item.lat, 5);
- plane.position.set(position.x, position.y, position.z);
- // 显示/隐藏
- // plane.visible = false;
- // 保存
- planeMarkers[item.anum] = plane;
- // 添加到场景
- group.add(plane);
- // 绘制历史轨迹
- drawHistoryTrack(item.anum);
- }
- }
绘制轨迹 (使用 socket 来获取的飞行轨迹经纬度坐标点)
- // 时间段
- var curTime = Date.parse(new Date()) / 1000;
- var depTime = curTime - 30 * 60;
- // 轨迹线质
- var trackMaterial = new THREE.LineBasicMaterial({
- color: 0x1B94B1
- });
- // 绘制历史轨迹
- function drawHistoryTrack(anum) {
- socket.emit("fullPath", anum, depTime, curTime,
- function(status, data) {
- if (status) {
- var dLength = data.length;
- if (dLength >= 2) {
- var trackCoordArr = [];
- for (var i = 0; i < dLength; i++) {
- if (data[i].lng && data[i].alt) {
- trackCoordArr.push({
- lng: data[i].lng,
- lat: data[i].lat
- });
- }
- }
- var tcaLength = trackCoordArr.length;
- if (tcaLength >= 2) {
- var tcaHalfLength = Math.ceil(tcaLength / 2),
- vertexArr = [];
- // 这里只取了三个点(起点、中点、终点)
- var p1 = getPosition(trackCoordArr[0].lng, trackCoordArr[0].lat, 0),
- p2 = getPosition(trackCoordArr[tcaHalfLength].lng, trackCoordArr[tcaHalfLength].lat, tcaLength * 0.01),
- p3 = getPosition(trackCoordArr[tcaLength - 1].lng, trackCoordArr[tcaLength - 1].lat, 0);
- var trackCurve = new THREE.CatmullRomCurve3([new THREE.Vector3(p1.x, p1.y, p1.z), new THREE.Vector3(p2.x, p2.y, p2.z), new THREE.Vector3(p3.x, p3.y, p3.z)]);
- var trackGeometry = new THREE.Geometry(),
- verticesArr = trackCurve.getPoints(tcaLength);
- trackGeometry.vertices = verticesArr;
- var trackLine = new THREE.Line(trackGeometry, trackMaterial);
- group.add(trackLine);
- // 动画点
- addLightPoint(p1, tcaLength, verticesArr);
- }
- }
- }
- });
- }
如果要绘制所有点,且头尾是在球面上的曲线,则需要两次循环
- var tcaRemainLength = tcaLength-tcaHalfLength
- for(var j=0; j<tcaHalfLength; j++) { // 前一半
- var p1 = getPosition(trackCoordArr[j].lng, trackCoordArr[j].lat, j*0.05);
- vertexArr.push(new THREE.Vector3(p1.x, p1.y, p1.z));
- }
- for(var k=tcaRemainLength; k>0; k--) { // 后一半
- var p2 = getPosition(trackCoordArr[tcaLength-k].lng, trackCoordArr[tcaLength-k].lat, k*0.05);
- vertexArr.push(new THREE.Vector3(p2.x, p2.y, p2.z));
- }
- var trackCurve = new THREE.CatmullRomCurve3(vertexArr);
这个部分看看就行了。。
光点动画
- // 点动画
- var pointGeometry = new THREE.SphereGeometry(0.2, 20, 20);
- var pointMaterial = new THREE.MeshBasicMaterial({
- color: 0x40E0D0
- });
- function addLightPoint(pos, coordsNum, verArr) {
- var pointMesh = new THREE.Mesh(pointGeometry, pointMaterial);
- pointMesh.position.set(pos.x, pos.y, pos.z);
- group.add(pointMesh);
- var index = 0;
- function pointAnimate() {
- index++;
- if (index > coordsNum) {
- index = 0;
- }
- pointMesh.position.set(verArr[index].x, verArr[index].y, verArr[index].z);
- requestAnimationFrame(pointAnimate);
- }
- pointAnimate();
- }
这个点使用的是 sphere,,当然也可以用顶点来实现,如下
- var geometry = new THREE.Geometry();
- geometry.vertices.push(new THREE.Vector3(0, 0, 0)) geometry.colors.push(new THREE.Color(0xffffff));
- var material = new THREE.PointsMaterial({
- size: 1,
- vertexColors: THREE.VertexColors,
- opacity: 0.75,
- sizeAttenuation: true,
- transparent: true
- });
- var point = new THREE.Points(geometry, material);
- point.position.set(x, y, z);
- group.add(point);
另外不想用光点动画的话,也可以用线动画,实现原理是不断更新顶点坐标,如下
- var curveGeometry = new THREE.Geometry();
- var curveData = new THREE.CatmullRomCurve3(verArr.slice(0, 10));
- curveGeometry.vertices = curveData.getPoints(10);
- var curveMaterial = new THREE.LineBasicMaterial({
- color: 0x40E0D0
- });
- var curveLine = new THREE.Line(curveGeometry, curveMaterial);
- group.add(curveLine);
- var index = 0;
- function lineAnimate() {
- index++;
- if (index > coordsNum - 10) {
- index = 0;
- }
- var offsetData = verArr.slice(index, 10 + index);
- if (offsetData.length > 0) {
- curveData = new THREE.CatmullRomCurve3(offsetData);
- curveLine.geometry.vertices = curveData.getPoints(10);
- curveLine.geometry.verticesNeedUpdate = true;
- }
- requestAnimationFrame(lineAnimate);
- }
- lineAnimate();
最后就是布置场景和事件了
- // 初始化
- function init() {
- container = document.getElementById('zh_globe_container');
- scene = new THREE.Scene();
- var bgTexture = new THREE.TextureLoader().load("images/textures/starfield.jpg");
- scene.background = bgTexture;
- camera = new THREE.PerspectiveCamera(50, winWth / winHgt, 1, 2000);
- camera.up.x = 0;
- camera.up.y = 1;
- camera.up.z = 0;
- camera.position.x = 0;
- camera.position.y = 0;
- camera.position.z = 400;
- camera.lookAt(0, 0, 0);
- group = new THREE.Group();
- scene.add(group);
- // 地球
- globe();
- // 飞机
- plane();
- // 星点
- stars();
- // 半球光
- lights();
- // 渲染器
- renderer = new THREE.WebGLRenderer({
- antialias: true,
- preserveDrawingBuffer: true
- });
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(winWth, winHgt);
- container.appendChild(renderer.domElement);
- // 盘旋控制
- var orbitControl = new THREE.OrbitControls(camera, renderer.domElement);
- orbitControl.minDistrance = 20;
- orbitControl.maxDistrance = 50;
- orbitControl.maxPolarAngle = Math.PI / 2;
- // 性能测试
- stats = new Stats();
- container.appendChild(stats.dom);
- // resize事件
- window.addEventListener('resize', onWindowResize, false);
- }
- // 窗口大小改变
- function onWindowResize() {
- camera.aspect = window.innerWidth / window.innerHeight;
- camera.updateProjectionMatrix();
- renderer.setSize(window.innerWidth, window.innerHeight);
- }
- // 渲染
- function render() {
- group.rotation.y -= 0.0005;
- renderer.render(scene, camera);
- }
- // 动画
- function animate() {
- requestAnimationFrame(animate);
- render();
- stats.update();
- }
- init();
- animate();
完整代码:
- var log = console.log.bind(console);
- var globeObj = (function() {
- 'use strict';
- // 判断浏览器是否支持webgl
- if (!Detector.webgl) Detector.addGetWebGLMessage();
- var container, stats;
- var camera, scene, renderer;
- var group;
- var mouseX = 0,
- mouseY = 0;
- var winWth = window.innerWidth,
- winHgt = window.innerHeight;
- // 获取position
- function getPosition(lng, lat, alt) {
- var phi = (90 - lat) * (Math.PI / 180),
- theta = (lng + 180) * (Math.PI / 180),
- radius = alt + 200,
- x = -(radius * Math.sin(phi) * Math.cos(theta)),
- z = (radius * Math.sin(phi) * Math.sin(theta)),
- y = (radius * Math.cos(phi));
- return {
- x: x,
- y: y,
- z: z
- };
- }
- // 飞机
- function plane() {
- var socket = io('https://loc.variflight.com/*****此处接口地址不能给了', {
- transports: ['websocket']
- });
- var clientBounds = [52.793056, 72.427908, 2.970897, 135.181814];
- // 连接
- socket.on('connect',
- function() {
- socket.emit("sub", clientBounds, -1, '',
- function() {});
- });
- // 飞机标记
- var planeMarkers = {};
- // 飞机形状
- var planeShape = new THREE.Shape();
- planeShape.moveTo(0, 0);
- planeShape.lineTo(0.2, -0.2);
- planeShape.lineTo(0.2, -1.3);
- planeShape.lineTo(1.6, -2.7);
- planeShape.lineTo(1.6, -3);
- planeShape.lineTo(0.2, -2.1);
- planeShape.lineTo(0.2, -3);
- planeShape.lineTo(0.5, -3.4);
- planeShape.lineTo(0.5, -3.7);
- planeShape.lineTo(0, -3.3);
- planeShape.lineTo( - 0.5, -3.7);
- planeShape.lineTo( - 0.5, -3.4);
- planeShape.lineTo( - 0.2, -3);
- planeShape.lineTo( - 0.2, -2.1);
- planeShape.lineTo( - 1.6, -3);
- planeShape.lineTo( - 1.6, -2.7);
- planeShape.lineTo( - 0.2, -1.3);
- planeShape.lineTo( - 0.2, -0.2);
- var planeGeometry = new THREE.ShapeGeometry(planeShape);
- // 飞机材质
- var planeMaterial = new THREE.MeshPhongMaterial({
- color: 0x0FB4DD,
- side: THREE.DoubleSide,
- depthTest: true
- });
- // 添加飞机
- function addPlane(item) {
- if (item.anum && item.lng && item.lat) {
- var plane = new THREE.Mesh(planeGeometry, planeMaterial);
- // 旋转
- plane.rotation.z = THREE.Math.degToRad(item.ang);
- // 定位
- var position = getPosition(item.lng, item.lat, 5);
- plane.position.set(position.x, position.y, position.z);
- // 显示/隐藏
- // plane.visible = false;
- // 保存
- planeMarkers[item.anum] = plane;
- // 添加到场景
- group.add(plane);
- // 绘制历史轨迹
- drawHistoryTrack(item.anum);
- }
- }
- // 时间段
- var curTime = Date.parse(new Date()) / 1000;
- var depTime = curTime - 30 * 60;
- // 轨迹线质
- var trackMaterial = new THREE.LineBasicMaterial({
- color: 0x1B94B1
- });
- // 绘制历史轨迹
- function drawHistoryTrack(anum) {
- socket.emit("fullPath", anum, depTime, curTime,
- function(status, data) {
- if (status) {
- var dLength = data.length;
- if (dLength >= 2) {
- var trackCoordArr = [];
- for (var i = 0; i < dLength; i++) {
- if (data[i].lng && data[i].alt) {
- trackCoordArr.push({
- lng: data[i].lng,
- lat: data[i].lat
- });
- }
- }
- var tcaLength = trackCoordArr.length;
- if (tcaLength >= 2) {
- var tcaHalfLength = Math.ceil(tcaLength / 2),
- tcaRemainLength = tcaLength - tcaHalfLength,
- vertexArr = [];
- /* 所有点
- for(var j=0; j<tcaHalfLength; j++) {
- var p1 = getPosition(trackCoordArr[j].lng, trackCoordArr[j].lat, j*0.05);
- vertexArr.push(new THREE.Vector3(p1.x, p1.y, p1.z));
- }
- for(var k=tcaRemainLength; k>0; k--) {
- var p2 = getPosition(trackCoordArr[tcaLength-k].lng, trackCoordArr[tcaLength-k].lat, k*0.05);
- vertexArr.push(new THREE.Vector3(p2.x, p2.y, p2.z));
- }
- var trackCurve = new THREE.CatmullRomCurve3(vertexArr);
- */
- // 三个点
- var p1 = getPosition(trackCoordArr[0].lng, trackCoordArr[0].lat, 0),
- p2 = getPosition(trackCoordArr[tcaHalfLength].lng, trackCoordArr[tcaHalfLength].lat, tcaLength * 0.01),
- p3 = getPosition(trackCoordArr[tcaLength - 1].lng, trackCoordArr[tcaLength - 1].lat, 0);
- var trackCurve = new THREE.CatmullRomCurve3([new THREE.Vector3(p1.x, p1.y, p1.z), new THREE.Vector3(p2.x, p2.y, p2.z), new THREE.Vector3(p3.x, p3.y, p3.z)]);
- var trackGeometry = new THREE.Geometry(),
- verticesArr = trackCurve.getPoints(tcaLength);
- trackGeometry.vertices = verticesArr;
- var trackLine = new THREE.Line(trackGeometry, trackMaterial);
- group.add(trackLine);
- // 动画点
- addLightPoint(p1, tcaLength, verticesArr);
- }
- }
- }
- });
- }
- // 点动画
- var pointGeometry = new THREE.SphereGeometry(0.2, 20, 20);
- var pointMaterial = new THREE.MeshBasicMaterial({
- color: 0x40E0D0
- });
- function addLightPoint(pos, coordsNum, verArr) {
- var pointMesh = new THREE.Mesh(pointGeometry, pointMaterial);
- pointMesh.position.set(pos.x, pos.y, pos.z);
- group.add(pointMesh);
- var index = 0;
- function pointAnimate() {
- index++;
- if (index > coordsNum) {
- index = 0;
- }
- pointMesh.position.set(verArr[index].x, verArr[index].y, verArr[index].z);
- requestAnimationFrame(pointAnimate);
- }
- pointAnimate();
- /*var curveGeometry = new THREE.Geometry();
- var curveData = new THREE.CatmullRomCurve3(verArr.slice(0, 10));
- curveGeometry.vertices = curveData.getPoints(10);
- var curveMaterial = new THREE.LineBasicMaterial({color: 0x40E0D0});
- var curveLine = new THREE.Line(curveGeometry, curveMaterial);
- group.add(curveLine);
- var index = 0;
- function lineAnimate() {
- index++;
- if(index>coordsNum-10) {
- index = 0;
- }
- var offsetData = verArr.slice(index, 10+index);
- if(offsetData.length > 0) {
- curveData = new THREE.CatmullRomCurve3(offsetData);
- curveLine.geometry.vertices = curveData.getPoints(10);
- curveLine.geometry.verticesNeedUpdate = true;
- }
- requestAnimationFrame(lineAnimate);
- }
- lineAnimate();*/
- }
- // 监听数据(添加并更新)
- socket.on('~',
- function(res) {
- if ($.isEmptyObject(planeMarkers)) {
- $.each(res,
- function(i, item) {
- addPlane(item);
- });
- } else {
- $.each(res,
- function(i, item) {
- if (planeMarkers[item.anum]) {
- if (item.lng && item.lat) {
- var pos = getPosition(item.lng, item.lat, 5);
- planeMarkers[item.anum].position.set(pos.x, pos.y, pos.z);
- }
- } else {
- addPlane(item);
- }
- });
- }
- });
- }
- // 地球
- function globe() {
- var globeTextureLoader = new THREE.TextureLoader();
- globeTextureLoader.load('images/textures/earth.jpg',
- function(texture) {
- var globeGgeometry = new THREE.SphereGeometry(200, 100, 100);
- var globeMaterial = new THREE.MeshStandardMaterial({
- map: texture
- });
- var globeMesh = new THREE.Mesh(globeGgeometry, globeMaterial);
- group.add(globeMesh);
- group.rotation.x = THREE.Math.degToRad(35);
- group.rotation.y = THREE.Math.degToRad(170);
- });
- }
- // 星点
- function stars() {
- var starsGeometry = new THREE.Geometry();
- for (var i = 0; i < 2000; i++) {
- var starVector = new THREE.Vector3(THREE.Math.randFloatSpread(2000), THREE.Math.randFloatSpread(2000), THREE.Math.randFloatSpread(2000));
- starsGeometry.vertices.push(starVector);
- }
- var starsMaterial = new THREE.PointsMaterial({
- color: 0x888888
- }) var starsPoint = new THREE.Points(starsGeometry, starsMaterial);
- group.add(starsPoint);
- }
- // 光
- function lights() {
- var hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x333333, 2);
- hemisphereLight.position.x = 0;
- hemisphereLight.position.y = 0;
- hemisphereLight.position.z = -200;
- group.add(hemisphereLight);
- }
- // 初始化
- function init() {
- container = document.getElementById('zh_globe_container');
- scene = new THREE.Scene();
- var bgTexture = new THREE.TextureLoader().load("images/textures/starfield.jpg");
- scene.background = bgTexture;
- camera = new THREE.PerspectiveCamera(50, winWth / winHgt, 1, 2000);
- camera.up.x = 0;
- camera.up.y = 1;
- camera.up.z = 0;
- camera.position.x = 0;
- camera.position.y = 0;
- camera.position.z = 400;
- camera.lookAt(0, 0, 0);
- group = new THREE.Group();
- scene.add(group);
- // 地球
- globe();
- // 飞机
- plane();
- // 星点
- stars();
- // 半球光
- lights();
- // 渲染器
- renderer = new THREE.WebGLRenderer({
- antialias: true,
- preserveDrawingBuffer: true
- });
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(winWth, winHgt);
- container.appendChild(renderer.domElement);
- // 盘旋控制
- var orbitControl = new THREE.OrbitControls(camera, renderer.domElement);
- orbitControl.minDistrance = 20;
- orbitControl.maxDistrance = 50;
- orbitControl.maxPolarAngle = Math.PI / 2;
- // 性能测试
- stats = new Stats();
- container.appendChild(stats.dom);
- // resize事件
- window.addEventListener('resize', onWindowResize, false);
- }
- // 窗口大小改变
- function onWindowResize() {
- camera.aspect = window.innerWidth / window.innerHeight;
- camera.updateProjectionMatrix();
- renderer.setSize(window.innerWidth, window.innerHeight);
- }
- // 渲染
- function render() {
- group.rotation.y -= 0.0005;
- renderer.render(scene, camera);
- }
- // 动画
- function animate() {
- requestAnimationFrame(animate);
- render();
- stats.update();
- }
- init();
- animate();
- })();
场景背景图
总结
以上就是关于利用 three.js 绘制地球、飞机、轨迹的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
(adsbygoogle = window.adsbygoogle || []).push({});
来源: http://www.phperz.com/article/17/0703/327205.html