解决Chrome正常但Firefox中echarts地图图表鼠标位置偏移问题
# 一次 ECharts 中国地图鼠标偏移问题排查:为什么 Chrome 正常,Firefox 却点到隔壁省? 最近在做一个大屏页面时遇到一个很有意思的问题: > 中国地图显示是正常的,但鼠标位置和实际命中的省份有偏移。 > 鼠标明明在山东,实际触发的却是内蒙。 更奇怪的是,这个问题在 Chrome 浏览器里不明显,但在 Firefox 里稳定复现。 最后定位下来,问题不在地图 JSON,也不在 ECharts 的 `geo.center` 配置,而是在大屏父容器的缩放方式上。 ## 页面结构 这个页面是典型的大屏适配方案: - 设计稿固定为 `1920 x 1080` - 页面实际根据浏览器窗口大小缩放 - 父容器通过 CSS `transform` 做适配 核心代码类似这样: ```js const DESIGN_WIDTH = 1920 const DESIGN_HEIGHT = 1080 handleResize() { const windowWidth = window.innerWidth const windowHeight = window.innerHeight const scaleX = windowWidth / DESIGN_WIDTH const scaleY = windowHeight / DESIGN_HEIGHT this.scaleX = scaleX this.scaleY = scaleY this.applyScale() }, applyScale() { const container = this.$refs.screenContainer if (container) { container.style.transform = `scaleX(${this.scaleX}) scaleY(${this.scaleY})` container.style.transformOrigin = 'left top' } }地图组件在这个被缩放的父容器里面。
现象
页面视觉上问题:
- 中国地图显示正常
- 省份边界正常
- hover 高亮错误显示在图形区域附近
比如:
- 鼠标视觉上在山东
- ECharts 实际判断鼠标在更靠北的位置触发在了内蒙区域
根因:父级 transform 改变了视觉坐标
transform: scale(...)是视觉层面的缩放。
它会让元素看起来变大或变小,但元素自身的布局尺寸、内部 canvas 尺寸、事件坐标计算方式,并不总是和视觉结果完全一致。
简单举个例子:
container.style.transform='scale(0.75)'假设地图内部某个点的逻辑坐标是:
x=800y=500经过父级缩放后,它在屏幕上的视觉位置变成:
screenX=800*0.75=600screenY=500*0.75=375用户把鼠标移动到视觉上的这个位置时,浏览器事件拿到的是屏幕坐标。
如果 ECharts/zrender 没有正确把600, 375还原成内部坐标800, 500,命中检测就会偏。
所以用户看到鼠标在山东,但 ECharts 内部判断的位置可能已经跑到了山东上方。
为什么 Chrome 没问题,Firefox 有问题?
关键在 ECharts 底层依赖的 zrender。
zrender 会把浏览器原始鼠标事件转换成自己的坐标:
zrX zrY在 zrender 的事件处理逻辑中,不同浏览器走的分支不一样。简化后大概是这样:
if(firefox&&e.layerX!=null&&e.layerX!==e.offsetX){out.zrX=e.layerX out.zrY=e.layerY}elseif(e.offsetX!=null){out.zrX=e.offsetX out.zrY=e.offsetY}也就是说:
- Chrome 通常走
offsetX / offsetY - Firefox 会优先走
layerX / layerY
而layerX / layerY在 Firefox 中和父级定位、transform、overflow 等上下文强相关。
当父容器存在:
transform:scaleX(...)scaleY(...);Firefox 下的layerX / layerY更容易和 ECharts 期待的 canvas 内部坐标不一致。
所以 Chrome 没问题,并不代表代码没有问题。
更准确地说:
Chrome 的事件坐标刚好比较接近 ECharts 的预期;Firefox 把父级 transform 缩放导致的坐标系问题暴露出来了。
方案一:避免让 ECharts 处在 transform 缩放容器内
从根上讲,最稳的方案是:
不要让 ECharts、地图、canvas 这类强依赖鼠标坐标的组件处在被
transform缩放的父容器里。
可以考虑几种做法:
- 大屏整体仍然按设计稿布局,但地图区域单独不参与父级 transform。
- 地图组件放到未缩放层,通过真实宽高适配。
- 页面从
transform scale方案迁移到响应式布局。
例如使用:
vwvhrem- CSS 变量
- flex/grid 响应式布局
让元素真实尺寸变化,而不是视觉层缩放。
这种方案改动成本更高,但兼容性最好。
方案二:在 ECharts 事件进入前修正坐标
如果短期内不能调整大屏适配方案,可以在地图组件里做兜底处理。
核心思路是:
- 获取图表 DOM 的真实视觉尺寸
- 获取图表 DOM 的内部逻辑尺寸
- 根据比例把鼠标坐标还原成 ECharts 需要的坐标
示例:
fixScaleEventPosition(event){constchartDom=this.chart&&this.chart.getDom&&this.chart.getDom()if(!chartDom)returnconstrect=chartDom.getBoundingClientRect()constwidthScale=rect.width?chartDom.clientWidth/rect.width:1constheightScale=rect.height?chartDom.clientHeight/rect.height:1constsourceEvent=event.touches&&event.touches.length?event.touches[0]:event.changedTouches&&event.changedTouches.length?event.changedTouches[0]:event event.zrX=(sourceEvent.clientX-rect.left)*widthScale event.zrY=(sourceEvent.clientY-rect.top)*heightScale}这个方案的好处是改动小,对现有大屏结构影响最小。
但它属于补丁型方案。
如果后续页面里还有别的 ECharts 图表也需要点击、拖拽、hover,同类问题可能还会继续出现。
方案三:升级echarts到5.x版本
这个问题只出现在5.x以下的版本,可以将依赖升级即可解决
最终判断
这次问题的根因可以总结为一句话:
父级容器使用
transform: scaleX(...) scaleY(...)做大屏适配,导致 Firefox 下 zrender 获取到的鼠标事件坐标与 ECharts 内部坐标不一致,最终出现地图省份命中偏移。
Chrome 没暴露问题,是浏览器事件实现差异带来的“看起来正常”。
Firefox 暴露问题,反而帮我们看到了真正的不稳定点。
建议的修复优先级
我的建议是:
1.如果要兼容 Firefox,尽量避免 ECharts 处在transform缩放容器里
2. 短期无法改大屏结构时,再对 ECharts/zrender 事件坐标做兜底修正
3. 长期把大屏适配从transform scale迁移到真实响应式尺寸方案
小结
这类问题最容易误判成地图数据问题。
但判断方向其实很简单:
- 如果地图显示错,优先看 GeoJSON、投影、center、zoom
- 如果地图显示对,但鼠标命中错,优先看事件坐标、父级 transform、浏览器差异
这次就是后者。
显示系统和事件系统看起来在同一个页面里,实际上它们可能在两个不同的坐标系里各自认真工作。Chrome 帮你糊过去了,Firefox 比较诚实。
