arcgis 中的悬停效果并不如想象中那么容易实现,本文会介绍如何完整的实现如下悬停效果,并对相关的技术细节进行解释,讲解如何避免一些小坑。让你不仅知其然,更知其所以然。
![](https://i-blog.csdnimg.cn/blog_migrate/8c3272ba3e0789ad716a6b96810b9c0c.gif)
文章正文主要涉及对细节和原理的讲解,如果你着急的话,文末有完整的使用 demo,需要自取。
效果拆分
从上面的 gif 里可以看的,一个完整的悬停效果包含三部分:
- 鼠标指针变为小手(pointer)
- 显示标签名称
- 悬停图标放大
在网上有很多的文章都只是简单的解释了某一种效果(大多数都只是简单的更新下鼠标样式),但是大概率是满足不了需求的,下面咱们来一一介绍一下:
1、获取鼠标悬停事件回调
arcgis 中并不能通过类似 .addEventListener
或者 .on('hover-icon')
的形式直接监听图标的悬停事件,所以我们把这部分内容单独拿出来讲。
我们知道 arcgis js 的实例化分两部分:地图创建和视图创建:
const map = new Map({ layers: [baseMap] });
const view = new MapView({ container: "viewDiv", map });
map 对象就是地图实例,包含了核心的地图实现,是不包含任何与显示相关的功能的。而 view 则是视图实例,负责把一个数字化的地图显示在我们眼前的,例如这里使用的 MapView 就是 2D 渲染,而 SceneView 则是负责 3D 地图的渲染。
所以,我们可以通过 view.on('pointer-move', callback)
来监听鼠标在视图上的移动操作,并通过另一个方法 view.hitTest
来检测某个事件和那些地图元素重合:
view.on('pointer-move', async e => {const { results = [] } = await view.hitTest(e);console.log('悬停到的元素', results);
});
这里可以优化一下,使用 lodash 的 throttle 做一个节流,因为 hitTest 毕竟也是有一定消耗的异步操作,如果频繁触发的话不仅会导致卡顿,也会因为异步返回导致已经从元素上移走了,悬停效果却出现的问题。注意这里不能用 arcgis 自带的 promiseUtils.debounce
因为防抖并不适合这个场景,并且它还会阻塞异步事件,会导致悬停效果不跟手。
view.on('pointer-move', _.throttle(async e => {const { results = [] } = await view.hitTest(e);if (results.length <= 0) return;// 后续逻辑
}, 50));
由于 result 是个数组,因为鼠标有可能同时经过了多个元素,所以说我们要在这里元素里匹配到我们要实现悬停效果的那部分。
注意,这里不能直接取 result[0]
,因为鼠标可能会触及多种类型的元素,例如底图图层、或者悬停后显示的名称,所以有可能会出现悬停到了名字上,挡住了后面的图标的情况:
view.on('pointer-move', _.throttle(async e => {const { results = [] } = await view.hitTest(e);if (results.length <= 0) return;// 找到第一个图标,因为有可能悬停到了上一个图标的名称 label 上const { graphic } = results.find(hit => {// 注意这里判断了是否有 name 这个属性,你的场景可能和我不同return hit?.graphic?.attributes?.name;}) || {};if (!graphic) return;// 后续逻辑
}, 50));
现在我们就能准确且轻量的获取到当前悬停到了哪个图标上。
2、悬停时修改鼠标指针
这个效果其实是最简单的,在悬停时修改 css 的 cursor
属性就行了,我们可以封装一下:
const setCoursor = (type) => {// 这个 viewDiv 要换成你的const containerDom = document.getElementById('viewDiv');if (containerDom) containerDom.style.cursor = type;
}
注意,这里获取的目标 dom 是地图的容器,不是 document.body,网上有很多文章都是直接设置 body 或其他全局样式,这其实是不太合适的,因为总会出现鼠标样式没有正确复原的情况,例如点击地图图标后弹出一个 html 的弹窗,此时是不会触发