threebox实现飞线流光效果

技术路线为threebox+tween.js

一、初始化地图场景

mapboxgl.accessToken = ''
let map
let tb
  
map = new mapboxgl.Map({
  container: 'map2',
  style: 'mapbox://styles/mapbox/dark-v11',
  zoom: 12,
  center: [120.64088192016314, 31.41941143463331],
  pitch: 60,
  bearing: 0,
  antialias: true,
});

map.on('load', () => {
  addMapLine()
});


function addMapLine() {
  map.addLayer({
      id: 'custom_layer',
      type: 'custom',
      renderingMode: '3d',
      onAdd: function (map, mbxContext) {
        tb = new Threebox(
          map,
          mbxContext,
          {defaultLights: true}
        );
        let obj3D = draw();
        tb.add(obj3D);
      },
      render: function (gl, matrix) {
        if (map) {
          map.triggerRepaint();
        }
        tb.update();
        TWEEN.update();
      }
    }
  )
}

二、实现二维线飞线流光效果(无高度)

function draw() {
  // 创建对象
  const lineGroup = new THREE.Group();
  lineGroup.name = 'lineGroup';
  // 创建轨迹线
  const points = getPoints()
  const lineGeom = new THREE.BufferGeometry().setFromPoints(points);
  const material = new THREE.LineBasicMaterial({
    color: 0x006666
  });
  const lineObject = new THREE.Line(lineGeom, material);

  // 创建移动的线
  const index = 20; //取点索引位置
  const num = 10; //从线上获取点数量
  const points2 = points.slice(index, index + num); //从线上获取一段
  const flyLineGeom = new THREE.BufferGeometry();
  flyLineGeom.setFromPoints(points2);

  const colorArr = [];
  for (let i = 0; i < points2.length; i++) {
    const color1 = new THREE.Color(0x006666); // 线颜色
    const color2 = new THREE.Color(0xffff00); // 飞线颜色
    // 飞线渐变色
    let color = color1.lerp(color2, i / 5)
    colorArr.push(color.r, color.g, color.b);
  }
  // 设置几何体顶点颜色数据
  flyLineGeom.attributes.color = new THREE.BufferAttribute(new Float32Array(colorArr), 3);
  flyLineGeom.attributes.position.needsUpdate = true;

  const material2 = new THREE.LineBasicMaterial({
    vertexColors: THREE.VertexColors
  });

  const curveFlyObject = new THREE.Line(flyLineGeom, material2);
  lineGroup.add(lineObject, curveFlyObject)

  // 创建动画
  let tween = new TWEEN.Tween({index: 1})
    .to({index: 150}, 5000)
    .onUpdate(function (t) {
      let id = Math.ceil(t.index);
      let pointsList = points.slice(id, id + 10); //从线上获取一段
      flyLineGeom && flyLineGeom.setFromPoints(pointsList);
      flyLineGeom.attributes.position.needsUpdate = true;
    })
    .repeat(Infinity)
  tween.start();

  return lineGroup
}

function getPoints() {
  // 定义多段折线的点
  let points = [
    {x: 120.64088192, y: 31.5294114},
    {x: 120.55088192, y: 31.524114},
    {x: 120.43588192, y: 31.3214114},
    {x: 120.7288192, y: 31.4124114}
  ];
  // 定义内插的点数
  let numPoints = 50;
  // 计算每个内插点的坐标
  let interpolatedPoints = [];

  for (let i = 0; i < points.length - 1; i++) {
    let p1 = points[i];
    let p2 = points[i + 1];
    let dx = (p2.x - p1.x) / numPoints;
    let dy = (p2.y - p1.y) / numPoints;

    for (let j = 0; j < numPoints; j++) {
      let x = p1.x + j * dx;
      let y = p1.y + j * dy;
      let pos = tb.utils.lnglatsToWorld([[...[x, y], 0]])
      interpolatedPoints.push(pos[0]);
    }
  }
  return interpolatedPoints
}

内插点是为了给飞线预设点位,若仅按照几个折点实现的效果并不好。效果如下:

三、实现三维曲线飞线流光效果

function draw() {
  const lineGroup = new THREE.Group();
  lineGroup.name = 'lineGroup';
  let point1 = [120.6, 31.41]
  let point2 = [120.7, 31.40]
  let point3 = [120.8, 31.39]
  let point4 = [120.9, 31.38]
  let point5 = [121, 31.37]


  const point11 = tb.utils.lnglatsToWorld([[...point1, 0]])
  const point22 = tb.utils.lnglatsToWorld([[...point2, 0]])
  const point33 = tb.utils.lnglatsToWorld([[...point3, 0]])
  const point44 = tb.utils.lnglatsToWorld([[...point4, 0]])
  const point55 = tb.utils.lnglatsToWorld([[...point5, 0]])

  const pointInLine = [
    new THREE.Vector3(point11[0].x, point11[0].y, 0),
    new THREE.Vector3(point22[0].x, point22[0].y, 120),
    new THREE.Vector3(point33[0].x, point33[0].y, 20),
    new THREE.Vector3(point44[0].x, point44[0].y, 80),
    new THREE.Vector3(point55[0].x, point55[0].y, 0)
  ];

  // 创建轨迹线
  const curve = new THREE.CatmullRomCurve3(pointInLine);
  const points = curve.getSpacedPoints(100);
  const lineGeom = new THREE.BufferGeometry().setFromPoints(points);

  const material = new THREE.LineBasicMaterial({
    color: 0x006666
  });
  const curveObject = new THREE.Line(lineGeom, material);

  // 创建移动的线
  const index = 20; //取点索引位置
  const num = 10; //从曲线上获取点数量
  const points2 = points.slice(index, index + num); //从曲线上获取一段
  const flyLineGeom = new THREE.BufferGeometry();
  flyLineGeom.setFromPoints(points2);

  // 操作颜色
  const colorArr = [];
  for (let i = 0; i < points2.length; i++) {
    const color1 = new THREE.Color(0x006666); // 线颜色
    const color2 = new THREE.Color(0xffff00); //飞痕颜色
    // 飞痕渐变色
    let color = color1.lerp(color2, i / 5)
    colorArr.push(color.r, color.g, color.b);
  }
  // 设置几何体顶点颜色数据
  flyLineGeom.attributes.color = new THREE.BufferAttribute(new Float32Array(colorArr), 3);
  flyLineGeom.attributes.position.needsUpdate = true;

  const material2 = new THREE.LineBasicMaterial({
    vertexColors: THREE.VertexColors, //使用顶点本身颜色
  });

  const curveFlyObject = new THREE.Line(flyLineGeom, material2);
  lineGroup.add(curveObject, curveFlyObject)

  // 创建动画
  let tween = new TWEEN.Tween({index: 1})
    .to({index: 100}, 3000)
    .onUpdate(function (t) {
      let id = Math.ceil(t.index);
      let pointsList = points.slice(id, id + 10); //从曲线上获取一段
      flyLineGeom && flyLineGeom.setFromPoints(pointsList);
      flyLineGeom.attributes.position.needsUpdate = true;
    })
    .repeat(Infinity)
  tween.start();

  return lineGroup
}

实现效果如下:

三维曲线参考大佬的文档:mapbox-gl添加threejs飞线_mapbox three_迦南giser的博客-CSDN博客

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
实现飞线迁徙图效果可以使用 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; } ``` 上述代码仅为示例,具体实现还需要根据实际情况进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值