从零开始Vue项目中使用MapboxGL开发三维地图教程(七)创建一个自动旋转的地球、添加一个3D模型、实现在两个地图之间滑动和同步来比较两个地图

1、创建一个自动旋转的地球地图

实现功能:地球仪和camera动画结合在一起,创建旋转行星效果。

实现思路:通过在动画结束时调用easeTo,旋转动画将无限期地继续。旋转在用户交互时暂停,并且在高缩放级别时减慢到停止。

首先在dom中添加一个button:

<button id="btn-spin">Pause rotation</button>

实现代码:

map.on('style.load', () => {
    
    
					map.setFog({
    
    }); // Set the default atmosphere style
				});

				// The following values can be changed to control rotation speed:

				// At low zooms, complete a revolution every two minutes.
				const secondsPerRevolution = 120;
				// Above zoom level 5, do not rotate.
				const maxSpinZoom = 5;
				// Rotate at intermediate speeds between zoom levels 3 and 5.
				const slowSpinZoom = 3;

				let userInteracting = false;
				let spinEnabled = true;

				function spinGlobe() {
    
    
					const zoom = map.getZoom();
					if (spinEnabled && !userInteracting && zoom < maxSpinZoom) {
    
    
						let distancePerSecond = 360 / secondsPerRevolution;
						if (zoom >
							slowSpinZoom) {
    
    
							// Slow spinning at higher zooms
							const zoomDif =
								(maxSpinZoom - zoom) / (maxSpinZoom - slowSpinZoom);
							distancePerSecond *= zoomDif;
						}
						const center = map.getCenter();
						center.lng -= distancePerSecond;
						// Smoothly animate the map over one second.
						// When this animation is complete, it calls a 'moveend' event.
						map.easeTo({
    
    
							center,
							duration: 1000,
							easing: (n) => n
						});
					}
				}

				// Pause spinning on interaction
				map.on('mousedown', () => {
    
    
					userInteracting = true;
				});

				// Restart spinning the globe when interaction is complete
				map.on('mouseup', () => {
    
    
					userInteracting = false;
					spinGlobe();
				});

				// These events account for cases where the mouse has moved
				// off the map, so 'mouseup' will not be fired.
				map.on('dragend', () => {
    
    
					userInteracting = false;
					spinGlobe();
				});
				map.on('pitchend', () => {
    
    
					userInteracting = false;
					spinGlobe();
				});
				map.on('rotateend', () => {
    
    
					userInteracting = false;
					spinGlobe();
				});

				// When animation is complete, start spinning if there is no ongoing interaction
				map.on('moveend', () => {
    
    
					spinGlobe();
				});

				document.getElementById('btn-spin').addEventListener('click', (e) => {
    
    
					spinEnabled = !spinEnabled;
					if (spinEnabled) {
    
    
						spinGlobe();
						e.target.innerHTML = 'Pause rotation';
					} else {
    
    
						map.stop(); // Immediately end ongoing animation
						e.target.innerHTML = 'Start rotation';
					}
				});

				spinGlobe();

效果展示:
在这里插入图片描述

2、添加一个3D模型

使用three.js,首先three.js安装到项目中:

# three.js
npm install --save three

在项目中引入:

	import * as THREE from 'three';
	import {
    
    
		GLTFLoader
	} from 'three/examples/jsm/loaders/GLTFLoader.js';

使用带有three.js的自定义样式层将三维模型添加到地图中:

