网站建设中可能遇到的问题,承德 网站建设 网络推广 网页设计,电商数据分析,新闻联播俄罗斯与乌克兰最终效果为上图。
实现该技术#xff0c;需要一些技术#xff0c;我分别罗列一下#xff1a;
canvas#xff1a;需要使用canvas根据json来绘制地球#xff0c;不懂的可以看这篇canvas绘制地球 threejs#xff1a;需要会使用threejs#xff0c;这里并没有使用shader需要一些技术我分别罗列一下
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.jsonexport 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生命周期里面调用就好了。