three.js3D地图省市下钻加上钻踩坑记录
1,three 安装失败
首先脚手架种安装three,我不知道是网络问题还是什么,three我总是安装不上,
于是我就下载了网上别的博主得成品代码,把里面 de 包拿出来放在我自己项目中
安装包链接在此,如有需要自取 ,https://files.cnblogs.com/files/jickma/three.rar?t=1702967875&download=true
解压之后直接替换本地 node_modules 包中未完成安装得包即可使用,包的版本为 three 0.159.0
(由于安装包与 package中版本不同,造成一个问题 背景颜色与本地颜色差异过大,具体为什么不知道,更新版本之后可解决 详情 https://segmentfault.com/q/1010000044485869 )
2,地图由于加载不同文件 比如说中国地图,河南省地图,郑州市地图,由于经纬度差异比较大,所以会造成 地图大小不一问题,
如图 河南省很大,其中郑州市仅一小块,下钻之后,郑州市还是这么大,这个就是比例不对问题,
地图下钻比例问题修改如下
1 // 地图模型 2 addMapGeometry(jsondata) { 3 // 初始化一个地图对象 4 this.map = new THREE.Object3D(); 5 // 墨卡托投影转换 6 const { averageCoordinate, scaleMin } = this.getCenterMap(jsondata); 7 const projection = d3 8 .geoMercator() 9 .center(averageCoordinate) 10 .scale(scaleMin * 0.5725) 11 .translate([1, -0.4]); // 根据地球贴图做轻微调整 12 jsondata.features.forEach((elem) => { 13 // 各种循环地图加载模块 14 /***你得代码 **** */
15 }); 16 },
1 // 计算中心点坐标,获取经纬度最大最小值缩放比例 2 getCenterMap(jsondata) { 3 let minLat = Infinity; 4 let maxLat = -Infinity; 5 let minLon = Infinity; 6 let maxLon = -Infinity; 7 // 获取所有坐标点 8 const allCoordinates = []; 9 jsondata.features.forEach((elem) => { 10 const coordinates = elem.geometry.coordinates; 11 coordinates.forEach((multiPolygon) => { 12 multiPolygon.forEach((polygon) => { 13 allCoordinates.push(...polygon); 14 for (let i = 0; i < polygon.length; i++) { 15 const [x, y] = polygon[i]; 16 minLat = Math.min(minLat, x); 17 maxLat = Math.max(maxLat, x); 18 minLon = Math.min(minLon, y); 19 maxLon = Math.max(maxLon, y); 20 } 21 }); 22 }); 23 }); 24 this.container = document.getElementById("container"); 25 const scaleX = this.container.clientWidth / (maxLat - minLat); 26 const scaleY = this.container.clientHeight / (maxLon - minLon); 27 const scaleMin = Math.min(scaleX, scaleY); 28 // 计算坐标点的平均值 29 const averageCoordinate = allCoordinates.reduce( 30 (acc, coordinate) => { 31 return [ 32 acc[0] + coordinate[0] / allCoordinates.length, 33 acc[1] + coordinate[1] / allCoordinates.length, 34 ]; 35 }, 36 [0, 0] 37 ); 38 return { averageCoordinate, scaleMin }; 39 },
修改这个问题得根源在于 加载地图之前 将墨卡托投影 缩放直接给改了
而 修改得代码 就在 this.getCenterMap(jsondata) 获取缩放比例,将这个缩放比例 赋值给墨卡托即可完成地图大小不一问题
千万不要在地图加载之后修改缩放比例,文本中红色代码,这样会造成地图缩放成功,但是别的地图上的文字或者图标什么得位置错误
代码还是有问题,暂时不知道如何解决 看链接 https://segmentfault.com/q/1010000044486494
3,地图初步测试成功之后,放进容器之后出现位置错误问题
因为是第一次使用 three。js 所以我就使用一个 空的div容纳我这个地图,而这个div 是直接放在 body 里面得,算是测试版 ,在测试之后,往界面中放进去时候出现了 位置错误问题
本来弹出来的省份名应该跟随鼠标位置的,现在却没有跟随,而我在测试代码中却好好的。根源在于取射线的位置不对
测试代码是这样的, 代码第八行 使用了 window.innerWidth 而我放在容器之后 已经不是window了,因此出现位置不对情况
1 2 // 射线 3 setRaycaster() { 4 this.raycaster = new THREE.Raycaster(); 5 this.mouse = new THREE.Vector2(); 6 this.tooltip = document.getElementById('tooltip'); 7 const onMouseMove = (event) => { 8 this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1; 9 this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; 10 this.tooltip.style.left = event.clientX + 2 + 'px'; 11 this.tooltip.style.top = event.clientY + 2 + 'px'; 12 } 13 // 点击地图事件 14 const onClick = (event) => { 15 // console.log(this.lastPick); 16 if (this.lastPick && "point" in this.lastPick) this.mapClickTween(this.lastPick.point); 17 else this.resetCameraTween(); 18 } 19 window.addEventListener('mousemove', onMouseMove, false); 20 window.addEventListener('click', onClick, false); 21 },
应修改为下边代码
1 // 射线 2 setRaycaster() { 3 this.raycaster = new THREE.Raycaster(); 4 this.mouse = new THREE.Vector2(); 5 this.tooltip = document.getElementById("tooltip"); 6 const container = document.getElementById("container"); 7 const that = this; 8 const onMouseMove = debounce((event) => { 9 that.mouse.x = (event.offsetX / container.offsetWidth) * 2 - 1; 10 that.mouse.y = -(event.offsetY / container.offsetHeight) * 2 + 1; 11 that.tooltip.style.left = event.offsetX + 2 + "px"; 12 that.tooltip.style.top = event.offsetY + 2 + "px"; 13 }, 100); 14 // 点击地图事件 15 const onClick = (event) => { 16 /***别的代码 */ 17 }; 18 window.addEventListener("mousemove", onMouseMove, false); 19 window.addEventListener("click", onClick, false); 20 },
放在哪个元素里面, mouse.x 计算时候就应该修改为当前元素的 offsetWidth
由此,可以推断出,测试代码中所有含有 window 的位置都有可能出问题,需要修改
这样的问题也是由于这个原因造成的
4,地图部署之后,无法加载json文件
地图文件是一个json 然后下载之后放在 public中 (我也不知道为什么不放在 assets 中),看网上大佬都是放在这个里面的 我就跟着放进去了
然后本地运行好好的,部署之后问题就来了
1 // 加载地图数据 2 loadMapData() { 3 const loader = new THREE.FileLoader(); 4 loader.load("/static/jsonMap/china.json", data => { 5 const jsondata = JSON.parse(data); 6 this.addMapGeometry(jsondata); 7 }) 8 },
部署之后是访问不到 /static/jsonMap/china.json 这个json的,然后就会报代码循环错误 导致后边的地图无法加载,因为没有json
new THREE.FileLoader().load() 别的大佬都是这么访问的,我也跟着访问 后来由于发布之后无法正确访问 于是我就改成了这样
1 // 加载地图数据 2 loadMapData(str) { 4 this.resetInitMap(); 5 let jsondata = require(`@/assets/json/${str || "china"}.json`); 6 this.addMapGeometry(jsondata); 7 },
因为地图本身就是一个对象数组结构的数据,我直接按照 vue 方式访问这个 json 传递到我得绘画地图函数中即可跳过这个步骤
5,地图下钻之后数据残留问题
由于地图需要下钻 展示中国,点击河南之后展示河南地图,这时候需要清除掉原本在场景的中国地图,以及中国地图上面各种点,线,等等各种渲染
仔细看可以发现这里是有两幅地图的,一副中国地图,一副河南地图,重叠在一起了。
这个时候就需要前端一贯的风格,秉承先清后加的原则 ,在加载地图之前 先清除掉地图然后在加上一副地图
1 // 加载地图数据 2 loadMapData(str) { 3 this.resetInitMap(); 4 let jsondata = require(`@/assets/json/${str || "china"}.json`); 5 this.addMapGeometry(jsondata); 6 },
1 resetInitMap() { 2 let arr = [] 3 this.scene.children.forEach(ele => { 4 if (ele.type != "Mesh" && ele.type != "Points") { 5 arr.push(ele) 6 } 7 }); 8 this.scene.children = [] 9 let list = [] 10 this.cityNumMeshArr = [] 11 this.groupOne.children.forEach(el => { 12 if (el.type != "Object3D") { 13 list.push(el) 14 } 15 }); 16 this.groupOne.children = [] 17 if (this.map) { 18 this.scene.add(...arr) 19 this.groupOne.add(...list) 20 } 21 this.scene.remove(this.map); 22 },
同样的不止地图,还有地图上渲染的别的东西,我得地图中渲染了一些特效,是动态的,但是在下钻之后,动效不会动了,并且新的动效出来了,会重叠在一起,或者留有一些残影,等,
可以一并在重置中给干掉
threejs 里面坑还有很多,暂时只有这么多。欢迎大佬留言