threejs 中实现 地图信息轮播功能

效果展示

 

实现思路:将区域信息存放在一个数组中并添加是否高亮展示字段,创建标签是添加与scene.children中其他对象不一样的name属性,轮询删除、创建所有标记的标签,并将是否高亮展示复制给下一个标签

1、创建轮播数组

let mapList = []
function initJson() {
        const loader = new THREE.FileLoader(); // 文件加载器
        loader.load('./json/china/china.json', function (data) {
            const jsonData = JSON.parse(data);
            jsonData.features.forEach(item => {
            if(item.properties.name) {
                mapList.push({
                    name: item.properties.name,
                    flag: 0,
                    x: null,
                    z: null
                })
            }
            mapList[0].flag = 1
        })
        initMap(jsonData);
    });

}

2、生成地图,遍历省份时拿到经纬度,转换为左边,复制到mapLIst中,initmap为遍历省份的方法,initLightPoint方法中获取经纬度/转换坐标

function initMap(chinaJson) {

			var matLine = new LineMaterial({
				//线条材质
				color: '#68c5f6',
				linewidth: 0.002,
				vertexColors: true, // 定点颜色
				dashed: false, //虚线
				alphaToCoverage: true,
			});

			var matLine2 = new LineMaterial({
				color: '#45bff8',
				linewidth: 0.004,
				vertexColors: true,
				dashed: false,
				alphaToCoverage: true,
			});
			// d3-geo转化坐标
			const projection = d3
				.geoMercator()
				.center([102.301409, 36.009473])
				.scale(100)
				.translate([0, 0]);
			// 省   450
			// 全国  90

			// 遍历省份构建模型
			chinaJson.features.forEach((elem) => {
				// console.log(elem)
				const province = new THREE.Object3D(); // 单个省份
				const coordinates = elem.geometry.coordinates; //坐标组
				var properties = elem.properties; //信息
				province.properties = elem.properties.name;
				//这里创建标签
				initLightPoint(properties, projection);
				coordinates.forEach((multiPolygon) => {
					multiPolygon.forEach((polygon) => {
						// polygon 单个坐标
						const positions = [];
						var colors = [];
						const color = new THREE.Color(); // 默认为白色
						var linGeometry = new LineGeometry(); //绘制线条
						for (let i = 0; i < polygon.length; i += 1) {
							const [x, y] = projection(polygon[i]);
							positions.push(x, - y, 4.01); // 定位
							color.setHSL(1, 1, 1);
							// * HSL中使用渐变
							// * h — hue value between 0.0 and 1.0
							// * s — 饱和度 between 0.0 and 1.0
							// * l — 亮度 between 0.0 and 1.0
							colors.push(color.r, color.g, color.b); //颜色
						}
						const mesh = drawExtrudeMesh(polygon, new THREE.Color(0xa0a0a0), projection)
						mesh.name = elem.properties.name
						mesh.rotateX(- Math.PI / 2)
						mesh.opacity = 0
						// console.log(mesh)
						//Line2
						linGeometry.setPositions(positions);
						linGeometry.setColors(colors);
						const line = new Line2(linGeometry, matLine);
						const line2 = new Line2(linGeometry, matLine2);
						line.computeLineDistances();
						line.rotateX(- Math.PI / 2);
						line2.rotateX(- Math.PI / 2);
						line.position.set(0, 0.1, - 3);
						line2.position.set(0, - 3.2, - 3);
						line2.computeLineDistances();
						line.scale.set(1, 1, 1);
						// 直接添加在scene上,方便查找
						scene.add(mesh)
						province.add(line);
						province.add(line2);
					});
				});

				map.add(province);
			});

			scene.add(map);
			chinaJson.features.forEach((elem) => {

				// 新建一个省份容器:用来存放省份对应的模型和轮廓线
				const meshArrs1 = new THREE.Object3D();
				const coordinates = elem.geometry.coordinates;
				const properties = elem.properties;
				// console.log(coordinates)
				coordinates.forEach((multiPolygon) => {

					multiPolygon.forEach((polygon) => {

						const shape = new THREE.Shape();
						var v3ps = [];
						for (let i = 0; i < polygon.length; i++) {

							const [x, y] = projection(polygon[i]);
							if (i === 0) {

								shape.moveTo(x, - y);

							}

							shape.lineTo(x, - y);
							v3ps.push(new THREE.Vector3(x, - y, 4.02));

						}

						const extrudeSettings = {
							depth: 3, //该属性指定图形可以拉伸多高,默认值是100
							bevelEnabled: false, //是否给这个形状加斜面,默认加斜面。
						};
						//拉升成地图
						const geometry = new THREE.ExtrudeGeometry(
							shape,
							extrudeSettings
						);
						const mesh = new THREE.Mesh(geometry, [material, material1]);
						// console.log(polygon)
						mesh.rotateX(- Math.PI / 2);
						mesh.name = polygon.name
						mesh.position.set(0, 1, - 3);
						meshArrs.add(mesh);

					});
				});
				map.add(meshArrs1);

			});
			scene.add(map);
		}

