mapbox-gl添加threejs飞线

news2024/11/25 20:33:33

文章目录

  • 前言
  • 飞线实现
    • 1 初始化地图并加载three图层
    • 2 绘制飞线几何体
      • 将几何体正确定位在mapbox上
      • 正确操作BufferGeometry几何体
    • 3 tween实现动画
  • 全部代码
  • 总结
    • 待改进之处
    • 参考


前言

mapbox-gl是一个基于webgl开发的三维地图渲染引擎,但是很多三维特效只用mapbox并不容易实现,比如带高度的飞线,但是使用threejs就有多种实现的方式,本文结合之前对threebox的研究,使用一种使用线图层动画的方式实现带高度的飞线。

在这里插入图片描述


飞线实现

1 初始化地图并加载three图层

mapbox中操作threejs建议使用threebox,相比直接操作threejs会简便很多,通过添加一个自定义图层实现。

 mapRef.current = new mapboxgl.Map({
      zoom: 12,
      center: [104.807073, 29.35702],
      pitch: 60,
      style: 'mapbox://styles/mapbox/dark-v11',
      container: mapContainerRef.current,
      antialias: true,
    });
    mapRef.current.on('load', () => {
      mapRef.current.addLayer(
        {
          id: 'custom_layer',
          type: 'custom',
          onAdd: function (map, mbxContext) {
            this.map = map;
            tbRef.current = new Threebox(
              map,
              mbxContext,
              { defaultLights: true }
            );
            let obj3D = draw2();
            tbRef.current.add(obj3D);
          },
          render: function (gl, matrix) {
            if (this.map) {
              this.map.triggerRepaint();
            }
            tbRef.current.update();
            TWEEN.update();
          }
        }
      )
    })

2 绘制飞线几何体

实现动画飞线的效果基本思路很直接:建立路径后,在线上截取一段作为飞痕,设置好颜色后使其沿着路线不断运动即可。在这一步中,关键的有两点:

将几何体正确定位在mapbox上

mapbox是3857坐标系,threejs是右手空间坐标系,在使用three的方法前要将坐标进行转换:

	let startPoint = [104.807073, 29.35702]
    let endPoint = [105.807073, 30.35702]
    // 坐标转换
    const xyz_start = tbRef.current.utils.lnglatsToWorld([[...startPoint, 0]])
    const xyz_end = tbRef.current.utils.lnglatsToWorld([[...endPoint, 0]])
	// 创建3d飞线路径
    const pointInLine = [
      new THREE.Vector3(xyz_start[0].x, xyz_start[0].y, 0),
      new THREE.Vector3((xyz_start[0].x + xyz_end[0].x) / 2, (xyz_start[0].y + xyz_end[0].y) / 2, curveH),
      new THREE.Vector3(xyz_end[0].x, xyz_end[0].y, 0)
    ];

正确操作BufferGeometry几何体

操作几何体参考之前有讲,一些核心方法在本例中常用。

// 为几何体attributes存储顶点信息
flyLineGeom.setFromPoints(pointsList);
// 几何体根据attributes中的position绘制
flyLineGeom.attributes.position.needsUpdate = true;
// 几何体attributes存储颜色信息
flyLineGeom.attributes.color = new THREE.BufferAttribute(new Float32Array(colorArr), 3);
// 几何体根据attributes中的color信息上色
 flyLineGeom.attributes.color.needsUpdate = true;
 // 几何体渲染时颜色采用geometry.attributes.color中对应的颜色
 const material2 = new THREE.LineBasicMaterial({
      vertexColors: THREE.VertexColors, //使用顶点本身颜色
    });

3 tween实现动画

tween可以方便实现动画,每帧跟新飞痕的位置

let tween = new TWEEN.Tween({ index: 1 })
 		.to({ index: 50 }, 2000)
        .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();

全部代码

import React, { useRef, useEffect } from 'react';
import mapboxgl from 'mapbox-gl';
import { Threebox, THREE } from 'threebox-plugin';
import TWEEN from '@tweenjs/tween.js'

