ThreeJS 地球添加迁徙图

环境

  • ThreeJS 107版本
  • three.min.js
  • OrbitControls.js
  • THREE.MeshLine.js

说明

迁徙图参考了网上大大们的方法做的,但是效果不太理想,迁徙飞行效果原理是生成50个小球循环飞,数据量一大有点卡,需要优化。

解决方案

  1. 创建球的过程参见"ThreeJS制作地球"

  2. 创建点位group,考虑后面会做删除功能,所以把所有的实体都以group组为单位添加,后续方便做删除

// 标记点组合
var marking = new THREE.Group();
  1. 根据数据,在地球上添加飞出、飞入的点位,并且绘制贝塞尔曲线
var groupLines = new THREE.Group();
for (var i = 0; i < _flyData.length; i++) {
index = i;
for (var j = 0; j < _flyData[i].length; j++) {
var ballPosFrom, ballPosTo;

// 创建标记点球体
var ballFrom = new THREE.Mesh(new THREE.SphereGeometry(0.5, 30, 30), new THREE.MeshBasicMaterial({
color: ballColors[index % 3]//'#1bb4b0'
}));
// 获取标记点坐标
ballPosFrom = this.getPosition(getPositionByName(_flyData[i][j][0].name)[1][0] + 90, getPositionByName(_flyData[i][j][0].name)[1][1], 30);
ballFrom.position.set(ballPosFrom.x, ballPosFrom.y, ballPosFrom.z);
marking.add(ballFrom);

// 创建标记点球体
var ballTo = new THREE.Mesh(new THREE.SphereGeometry(0.5, 30, 30), new THREE.MeshBasicMaterial({
color: ballColors[index % 3]//'#1bb4b0'
}));
// 获取标记点坐标
ballPosTo = this.getPosition(getPositionByName(_flyData[i][j][1].name)[1][0] + 90, getPositionByName(_flyData[i][j][1].name)[1][1], 30);
ballTo.position.set(ballPosTo.x, ballPosTo.y, ballPosTo.z);
marking.add(ballTo);

// 添加飞线
var line = addLine(ballFrom.position, ballTo.position);//迁徙方向,第一个参数是起始方向
groupLines.add(line.lineMesh);
animateDots.push(line.curve.getPoints(150));
}
}
scene.add(marking);
scene.add(groupLines);
  1. 将经纬度转换成球上坐标
//经纬度转球坐标
this.getPosition = function (_longitude, _latitude, _radius) {
var lg = THREE.Math.degToRad(_longitude);
var lt = THREE.Math.degToRad(_latitude);
var temp = _radius * Math.cos(lt);
var x = temp * Math.sin(lg);
var y = _radius * Math.sin(lt);
var z = temp * Math.cos(lg);
return {
x: x,
y: y,
z: z
}
}
  1. 绘制贝塞尔曲线
function animationLine() {
aGroup.children.forEach(function (elem, index) {
var _index = parseInt(index / 50);
var index2 = index - 50 * _index;
var _vIndex = 0;
if (firstBool) {
_vIndex = vIndex - index2 % 50 >= 0 ? vIndex - index2 % 50 : 0;
} else {
_vIndex = vIndex - index2 % 50 >= 0 ? vIndex - index2 % 50 : 150 + vIndex - index2;
}
var v = animateDots[_index][_vIndex];
elem.position.set(v.x, v.y, v.z);
})
vIndex++;
if (vIndex > 150) {
vIndex = 0;
}
if (vIndex == 150 && firstBool) {
firstBool = false;
}
requestAnimationFrame(animationLine);
}
// 计算球体上两个点的中点
function getVCenter(v1, v2) {
var v = v1.add(v2);
return v.divideScalar(2);
}
// 计算球体两点向量固定长度的点
function getLenVcetor(v1, v2, len) {
var v1v2Len = v1.distanceTo(v2);
return v1.lerp(v2, len / v1v2Len);
}
// 添加轨迹函数
function addLine(v0, v3) {
var angleRate = _style.angleRate ? _style.angleRate : 0.5;
var angle = (v0.angleTo(v3) * 180) / Math.PI;
var aLen = angle * angleRate * (1 - angle / (Math.PI * 90));
var hLen = angle * angle * 1.2 * (1 - angle / (Math.PI * 90));
var p0 = new THREE.Vector3(0, 0, 0);
// 法线向量
var rayLine = new THREE.Ray(p0, getVCenter(v0.clone(), v3.clone()));
// 顶点坐标
var vtop = rayLine.at(hLen / rayLine.at(1).distanceTo(p0));
// 控制点坐标
var v1 = getLenVcetor(v0.clone(), vtop, aLen);
var v2 = getLenVcetor(v3.clone(), vtop, aLen);
// 绘制贝塞尔曲线
var curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);
var geometry = new THREE.Geometry();
geometry.vertices = curve.getPoints(100);
var line = new MeshLine();
line.setGeometry(geometry);
var material = new MeshLineMaterial({
color: lineColors[index % 3],
lineWidth: _style.lineWidth ? _style.lineWidth : 0.1,
transparent: true,
opacity: 1
})
return {
curve: curve,
lineMesh: new THREE.Mesh(line.geometry, material)
}
}
  1. 构造循环动画的小球
