
# 一次 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.transformscale(0.75)假设地图内部某个点的逻辑坐标是x800y500经过父级缩放后它在屏幕上的视觉位置变成screenX800*0.75600screenY500*0.75375用户把鼠标移动到视觉上的这个位置时浏览器事件拿到的是屏幕坐标。如果 ECharts/zrender 没有正确把600, 375还原成内部坐标800, 500命中检测就会偏。所以用户看到鼠标在山东但 ECharts 内部判断的位置可能已经跑到了山东上方。为什么 Chrome 没问题Firefox 有问题关键在 ECharts 底层依赖的 zrender。zrender 会把浏览器原始鼠标事件转换成自己的坐标zrX zrY在 zrender 的事件处理逻辑中不同浏览器走的分支不一样。简化后大概是这样if(firefoxe.layerX!nulle.layerX!e.offsetX){out.zrXe.layerX out.zrYe.layerY}elseif(e.offsetX!null){out.zrXe.offsetX out.zrYe.offsetY}也就是说Chrome 通常走offsetX / offsetYFirefox 会优先走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方案迁移到响应式布局。例如使用vwvhremCSS 变量flex/grid 响应式布局让元素真实尺寸变化而不是视觉层缩放。这种方案改动成本更高但兼容性最好。方案二在 ECharts 事件进入前修正坐标如果短期内不能调整大屏适配方案可以在地图组件里做兜底处理。核心思路是获取图表 DOM 的真实视觉尺寸获取图表 DOM 的内部逻辑尺寸根据比例把鼠标坐标还原成 ECharts 需要的坐标示例fixScaleEventPosition(event){constchartDomthis.chartthis.chart.getDomthis.chart.getDom()if(!chartDom)returnconstrectchartDom.getBoundingClientRect()constwidthScalerect.width?chartDom.clientWidth/rect.width:1constheightScalerect.height?chartDom.clientHeight/rect.height:1constsourceEventevent.touchesevent.touches.length?event.touches[0]:event.changedTouchesevent.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 比较诚实。