function App() {
  const mapContainerRef = useRef();
  const tbRef = useRef();
  const mapRef = useRef();

  // 初始化基础图层
  useEffect(() => {
    mapboxgl.accessToken = 'pk.eyJ1IjoiamFja2Vyb28iLCJhIjoiY2w5aThzdWJ5MTBjYjQybzg1cHNhYXFqeCJ9.HmUVfVzRx1EOl4uzY-HFuQ'
    mapRef.current = new mapboxgl.Map({
      zoom: 12,
      center: [104.807073, 29.35702],
      pitch: 60,
      style: 'mapbox://styles/mapbox/dark-v11',
      container: mapContainerRef.current,
      antialias: true,
    });
    mapRef.current.on('load', () => {
      mapRef.current.addLayer(
        {
          id: 'custom_layer',
          type: 'custom',
          onAdd: function (map, mbxContext) {
            this.map = map;
            tbRef.current = new Threebox(
              map,
              mbxContext,
              { defaultLights: true }
            );
            let obj3D = draw2();

            console.log(obj3D)
            tbRef.current.add(obj3D);
          },
          render: function (gl, matrix) {
            if (this.map) {
              this.map.triggerRepaint();
            }
            tbRef.current.update();
            TWEEN.update();
          }
        }
      )
    })
  }, []);

  function draw2() {
    const curveH = 800; // 飞线最大高
    const lineGroup = new THREE.Group();
    lineGroup.name = 'lineGroup';
    let startPoint = [104.807073, 29.35702]
    let endPoint = [105.807073, 30.35702]
    const xyz_start = tbRef.current.utils.lnglatsToWorld([[...startPoint, 0]])
    const xyz_end = tbRef.current.utils.lnglatsToWorld([[...endPoint, 0]])

    const pointInLine = [
      new THREE.Vector3(xyz_start[0].x, xyz_start[0].y, 0),
      new THREE.Vector3((xyz_start[0].x + xyz_end[0].x) / 2, (xyz_start[0].y + xyz_end[0].y) / 2, curveH),
      new THREE.Vector3(xyz_end[0].x, xyz_end[0].y, 0)
    ];

    // 创建轨迹线
    const curve = new THREE.CatmullRomCurve3(pointInLine);
    const points = curve.getSpacedPoints(50);
    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: 50 }, 2000)
                    .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
  }


  return (
    <div style={{ display: 'flex' }}>
      <div
        id="map-container"
        ref={mapContainerRef}
        style={{ height: '100vh', width: '100vw' }}
      />
      <div style={{ position: 'fixed', top: '0', right: '0' }}>
      </div>
    </div>
  );
}
export default App;

总结

飞线实现

  • 1 初始化地图并加载three图层
  • 2 绘制飞线几何体
  • 3 tween实现动画

待改进之处

本文采用简单的思路实现了在mapbox地图中加入three飞线,但是还有改进方案,比如:

  • 通过更改geometry.attributes.color实现动画效果,不需要额外新建几何体
  • 通过着色器实现更加生动的效果

参考

  • mapboxgl + three.js 开发实践

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CSDN 年终总结】CSDN的进阶之路—— “1+1=王”的2022总结

正文之前打个广告&#xff0c;我正在参加年度博客之星评选&#xff0c;请大家帮我投票打分&#xff0c;您的每一分都是对我的支持与鼓励。⭐ ⭐ ⭐ ⭐ ⭐https://bbs.csdn.net/topics/611386885?spm1001.2014.3001.6953 2022我在CSDN 2022 在CSDN是持续输出&#xff0c;持续…

TinyPng图片压缩的正确打开方式

https://tinypng.com/ TinyPNG使用智能的「有损压缩技术」来减少WEBP、JPEG和PNG文件的文件大小。通过选择性地减少图像中的「颜色数量」&#xff0c;使用更少的字节来存储数据。这种效果几乎是看不见的&#xff0c;但在文件大小上有非常大的差别。 使用过TinyPNG的都知道&…

MyBatis 万字长文:从入门到动态SQL超详细

文章目录1. 前言2. 创建项目3. 添加框架支持4. 建库5. 配置数据库连接信息和 XML 文件路径5.1 创建 Java 类5.2 Java 接口5.3 XML 文件6. 查询6.1 不带参数的查询6.2 单元测试6.3 带参数的查询7. 修改8. 增加8.1 将对象插入表中8.2 获取自增主键9. 删除10. 数据库字段和类属性名…

Video2StyleGAN: Disentangling Local and Global Variations in a Video翻译

点击下载论文 代码地址 摘要 使用预训练的StyleGAN生成器进行图像编辑已成为面部编辑的强大范例&#xff0c;它提供了对年龄、表情、照明度等的解纠缠控制。然而&#xff0c;该方法不能直接用于视频操作。我们认为主要因素是缺乏对面部位置、面部姿势和局部面部表情的精细和清…

腾讯云-云服务器购买流程-Java项目部署(详细的一批)

文章目录云服务器购买云服务搭建部署环境宝塔面板使用&#xff08;安装所需环境&#xff09;部署SpringBoot项目出现Error: Unable to access jarfile /www/wwwroot/xxxx.jar--server.port6066 问题解决腾讯云COS有什么用&#xff1f;如果感觉有用就一键三连吧&#xff0c;创作…

Electron 实现切换暗_亮模式与主题

文章末尾附上仓库地址&#xff01;&#xff01;&#xff01;&#xff01; 清单 模板基于 electron-vite-vue vue3 ts vite组件库 element-plushooks库 vueuse 、useElementPlusTheme 初始化工程 使用 electron-vite 作为模板&#xff0c;方便大家尽快吧项目跑起来 # 创建模…

Java面试之数据库篇

一、基础 1.数据库事务的特征ACID 原子性&#xff08;Atomicity&#xff09;&#xff1a;原子性是指事务包含的所有操作要么全部成功&#xff0c;要么全部失败回滚&#xff0c;这和前面两篇博客介绍事务的功能是一样的概念&#xff0c;因此事务的操作如果成功就必须要完全应用…

UOS服务器操作系统多版本Java切换