3、initLightPoint方法

function initLightPoint(properties, projection) {
			// console.log('创建光柱')
			// 创建光柱
			const heightScaleFactor = 1 + random(1, 5) / 5;
			const lightCenter = properties.centroid || properties.center;
			const areaName = properties.name;
			// let lightCenter = properties.centroid;
			// projection用来把经纬度转换成坐标
			const [x, y] = projection(lightCenter);
			const light = createLightPillar(x, y, heightScaleFactor);
			light.position.z -= 3;
			// light.position.y = 13.31;
			map.add(light);

            // 赋值mapList
            mapList.forEach(item => {
				if(item.name === areaName) {
					item.x = x
                    item.z = z
				}
			})

}

4、创建标签

function setPop() {
	mapList.forEach(item => {
		if(item.name === '' ){
			return
		}
    // 判断是否为高亮显示的标签 
	if(item.flag === 0) {

		const tag = document.createElement('div');
		// tag.innerHTML = name;
		// tag.className = className
		// tag.style.pointerEvents = 'none';
		tag.style.border = '1px solid #00EEFF';
		tag.style.backgroundColor = '#07113295'
		tag.style.padding = '0px 5px'
		tag.style.fontSize = '8px'
		tag.style.fontWeight = '800'
		// tag.style.visibility = 'hidden'
		tag.style.position = 'absolute';
		tag.style.zIndex = '1'
		const label = new CSS2DObject(tag);
		let name = ''
        // 处理身份名称
		if(item.name.split('省').length == 2) {
			name = item.name.split('省')[0]
		} else if(item.name.split('维吾尔').length == 2) {
            name = item.name.split('维吾尔')[0]
		} else if(item.name.split('回族').length == 2) {
            name = item.name.split('回族')[0]
		} else if(item.name.split('壮族').length == 2) {
             name = item.name.split('壮族')[0]
		} else if(item.name.split('市').length == 2) {
             name = item.name.split('市')[0]
		} else if(item.name.split('内蒙古').length == 2) {
             name = '内蒙古'
		} else if(item.name.split('西藏').length == 2) {
             name = '西藏'
		}  else{
			name = item.name.split('特别行政区')[0]
		}
		label.element.innerHTML = `${name}  5397人`;
		label.element.style.visibility = 'visible';
		label.position.set(item.x, 7, item.z);
		label.position.z -= 3;
        // 所有新建的标签中都拼接‘标签’,方便查找
		label.name = item.name + '标签';
		// console.log(label)
		scene.add(label);

		} else if (item.flag === 1) {
			setTimeout(() => {
			// console.log(item)
			const tag = document.createElement('div');
			tag.style.border = '1px solid #00EEFF';
			tag.style.backgroundColor = '#CC8D1195'
			tag.style.padding = '0px 5px'
		    tag.style.fontSize = '8px'
			tag.style.fontWeight = '800'
			tag.style.textAlign = 'center'
			// tag.style.visibility = 'hidden'
			tag.style.position = 'absolute';
			tag.style.zIndex = '99999'
			let name = ''
			if(item.name.split('省').length == 2) {
				name = item.name.split('省')[0]
			} else if(item.name.split('维吾尔').length == 2) {
				 name = item.name.split('维吾尔')[0]
			} else if(item.name.split('回族').length == 2) {
				 name = item.name.split('回族')[0]
			} else if(item.name.split('壮族').length == 2) {
				 name = item.name.split('壮族')[0]
			} else if(item.name.split('市').length == 2) {
				 name = item.name.split('市')[0]
			} else if(item.name.split('内蒙古').length == 2) {
				 name = '内蒙古'
			} else if(item.name.split('西藏').length == 2) {
				 name = '西藏'
			}  else{
				name = item.name.split('特别行政区')[0]
			}
			const label = new CSS2DObject(tag);
			label.element.innerHTML = `${name}游客接待量 <br/>  12560人`;
			label.element.style.visibility = 'visible';
			label.position.set(item.x, 8, item.z);
			label.position.z -= 3;
			label.name = item.name + '标签';
			// console.log(label)
			scene.add(label);
			}, 10);
		}
	})
}

