Google Maps JS API旋转倾斜矢量地图时图片覆盖物变形修复方法
问题描述
- 基于Google Maps JavaScript API自定义覆盖物能力在矢量地图上添加图片覆盖层时,当地图执行旋转、倾斜操作,图片覆盖物会出现明显变形,变形效果参考截图:

- 排查确认问题来自原有
draw方法的实现逻辑:仅取覆盖物边界的西南(SouthWest)、东北(NorthEast)两个点做坐标转换,通过两点计算div的位置、宽高后附加旋转transform样式,原有代码如下:
draw() { const overlayProjection = this.getProjection(); const sw = overlayProjection.fromLatLngToDivPixel( this.bounds.getSouthWest() ); const ne = overlayProjection.fromLatLngToDivPixel( this.bounds.getNorthEast() ); if (this.div) { this.div.style.left = sw.x + "px"; this.div.style.top = ne.y + "px"; this.div.style.width = ne.x - sw.x + "px"; this.div.style.height = sw.y - ne.y + "px"; this.div.style.transform = 'rotate(' + this.angle + 'deg)'; } }
- 两点定位的逻辑无法适配地图倾斜、旋转时的投影变形,需要实现基于四个顶点坐标的适配方案。
根因分析
原有实现默认把地理边界的屏幕投影当成了轴对齐的正矩形,认为只需要宽高+单角度旋转就能贴合。但矢量地图开启旋转、倾斜能力后,指定地理边界的屏幕投影是任意角度的凸四边形,甚至带透视形变,仅靠SW、NE两个点根本无法描述四边形的实际形态,必然出现拉伸、错位。
解决方案
核心逻辑是放弃动态修改div宽高、定位+单角度旋转的实现,改用CSS透视变换,把图片精准映射到四个角点对应的屏幕位置:
- 在
draw方法中不再只取两个角点,而是提取覆盖物边界的全部四个角点经纬度,全部通过投影接口转换为屏幕像素坐标,四个角点分别为西南(SW)、东南(SE)、东北(NE)、西北(NW)。 - 给覆盖物div设置固定基准尺寸,和所用图片的原始像素尺寸完全一致,不再动态修改
left/top/width/height属性,将div的变换原点固定在左上角(transform-origin: 0 0)。 - 计算从图片原始矩形(四个角点为
(0,0)/(图片宽,0)/(图片宽,图片高)/(0,图片高))到屏幕上四个像素点围成四边形的单应性(透视)变换矩阵,将矩阵格式化为CSSmatrix3d支持的格式,赋值给div的transform属性即可。
改造后的draw方法参考实现:
draw() { const overlayProjection = this.getProjection(); // 提取四个角点的经纬度 const swLatLng = this.bounds.getSouthWest(); const neLatLng = this.bounds.getNorthEast(); const seLatLng = new google.maps.LatLng(swLatLng.lat(), neLatLng.lng()); const nwLatLng = new google.maps.LatLng(neLatLng.lat(), swLatLng.lng()); // 所有角点转屏幕div像素坐标 const pSw = overlayProjection.fromLatLngToDivPixel(swLatLng); const pSe = overlayProjection.fromLatLngToDivPixel(seLatLng); const pNe = overlayProjection.fromLatLngToDivPixel(neLatLng); const pNw = overlayProjection.fromLatLngToDivPixel(nwLatLng); if (this.div) { // 固定div尺寸为图片原始尺寸 const imgNaturalWidth = this.imgEl.naturalWidth; const imgNaturalHeight = this.imgEl.naturalHeight; this.div.style.width = `${imgNaturalWidth}px`; this.div.style.height = `${imgNaturalHeight}px`; this.div.style.left = '0px'; this.div.style.top = '0px'; this.div.style.transformOrigin = '0 0'; // 计算透视变换矩阵:源点为图片原始四角,目标点为屏幕投影四角 const transformMatrix = this.calcPerspectiveMatrix( /* 源点 */ [0, 0], [imgNaturalWidth, 0], [imgNaturalWidth, imgNaturalHeight], [0, imgNaturalHeight], /* 目标点 */ [pSw.x, pSw.y], [pSe.x, pSe.y], [pNe.x, pNe.y], [pNw.x, pNw.y] ); this.div.style.transform = `matrix3d(${transformMatrix.join(',')})`; } }
注:
calcPerspectiveMatrix是四点单应性矩阵计算函数,输入4组源点、4组目标点即可输出符合CSS 3D变换要求的16位矩阵值,常规开源实现仅几十行代码,无需引入大型依赖。
该方案下,每次地图触发重绘都会实时更新四个角点的屏幕坐标并重新计算变换矩阵,无论地图旋转、倾斜、缩放、平移,图片都能精准贴合指定的地理边界,不会出现变形。
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