// parameters to ensure the model is georeferenced correctly on the map
				const modelOrigin = [104.9819, 37.39847];
				const modelAltitude = 0;
				const modelRotate = [Math.PI / 2, 0, 0];

				const modelAsMercatorCoordinate = this.$mapboxgl.MercatorCoordinate.fromLngLat(
					modelOrigin,
					modelAltitude
				);

				// transformation parameters to position, rotate and scale the 3D model onto the map
				const modelTransform = {
    
    
					translateX: modelAsMercatorCoordinate.x,
					translateY: modelAsMercatorCoordinate.y,
					translateZ: modelAsMercatorCoordinate.z,
					rotateX: modelRotate[0],
					rotateY: modelRotate[1],
					rotateZ: modelRotate[2],
					/* Since the 3D model is in real world meters, a scale transform needs to be
					 * applied since the CustomLayerInterface expects units in MercatorCoordinates.
					 */
					scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
				};

				// const THREE = window.THREE;

				// configuration of the custom layer for a 3D model per the CustomLayerInterface
				const customLayer = {
    
    
					id: '3d-model',
					type: 'custom',
					renderingMode: '3d',
					onAdd: function (map, gl) {
    
    
						this.camera = new THREE.Camera();
						this.scene = new THREE.Scene();

						// create two three.js lights to illuminate the model
						const directionalLight = new THREE.DirectionalLight(0xffffff);
						directionalLight.position.set(0, -70, 100).normalize();
						this.scene.add(directionalLight);

						const directionalLight2 = new THREE.DirectionalLight(0xffffff);
						directionalLight2.position.set(0, 70, 100).normalize();
						this.scene.add(directionalLight2);

						// use the three.js GLTF loader to add the 3D model to the three.js scene
						const loader = new GLTFLoader();
						loader.load(
							'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',
							(gltf) => {
    
    
								this.scene.add(gltf.scene);
							}
						);
						this.map = map;

						// use the Mapbox GL JS map canvas for three.js
						this.renderer = new THREE.WebGLRenderer({
    
    
							canvas: map.getCanvas(),
							context: gl,
							antialias: true
						});

						this.renderer.autoClear = false;
					},
					render: function (gl, matrix) {
    
    
						const rotationX = new THREE.Matrix4().makeRotationAxis(
							new THREE.Vector3(1, 0, 0),
							modelTransform.rotateX
						);
						const rotationY = new THREE.Matrix4().makeRotationAxis(
							new THREE.Vector3(0, 1, 0),
							modelTransform.rotateY
						);
						const rotationZ = new THREE.Matrix4().makeRotationAxis(
							new THREE.Vector3(0, 0, 1),
							modelTransform.rotateZ
						);

						const m = new THREE.Matrix4().fromArray(matrix);
						const l = new THREE.Matrix4()
							.makeTranslation(
								modelTransform.translateX,
								modelTransform.translateY,
								modelTransform.translateZ
							)
							.scale(
								new THREE.Vector3(
									modelTransform.scale,
									-modelTransform.scale,
									modelTransform.scale
								)
							)
							.multiply(rotationX)
							.multiply(rotationY)
							.multiply(rotationZ);

						this.camera.projectionMatrix = m.multiply(l);
						// this.renderer.resetState();
						this.renderer.render(this.scene, this.camera);
						this.map.triggerRepaint();
					}
				};

				map.on('style.load', () => {
    
    
					map.addLayer(customLayer, 'waterway-label');
				});

上效果:
在这里插入图片描述

3、一个页面创建两个底图之间滑动

以通过左右滑动来比较两个地图

使用mapbox插件 mapbox-gl-compare
安装

npm i mapbox-gl-compare -D
npm i mapbox-gl-sync-move -D

导入

import mapboxgl from ‘mapbox-gl’
import Compare from ‘mapbox-gl-compare’

创建两个div:

<div id="comparison-container">
    <div id="before" class="map"></div>
    <div id="after" class="map"></div>
</div>

核心代码:

	mapboxgl.accessToken = 'xxxxxxx';
    const beforeMap = new mapboxgl.Map({
    
    
        container: 'before',
        // Choose from Mapbox's core styles, or make your own style with Mapbox Studio
        style: 'mapbox://styles/mapbox/streets-v12',
        center: [0, 0],
        zoom: 0
    });

    const afterMap = new mapboxgl.Map({
    
    
        container: 'after',
        style: 'mapbox://styles/mapbox/satellite-streets-v12',
        center: [0, 0],
        zoom: 0
    });

    // A selector or reference to HTML element
    const container = '#comparison-container';

    const map = new mapboxgl.Compare(beforeMap, afterMap, container, {
    
    
        // Set this to enable comparing two maps by mouse movement:
        // mousemove: true
    });

效果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43025151/article/details/131272095