// 线上滑动的小球
var aGroup = new THREE.Group();
for (var i = 0; i < animateDots.length; i++) {
for (var j = 0; j < 50; j++) {
var aGeo = new THREE.SphereGeometry(0.2, 10, 10);
var aMaterial = new THREE.MeshBasicMaterial({
color: lineColors[index % 3],//"rgb(27, 180, 176)",
transparent: true,
opacity: 1 - j * 0.02
})
var aMesh = new THREE.Mesh(aGeo, aMaterial);
aGroup.add(aMesh);
}
}

附上效果图

迁徙图

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
实现飞线迁徙效果可以使用 Three.js 库和 D3.js 库。Three.js 库用于渲染 3D 场景,D3.js 库用于处理数据和计算位置。下面是一个简单的示例代码: 首先安装依赖: ```bash npm install three d3 --save ``` 然后在 Vue 组件中引入 Three.js 和 D3.js 库: ```javascript import * as THREE from 'three'; import * as d3 from 'd3'; ``` 在组件中定义渲染器、相机、场景、光源等变量: ```javascript data() { return { renderer: null, // 渲染器 camera: null, // 相机 scene: null, // 场景 light: null, // 光源 lines: null, // 线条 width: null, // 宽度 height: null, // 高度 data: [], // 数据 color: null // 颜色 } }, ``` 在 mounted 生命周期中初始化渲染器、相机、场景、光源等: ```javascript mounted() { this.initRenderer(); this.initCamera(); this.initScene(); this.initLight(); this.loadData(); this.initLines(); this.renderScene(); }, ``` 接下来定义初始化渲染器、相机、场景、光源等方法: ```javascript // 初始化渲染器 initRenderer() { this.width = this.$el.clientWidth; this.height = this.$el.clientHeight; this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); this.renderer.setSize(this.width, this.height); this.$el.appendChild(this.renderer.domElement); }, // 初始化相机 initCamera() { this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 10000); this.camera.position.set(0, 0, 1000); }, // 初始化场景 initScene() { this.scene = new THREE.Scene(); }, // 初始化光源 initLight() { this.light = new THREE.DirectionalLight(0xffffff, 1); this.light.position.set(1, 1, 1); this.scene.add(this.light); }, // 加载数据 loadData() { // TODO: 加载数据 }, // 初始化线条 initLines() { const geometry = new THREE.BufferGeometry(); const positions = []; const colors = []; for (let i = 0; i < this.data.length; i++) { const d = this.data[i]; positions.push(d.source[0], d.source[1], d.source[2]); positions.push(d.target[0], d.target[1], d.target[2]); colors.push(this.color(d.value)); colors.push(this.color(d.value)); } geometry.addAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); geometry.addAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); const material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors }); this.lines = new THREE.LineSegments(geometry, material); this.scene.add(this.lines); }, // 渲染场景 renderScene() { requestAnimationFrame(this.renderScene); this.renderer.render(this.scene, this.camera); } ``` 在 loadData 方法中加载数据,可以使用 D3.js 库读取 JSON 格式的数据: ```javascript loadData() { d3.json('data.json').then(data => { this.data = data; const max = d3.max(this.data, d => d.value); this.color = d3.scaleSequential().domain([0, max]).interpolator(d3.interpolateRainbow); this.initLines(); }); }, ``` 最后,在组件中添加 CSS 样式,设置容器的宽度和高度即可: ```css .three-container { width: 100%; height: 500px; } ``` 上述代码仅为示例,具体实现还需要根据实际情况进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值