当前位置: 首页 > news >正文

Vue3实现地球上加载柱体

最终效果为上图。

实现该技术,需要一些技术,我分别罗列一下:

  • canvas:需要使用canvas根据json来绘制地球,不懂的可以看这篇canvas绘制地球 
  • threejs:需要会使用threejs,这里并没有使用shader,不需要制作复杂的东西。
  • Vue3:这个可选。不会也能实现。

需要使用的插件:

  • @surbowl/world-geo-json-zh :这个第三方包是简体中文 Geo JSON 世界地图,带有国家(地区)的 ISO 3166 代码、中文简称与全称。含中国南海海域十段线。

  • three :这个我就不用说了。

然后下面是具体实现的代码:

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as TWEEN from 'three/examples/jsm/libs/tween.module.js';
import worldJSON from '@surbowl/world-geo-json-zh'
import earthquakeJSON from '../assets/json/earthquake.json'export default (domId) => {/* ------------------------------初始化三件套--------------------------------- */const dom = document.getElementById(domId);const { innerHeight, innerWidth } = windowconst scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 2000);camera.position.set(0, 0, 10);camera.lookAt(scene.position);const renderer = new THREE.WebGLRenderer({antialias: true,// 抗锯齿alpha: false,// 透明度powerPreference: 'high-performance',// 性能logarithmicDepthBuffer: true,// 深度缓冲})// renderer.setClearColor(0x000000, 0);// 设置背景色// renderer.clear();// 清除渲染器renderer.shadowMap.enabled = true;// 开启阴影renderer.shadowMap.type = THREE.PCFSoftShadowMap;// 阴影类型renderer.outputEncoding = THREE.sRGBEncoding;// 输出编码renderer.toneMapping = THREE.ACESFilmicToneMapping;// 色调映射renderer.toneMappingExposure = 1;// 色调映射曝光renderer.physicallyCorrectLights = true;// 物理正确灯光renderer.setPixelRatio(devicePixelRatio);// 设置像素比renderer.setSize(innerWidth, innerHeight);// 设置渲染器大小dom.appendChild(renderer.domElement);// 重置大小window.addEventListener('resize', () => {const { innerHeight, innerWidth } = windowcamera.aspect = innerWidth / innerHeight;camera.updateProjectionMatrix();renderer.setSize(innerWidth, innerHeight);})/* ------------------------------初始化工具--------------------------------- */const controls = new OrbitControls(camera, renderer.domElement) // 相机轨道控制器controls.enableDamping = true // 是否开启阻尼controls.dampingFactor = 0.05// 阻尼系数controls.panSpeed = -1// 平移速度// const axesHelper = new THREE.AxesHelper(10);// scene.add(axesHelper);/* ------------------------------正题--------------------------------- */// 地图配置const mapOptions = {sphere: null, // 球体bg: 'rgb(10 ,20 ,28)',// 背景色borderColor: 'rgb(10 ,20 ,28)',// 边框颜色blurColor: '#000000',// 模糊颜色borderWidth: 1,// 边框宽度blurWidth: 5,// 模糊宽度fillColor: 'rgb(26, 35, 44)',// 填充颜色barHueStart: 0.7,// 柱体颜色起始值barHueEnd: 0.2,// 柱体颜色结束值barLightStart: 0.1,// 柱体亮度起始值barLightEnd: 1.0// 柱体亮度结束值}// 相机位置const cameraPos = {x: 0.27000767404584447,y: 1.0782003329514755,z: 3.8134631736522793}// 相机控制器位置const controlPos = {x: 0,y: 0,z: 0}// 柱状图信息const barInfo = {barMin: 0.01,barMax: 0.5,currentBarH: 0.01,// 柱体高度min: Number.MAX_SAFE_INTEGER,max: Number.MIN_SAFE_INTEGER,range: 0,mesh: null,// 柱体lonHelper: null, // 经度辅助线latHelper: null, // 纬度辅助线positionHelper: null,originHelper: null,}// 用于绑定整个地球的容器const objGroup = new THREE.Group();scene.add(objGroup);// 绘制地图const drawRegion = (ctx, c, geoInfo) => {ctx.beginPath();c.forEach((item, i) => {let pos = [(item[0] + 180) * 10, (-item[1] + 90) * 10];if (i == 0) {ctx.moveTo(pos[0], pos[1]);} else {ctx.lineTo(pos[0], pos[1]);}});ctx.closePath();ctx.fill();ctx.stroke();}// 创建地球const createMap = () => {const canvas = document.createElement('canvas');canvas.width = 3600;canvas.height = 1800;const ctx = canvas.getContext('2d');ctx.fillStyle = mapOptions.bg;ctx.rect(0, 0, canvas.width, canvas.height);ctx.fill();// 设置地图样式ctx.strokeStyle = mapOptions.borderColor;ctx.lineWidth = mapOptions.borderWidth;ctx.fillStyle = mapOptions.fillColor;if (mapOptions.blurWidth) {ctx.shadowColor = mapOptions.blurColor;ctx.shadowBlur = mapOptions.blurWidth;}// 遍历数据worldJSON.features.forEach(c1 => {// 判断是否为多边形if (c1.geometry.type == 'MultiPolygon') {c1.geometry.coordinates.forEach(c2 => {c2.forEach(c3 => {drawRegion(ctx, c3)})})}else {c1.geometry.coordinates.forEach(c2 => {drawRegion(ctx, c2)})}})const map = new THREE.CanvasTexture(canvas);// 创建纹理贴图map.wrapS = THREE.RepeatWrapping;// 水平方向重复map.wrapT = THREE.RepeatWrapping;// 垂直方向重复const geometry = new THREE.SphereGeometry(1, 32, 32);// 创建球体几何体const material = new THREE.MeshBasicMaterial({map: map,// 纹理贴图transparent: true// 透明});mapOptions.sphere = new THREE.Mesh(geometry, material);mapOptions.sphere.visible = false;// 隐藏地球objGroup.add(mapOptions.sphere);// 添加到场景中}// 创建柱体const createBar = (info, index) => {const amount = (info.mag - barInfo.min) / barInfo.range;// 根据值计算比例const hue = THREE.MathUtils.lerp(mapOptions.barHueStart, mapOptions.barHueEnd, amount);// 根据值计算颜色const saturation = 1;// 饱和度const lightness = THREE.MathUtils.lerp(mapOptions.barLightStart, mapOptions.barLightEnd, amount);// 根据值计算亮度const color = new THREE.Color();color.setHSL(hue, saturation, lightness);// 设置颜色barInfo.mesh.setColorAt(index, color);// 设置颜色barInfo.lonHelper.rotation.y = THREE.MathUtils.degToRad(info.lon) + Math.PI * 0.5;barInfo.latHelper.rotation.x = THREE.MathUtils.degToRad(-info.lat);barInfo.positionHelper.updateWorldMatrix(true, false);let h = THREE.MathUtils.lerp(0.01, 0.5, amount);barInfo.positionHelper.scale.set(0.01, 0.01, h <= barInfo.currentBarH ? h : barInfo.currentBarH);barInfo.originHelper.updateWorldMatrix(true, false);barInfo.mesh.setMatrixAt(index, barInfo.originHelper.matrixWorld);}// 创建柱体群const createBars = (list) => {list.forEach((info, index) => {createBar(info, index)})barInfo.mesh.instanceColor.needsUpdate = true;barInfo.mesh.instanceMatrix.needsUpdate = true;}// 创建全部柱体const createAllBars = () => {// 辅助对象barInfo.lonHelper = new THREE.Object3D();// 经度辅助对象scene.add(barInfo.lonHelper);barInfo.latHelper = new THREE.Object3D();// 纬度辅助对象barInfo.lonHelper.add(barInfo.latHelper);barInfo.positionHelper = new THREE.Object3D();// 位置辅助对象barInfo.positionHelper.position.z = 1;barInfo.latHelper.add(barInfo.positionHelper);barInfo.originHelper = new THREE.Object3D();// 原点barInfo.originHelper.position.z = 0.5;barInfo.positionHelper.add(barInfo.originHelper);const boxGeometry = new THREE.BoxGeometry(1, 1, 1);const boxMaterial = new THREE.MeshBasicMaterial({ color: '#FFFFFF' });earthquakeJSON.forEach(c1 => {if (barInfo.min > c1.mag) {barInfo.min = c1.mag;}if (barInfo.max < c1.mag) {barInfo.max = c1.mag;}})barInfo.range = barInfo.max - barInfo.min;barInfo.mesh = new THREE.InstancedMesh(boxGeometry, boxMaterial, earthquakeJSON.length);objGroup.add(barInfo.mesh);createBars(earthquakeJSON);objGroup.scale.set(0.1, 0.1, 0.1)mapOptions.sphere.visible = true;// 显示地球}// 播放动画const play = () => {const orgCamera = camera.position;const orgControl = controls.target;const tween = new TWEEN.Tween({scale: 0.1,rotate: 0,cameraX: orgCamera.x,cameraY: orgCamera.y,cameraZ: orgCamera.z,controlsX: orgControl.x,controlsY: orgControl.y,controlsZ: orgControl.z}).to({scale: 1,rotate: Math.PI,cameraX: cameraPos.x,cameraY: cameraPos.y,cameraZ: cameraPos.z,controlsX: controlPos.x,controlsY: controlPos.y,controlsZ: controlPos.z}, 2000).easing(TWEEN.Easing.Quadratic.Out).onUpdate((obj) => {objGroup.scale.set(obj.scale, obj.scale, obj.scale);objGroup.rotation.y = obj.rotate;camera.position.set(obj.cameraX, obj.cameraY, obj.cameraZ);controls.target.set(obj.controlsX, obj.controlsY, obj.controlsZ);}).chain(new TWEEN.Tween({ h: barInfo.barMin }).to({ h: barInfo.barMax }, 2000).easing(TWEEN.Easing.Quadratic.Out).onUpdate((obj) => {barInfo.currentBarH = obj.h;createBars(earthquakeJSON);})).start();TWEEN.add(tween);}// 初始化const init = () => {camera.near = 0.1;// 相机最近距离camera.updateProjectionMatrix();// 更新相机投影矩阵createMap();createAllBars();play();}init();/* ------------------------------动画函数--------------------------------- */const animation = () => {TWEEN.update();renderer.render(scene, camera);controls.update();requestAnimationFrame(animation);}animation();
}

在引入的地方有使用到一个文件 earthquake.json我也一块放在资源里面了。

最后在vue的onMounted生命周期里面调用就好了。

相关文章:

Vue3实现地球上加载柱体

最终效果为上图。 实现该技术&#xff0c;需要一些技术&#xff0c;我分别罗列一下&#xff1a; canvas&#xff1a;需要使用canvas根据json来绘制地球&#xff0c;不懂的可以看这篇canvas绘制地球 threejs&#xff1a;需要会使用threejs&#xff0c;这里并没有使用shader&am…...

OpenGL入门003——使用Factory设计模式简化渲染流程

前面两节已经学会了如何使用opengl创建窗口并绘制三角形&#xff0c;我们可以看出有些步骤是固定的&#xff0c;而且都写在main.cpp&#xff0c;这一节我们将了解如何使用Factroy设计模型。将模型渲染逻辑封装在一个单独的类中&#xff0c;简化开发流程&#xff0c;且提高代码复…...

01_AI编程案例展示:借助AI轻松爬取海量网盘链接

爬虫案例展示 今天,我们将展示如何利用AI快速开发一个网络爬虫&#xff0c; 使用的工具是Python和Claude 3.5 Sonnet(国内可用豆包替代) 我们的目标是爬取panhub.fun网站上的夸克网盘链接, 即使你是编程新手,也可以轻松完成这样的任务。 案例1-批量爬取panhub网盘整合包 下…...

【机器学习导引】ch5-神经网络

Q&A 1x1 卷积层在深度学习中的作用&#xff1f; 1x1 卷积层在深度学习中具有几个重要的作用&#xff1a; 通道压缩&#xff1a;1x1卷积可以通过调整输出通道数来减少特征图的深度&#xff0c;从而降低计算成本和参数数量。这有助于在保持特征的情况下简化模型。特征融合&am…...

【Axure原型分享】颜色选择器——填充颜色

今天和大家分享颜色选择器——填充颜色的原型模板&#xff0c;点击颜色区域可以弹出颜色选择器&#xff0c;点击可以选择对应颜色&#xff0c;颜色区域会变色我们选择的颜色&#xff0c;具体效果可以观看下方视频或者打开预览地址体验。 【原型效果】 【Axure高保真原型】颜色…...

怎么安装行星减速电机才是正确的

行星减速电机由于其高效、精密的传动能力&#xff0c;广泛应用于自动化设备、机器人、机床以及其他需要精准控制的领域。正确的安装行星减速电机对于确保设备的性能与延长使用寿命至关重要。 一、前期准备 在进行行星减速电机的安装之前&#xff0c;必须做好充分的前期准备工作…...

Unity程序化生成地形

制作地形&#xff1a; 绘制方块逐个绘制方块并加噪波高度删除Gizmos和逐个绘制 1.draw quad using System.Collections; using System.Collections.Generic; using UnityEngine;[RequireComponent(typeof(MeshFilter))] public class mesh_generator : MonoBehaviour {Mesh m…...

Vxe UI vue vxe-table 表格中使用下拉表格,单元格渲染下拉表格

Vxe UI vue vxe-table 表格中使用下拉表格&#xff0c;单元格渲染下拉表格 单元格中渲染下拉表格&#xff0c;需要使用到 vxe-table-select 这个组件&#xff0c;在 vxe-table 4.7 中使用非常简单&#xff0c;只需要配置好渲染器数据源就可以。 支持单选 也可以多选 代码 …...

Android开发教程实加载中...动效

Android开发教程实加载中…动效 加载中&#xff0c;发送中&#xff0c;匹配中都可以用&#xff0c;就是后面是三个点还是两个点&#xff0c;不断在切换 一、思路&#xff1a; 隔500ms发送一次&#xff0c;改变内容 二、效果图&#xff1a; 看视频更加直观点&#xff1a; An…...

NVR设备ONVIF接入平台EasyCVR视频融合平台智慧小区视频监控系统建设方案

一、方案背景 智慧小区构成了“平安城市”建设的基石。随着社会的进步&#xff0c;社区安全问题逐渐成为公众关注的热点。诸如高空抛物、乱丢垃圾、破坏车辆、入室盗窃等不文明行为和违法行为频繁出现。目前&#xff0c;许多小区的物业管理和安全防护系统仍然较为简单和陈旧&a…...

适配器模式适用的场景

适配器模式是一种常用的设计模式&#xff0c;能够将不兼容的接口转换为客户端所需的接口。在实际开发中&#xff0c;我们常常会遇到需要统一接口、替换外部系统、兼容旧接口或适配不同数据格式的情况。本文将结合详细的代码示例&#xff0c;介绍适配器模式的适用场景。 1. 统一…...

Ambari里面添加hive组件

1.创建hive数据库 在添加hive组件之前需要做的事情&#xff0c;先在master这个虚拟机里面创建好hive 先进入虚拟机里面进入mysql 然后输入这个命令看看有没有自己创建的hive数据库 show databases;有的话会显示下面这个样子 没有的同学使用以下命令可以在MySQL中创建hive数…...

Windows部署rabbitmq

本次安装环境&#xff1a; 系统&#xff1a;Windows 11 软件建议版本&#xff1a; erlang OPT 26.0.2rabbitmq 3.12.4 一、下载 1.1 下载erlang 官网下载地址&#xff1a; 1.2 下载rabbitmq 官网下载地址&#xff1a; 建议使用解压版&#xff0c;安装版可能会在安装软件…...

【Flask】四、flask连接并操作数据库

目录 前言 一、 安装必要的库 二、配置数据库连接 三、定义模型 四、操作数据库 1.添加用户 2.删除用户 3.更新用户信息 4查询所有用户 五、测试结果 前言 在Flask框架中&#xff0c;数据库的操作是一个核心功能&#xff0c;它允许开发者与后端数据库进行交互&#xf…...

ES跟Kafka集成

配合流程 1. Kafka作为分布式流处理平台&#xff0c;能够实时收集和处理不同数据源的数据流&#xff1b; 2. 通过Kafka Connect或者Logstash等中间件&#xff0c;可以将Kafka中的数据流实时推送到Elasticsearch中&#xff1b; 3. Elasticsearch接收到数据后&#xff0c;会根据…...

Python Matplotlib:基本图表绘制指南

Python Matplotlib&#xff1a;基本图表绘制指南 Matplotlib 是 Python 中一个非常流行的绘图库&#xff0c;它以简单易用和功能丰富而闻名&#xff0c;适合各种场景的数据可视化需求。在数据分析和数据科学领域&#xff0c;Matplotlib 是我们展示数据的有力工具。本文将详细讲…...

供应商图纸外发:如何做到既安全又高效?

供应商跟合作伙伴、客户之间会涉及到图纸外发的场景&#xff0c;这是一个涉及数据安全、效率及合规性的重要环节。供应商图纸发送流程一般如下&#xff1a; 1.申请与审批 采购人员根据需要提出发放图纸的申请并提交审批&#xff1b; 采购部负责人审批发放申请&#xff0c;确…...

探索 Move 编程语言:智能合约开发的新纪元

目录 引言 一、变量的定义 二、整型 如何在Move中表示小数和负数&#xff1f; 三、运算符 as运算符 布尔型 地址类型 四、什么是包&#xff1f; 五、什么是模块&#xff1f; 六、如何定义方法&#xff1f; 方法访问权限控制 init方法 总结 引言 Move 是一种专为区…...

vue3+vant实现视频播放(含首次禁止进度条拖拽,视频看完后恢复,保存播放视频进度,刷新及下次进入继续播放,判断视频有无全部看完等)

1、效果图 2、 <div><videocontrolsclass"video_player"ref"videoPlayer":src"videoSrc"timeupdate"handleTimeUpdate"play"onPlay"pause"onPause"ended"onVideoEnded"></video><…...

情感强度分析:精确衡量文本情感强弱的 AI 技术

情感强度分析&#xff1a;精确衡量文本情感强弱的 AI 技术 一、引言 在当今信息爆炸的时代&#xff0c;我们每天都会接触到大量的文本信息。这些文本中蕴含着各种各样的情感&#xff0c;如喜悦、悲伤、愤怒、恐惧等。如何准确地理解和分析这些文本的情感强度&#xff0c;对于…...

龙芯2K3000与国产OS在轨道交通AFC系统中的工程实践

1. 项目概述&#xff1a;当国产芯遇上城市动脉每天早晚高峰&#xff0c;地铁站里人头攒动&#xff0c;闸机开合的“嘀嘀”声此起彼伏。你可能没留意&#xff0c;支撑这套庞大自动售检票系统&#xff08;AFC&#xff09;稳定运行的“大脑”&#xff0c;正经历一场静默而深刻的变…...

WindowResizer:如何打破Windows窗口尺寸限制,实现桌面布局自由?

WindowResizer&#xff1a;如何打破Windows窗口尺寸限制&#xff0c;实现桌面布局自由&#xff1f; 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 在Windows日常使用中&#xff0…...

从Polycam扫描到自定义街道:用3D高斯泼溅碎片‘搭积木’创建虚拟场景的完整流程

从Polycam扫描到自定义街道&#xff1a;用3D高斯泼溅碎片‘搭积木’创建虚拟场景的完整流程 走在城市的街道上&#xff0c;你是否曾想过把那些有趣的街景元素——复古的路灯、造型独特的长椅、枝繁叶茂的行道树——全都数字化&#xff0c;然后像玩乐高一样重新组合成自己理想中…...

联发科天玑700/720/900核心板选型指南:5G物联网与智能硬件性能功耗全解析

1. 项目概述&#xff1a;从核心板选型看5G入门级应用的性能锚点 在嵌入式开发和智能硬件领域&#xff0c;选择一颗合适的核心板&#xff08;Core Board&#xff09;往往是项目成败的第一步。它集成了处理器、内存、基带、射频等核心部件&#xff0c;直接决定了产品的性能基线、…...

GEO优化实战指南:中小企业如何精准提升本地服务获客效率?

随着线上营销的重要性日益凸显&#xff0c;中小企业面临着前所未有的机遇与挑战。GEO&#xff08;生成式引擎优化&#xff09;作为近年来兴起的一种技术手段&#xff0c;旨在帮助企业更高效地利用AI平台进行品牌推广与客户获取。本文将探讨中小企业如何通过GEO优化策略&#xf…...

RK3562核心板开发指南:从硬件设计到AI部署的全流程解析

1. 项目概述&#xff1a;从一颗芯片到一套完整的开发资源最近在嵌入式圈子里&#xff0c;RK3562这颗芯片的热度持续攀升。作为瑞芯微面向中高端AIoT和工业应用推出的新一代处理器&#xff0c;它凭借其均衡的CPU/GPU/NPU性能和出色的能效比&#xff0c;吸引了不少开发者的目光。…...

STM32F407移植EasyFlash:嵌入式Flash键值存储与磨损均衡实战

1. 项目概述&#xff1a;为什么要在STM32F407上折腾EasyFlash&#xff1f;最近在做一个基于STM32F407的物联网终端设备&#xff0c;功能上需要记录一些运行参数、用户配置&#xff0c;还得在意外断电后能恢复现场。最开始想着用片内Flash模拟EEPROM&#xff0c;自己写读写擦除逻…...

HPM6750 LVGL性能优化:利用TCM与DMA突破嵌入式图形内存瓶颈

1. 项目概述&#xff1a;当LVGL遇上HPM6750&#xff0c;一场关于性能的极限探索最近在嵌入式图形界面开发的圈子里&#xff0c;一个话题热度很高&#xff1a;如何在HPM6750这颗高性能RISC-V MCU上&#xff0c;让LVGL的刷屏性能再上一个台阶&#xff1f;这听起来像是一个常规的优…...

Claude Code 驾驭工程原则全解析:AI Agent、上下文工程、Prompt Cache、权限安全、A/B测试、长期记忆与多智能体架构底层方法论

如果只把 AI Agent 理解成“模型 工具”&#xff0c;很容易错过真正的工程难点。成熟系统的关键&#xff0c;不是让模型偶尔做对&#xff0c;而是让模型在复杂任务里持续稳定、安全、低成本、可观察地做事。一、为什么真正厉害的 AI Agent&#xff0c;不只是模型更强很多人看 …...

抖音下载器技术方案:重构短视频内容采集架构的90%效率提升方案

抖音下载器技术方案&#xff1a;重构短视频内容采集架构的90%效率提升方案 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallba…...