和后端一起遇到过的麻烦
本来是想写在上一篇里面的,但是上篇已经写得很长了…
而且想到未来可能会遇到更多要解决的问题。就重新记录了一篇文章
优化初始地图打开速度
数据量的问题通过分层解决,我们做了个全美人种分布的图层,首页显示了百万个点。这些点不仅影响了页面的打开速度,还对服务器造成了压力,同时浏览器渲染这些点也比较吃力
把后端做了多个 vector 源,前端写多个 layer,初始只展示数据量最小的源
接着把 script 标签从 api.mapbox.com 的 js 文件迁移到本地,从 network 里看到,mapbox-gl.js 这个文件的加载速度减少了好几秒
1 2 3 4 5
| <script src="/js/plugins/mapbox-2.3.1/mapbox-gl.js"></script>
<script src="/js/plugins/mapbox-2.3.1/mapbox-gl-geocoder-4.7.2.min.js"></script>
|
图标卡顿重叠
另一个数据层有三十多万个数据点,并且用图标分类显示每个点。
1 2 3 4 5 6 7 8 9 10 11 12
| "type": "symbol", "layout": { "icon-image": "xxx-icon", "icon-size": { "stops": [ [5, 0.6], [14, 0.8] ] }, "icon-allow-overlap": true, }
|
我加了icon-allow-overlap
的选项,图标确实不重叠了,页面直接卡爆了…
经过测试,是"type": "symbol"
渲染的问题,对比 circle,symble 会卡顿很多。
symble 适合稍微小一点的数据集,大量的数据会造成卡顿。
添加层控制的按钮
需求是点击按钮隐藏层,再次点击显示层,这个例子可以拿来参考
关键方法是
1 2 3 4 5
| map.setLayoutProperty( layerId, "visibility", "visible" );
|
但是地图一个功能有多个层,我把层分类和层 id 写在了 button 元素上,点击按钮的时候先处理一下层的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <a class="active" href="javascript:;" layer="Ethnicity" layer-id="Ethnicity_5000,Ethnicity_1000,Ethnicity_500,Ethnicity_200" ><i class="iconfont"></i><span>人种层开关</span></a > <a class="active" href="javascript:;" layer="Colleges" layer-id="Colleges,Colleges-count" ><i class="iconfont"></i><span>院校层开关</span></a > <a class="" href="javascript:;" layer="Companies" layer-id="Companies,Companies-count" ><i class="iconfont"></i><span>公司层开关</span></a > <a class="" href="javascript:;" layer="Crimes" layer-id="Crimes" ><i class="iconfont"></i><span>犯罪数据层开关</span></a >
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| layerSwitch() { const layerBtns = document.querySelectorAll('#menu a') const map = this.map layerBtns.forEach(link => { link.addEventListener('click', function (e) { e.preventDefault() e.stopPropagation() const layer = link.getAttribute('layer') const ids = link.getAttribute('layer-id').split(',') const legendsElement = document.querySelector(`.${layer}-legends`) ids.forEach(name => { const id = name.trim() const visibility = map.getLayoutProperty(id, 'visibility') if (visibility === 'visible' || visibility === undefined) { map.setLayoutProperty(id, 'visibility', 'none') this.className = '' legendsElement && (legendsElement.style.display = 'none') } else { this.className = 'active' map.setLayoutProperty(id, 'visibility', 'visible') legendsElement && (legendsElement.style.display = 'flex') } }) }) }) }
|
地图对数据集大小的限制,导致数据集只能传递一个参数
这是后端遇到的问题,好像 vector 的数据集大小每超过一定限制,都会减少数据集中每个 feature 携带的属性。
传过来的数据集只有一个属性…
后端把需要展示的数据排列在字符串中,用特殊符号分割。
前端使用 mapbox 的表达式从这个字符串中根据特殊符号 split 出属性…这是一个不是办法的办法呢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| data = 'id@param1@param2@...'
data.split('@')[0]
['slice', ['get', 'data'], 0,['index-of', '@', ['get', 'data']]]
'circle-color': [ 'match', ['slice', ['get', 'data'], 0,['index-of', '@', ['get', 'data']]], '2', '#686de0', '3', '#535c68', '4', '#6ab04c', '5', '#eb4d4b', '6', '#be2edd', '7', '#f0932b', '8', '#f9ca24', '9', '#22a6b3', '10', '#f9ca24', '#f9ca24', ],
|
mapbox 地图中,滚轮有缩放的功能,但是在地图自带的弹出框上滚动鼠标滚轮,页面会滚动。这是非常不友好的交互…
这个还蛮简单的,禁用掉滚动就好啦
审查元素发现 popup 实际上是一个 html 弹出框,是 html 那就简单了。给地图容器元素添加点击事件,冒泡给 popup 包裹的元素,禁用掉滚轮事件
注意滚轮的事件是wheel
1 2 3 4 5 6 7 8 9 10
| disablePopupScroll() { const callback = (e) => { if(e.target.closest('.mapboxgl-popup')) { e.preventDefault() return false } } document.querySelector('#map').addEventListener('wheel', callback) document.querySelector('#map').addEventListener('touchmove', callback) }
|
根据接口数据,初始化隐藏某些点
getHidePoint
是一个封装了 Ajax 的 promise
写一个 match 表达式,匹配数据给到的点,用filter
或者icon-image
隐藏掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| let hide = []; const expression = ["match", ["get", "code"], "placeholder", ""]; getHidePoint() .then((res) => { hide = res; Object.values(hide).forEach((item) => { expression.push(item.code, ""); map.addLayer({ layout: { "icon-image": expression, "icon-allow-overlap": true, }, }); }); }) .catch((err) => console.log(err));
|