5、轮播

function setLb() {
	setInterval(() => {
        // 轮询判断是否为省份信息标签,如果是就删除掉,remove为scene中的方法
	    for(let i = 0; i < scene.children.length; i++) {
		    if(scene.children[i].name.indexOf('标签') != -1) {
		    	scene.remove(scene.children[i])
			    i -= 1
		    }
    	}
        // 将当前高亮显示开关后移一位
		for(let i = 0; i < mapList.length; i++ ) {
			if(mapList[i].flag / 1 == 1){
				mapList[i].flag = 0
					if(i == mapList.length - 1) {
						mapList[0].flag = 1
					} else {
						mapList[i + 1].flag = 1
						i= 0
					}
                    // 创建标签
					setPop()
					break
			}
		}
    // 5秒轮播一次
    }, 5000);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现轮播功能的方法有很多种,常见的一种是使用JavaScript和CSS,具体步骤如下: 1. HTML结构:先创建一个包含轮播图片的容器和一个用于展示当前图片的标签。 ```html <div class="slider"> <img src="img1.jpg" alt="image1"> <img src="img2.jpg" alt="image2"> <img src="img3.jpg" alt="image3"> </div> <div class="slider-nav"></div> ``` 2. CSS样式:设置轮播容器的宽度和高度,以及每张图片的样式,同时隐藏除第一张图片以外的其它图片。 ```css .slider { width: 100%; height: 400px; position: relative; overflow: hidden; } .slider img { width: 100%; height: 100%; position: absolute; top: 0; left: 0; opacity: 0; transition: opacity .5s ease-in-out; } .slider img:first-child { opacity: 1; } ``` 3. JavaScript实现轮播:通过JavaScript控制图片的显示和隐藏,以及添加导航按钮。 ```javascript var slider = document.querySelector('.slider'); var sliderImages = slider.querySelectorAll('img'); var sliderNav = document.querySelector('.slider-nav'); // 添加导航按钮 for (var i = 0; i < sliderImages.length; i++) { var navItem = document.createElement('span'); navItem.dataset.index = i; navItem.addEventListener('click', function() { showImage(this.dataset.index); }); sliderNav.appendChild(navItem); } // 显示图片 function showImage(index) { for (var i = 0; i < sliderImages.length; i++) { if (i == index) { sliderImages[i].style.opacity = 1; } else { sliderImages[i].style.opacity = 0; } } } // 自动轮播 var currentIndex = 0; setInterval(function() { currentIndex++; if (currentIndex >= sliderImages.length) { currentIndex = 0; } showImage(currentIndex); }, 3000); ``` 通过上述步骤,即可实现一个简单的轮播功能

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值