一、修改java的环境变量和软链接来实现版本切换 1、配置环境变量 sudo vim &#xff5e;/.bashrc 2、创建java运行程序软连接 3、使配置生效&#xff0c;并检查java版本 source /etc/profile 二、使用update-alternatives 进行版本的切换 1、同时安装了openjdk-8-jdk 和…

shell第七天作业——awk

题目 1、获取根分区剩余大小 2、获取当前机器ip地址 3、统计出apache的/var/log/httpd/access_log文件中访问量最多的前3个IP 4、打印/etc/passwd中UID大于500的用户名和uid 5、/etc/passwd 中匹配包含root或sys或tcp的任意行 6、请打印出/etc/passwd 第一个域&#xff0…

指针进阶之数组参数和指针参数

文章目录一、回顾1.字符指针2.指针数组和数组指针&#xff08;1&#xff09;指针数组&#xff08;2&#xff09;数组指针二、数组参数1.一维数组传参&#xff08;1&#xff09;整型数组&#xff08;2&#xff09;指针数组&#xff08;3&#xff09;总结2.二维数组传参&#xff…

基于Python tensorflow2.3实现的水果识别系统源码+模型+数据集,卷积神经网络的入门案例

水果识别-基于tensorflow2.3实现 水果识别是卷积神经网络的入门案例&#xff0c;这里我将模型的训练、测试、保存以及使用整合在了一起&#xff0c;至于原理部分&#xff0c;大家可以参考知乎或者B站上的回答&#xff0c;在这里我就不赘述了 完整代码下载地址&#xff1a;基于…

计算机网络实验---验证性实验

实验一/ipconfig 实作一 实作二 实验二/ping 实作一 实作二 实验三/tracert 实作一 实作二 实验四/ARP 实作一 实作二 实作二 实验五/DHCP 实作一 实验六/netstat 实作一 实作二 实验七/DNS 实作一 实作二 实作二 实验八/cache 实作一 实作二 总结 实验一/ipconfig 实…

[Leetcode] 二叉树的遍历

转载自&#xff08;有删减和少量改动&#xff09; 图解二叉树的四种遍历 https://leetcode.cn/problems/binary-tree-preorder-traversal/solution/tu-jie-er-cha-shu-de-si-chong-bian-li-by-z1m/1. 相关题目144.二叉树的前序遍历 https://leetcode.cn/problems/binary-tree-p…

【SpringMVC 入门教程】

SpringMVC_day02 &#x1f308;博客主页&#xff1a;屠一乐的博客 &#x1f4c5; 发文时间&#xff1a;2023.1.5 &#x1f388; 一定存在只有你才能做成的事 &#x1f339; 博主水平有限&#xff0c;如有错误&#xff0c;欢迎指正 欢迎各位&#x1f44d;收藏&#x1f48e;评论✉…

MacBookPro安装mysql遇到的几个问题

用Mac的好处是不用开关机&#xff0c;无弹窗无广告&#xff0c;坏处是在安装某些第三方的软件时&#xff0c;总是和视频教程上的winows版不一致&#xff0c;需要自己上网找资料尝试怎么安装。今天学python&#xff0c;需要安装mysql&#xff0c;幸好网上有一些文章&#xff0c;…

Vulnhub靶机:MISDIRECTION_ 1

目录介绍信息收集主机发现主机信息探测网站探测反弹shell方式1&#xff1a;使用nc方式2&#xff1a;使用bash方式3&#xff1a;使用MSF提权sudo提权passwd提权docker提权参考介绍 系列&#xff1a;Misdirection&#xff08;此系列共1台&#xff09; 发布日期&#xff1a;2019 …

【ClickHouse】从Mysql迁移到ClickHouse大全

从关系型的数据库(Mysql)升级到列式管理的联机分析型数据库(ClickHouse)&#xff0c;这不亚于是小米加步枪升级为加特林机关枪的性能提升了&#xff0c;查询能力等确实是大大的提升了&#xff0c;这出现了一个问题我们之前存储在Mysql里的历史数据怎么往ClickHouse里面迁移呢&a…

访问者模式Visitor

1.意图&#xff1a;表示一个作用于某对象结构中的各元素的操作。它允许在不改变各元素的类的前提下定义作用于这些元素的操作。 2.结构 Visitor&#xff08;访问者&#xff09;为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请…

本地机器 Google Colab 通过 SSH 连接远程服务器

1. 情景描述 我自己笔记本配置太垃圾&#xff0c;想要用学校的深度学习服务器在Colab上跑程序。 2. 环境描述 远程服务器 (Ubuntu)&#xff1a; 用pip安装 jupyter notebook 以及 jupyter_http_over_ws 拓展包 (前提有python环境和pip) pip install notebookpip install j…

Android设计模式详解之外观模式

前言 外观模式也称门面模式&#xff0c;在开发过程中的运用频率非常高&#xff1b; 定义&#xff1a;要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行&#xff1b;门面模式提供一个高层次的接口&#xff0c;使得子系统更易于使用&#xff1b; 使用场景&#…