Skip to content

使用 zoom "stops" 符号(markerWidth/markerHeight)的矢量 marker 打开 InfoWindow 时抛出 "Position is NaN" #2911

Description

@yangjh168

问题描述

当矢量 marker 的 markerWidth / markerHeight 使用 zoom stops 函数式符号({ stops: [[zoom, value], ...] })时,打开(或 hover 触发打开)挂载在该 marker 上的 InfoWindow 会抛出错误:

Uncaught Error: Position is NaN

marker 本身渲染正常(maptalks 会把 stops 按当前 zoom 解析为具体数值),只有 InfoWindow 的偏移量计算会崩溃。

maptalks 版本

1.12.1(猜测更早的 1.x 版本也存在)

复现步骤

保存为 .html 文件打开,点击按钮(或把鼠标移到红点上)即可触发错误:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="https://unpkg.com/maptalks@1.12.1/dist/maptalks.css"/>
<style>html,body,#map{margin:0;height:100%}#b{position:fixed;top:8px;left:8px;z-index:9}</style>
</head>
<body>
<div id="map"></div>
<button id="b" onclick="openIW()">打开 InfoWindow(复现)</button>
<script src="https://unpkg.com/maptalks@1.12.1/dist/maptalks.js"></script>
<script>
var map = new maptalks.Map('map', { center:[0,0], zoom:6 });
var marker = new maptalks.Marker([0,0], {
  symbol: {
    markerType: 'ellipse',
    markerWidth:  { stops: [[12,6],[15,8]] },   // 合法的 zoom 函数式符号
    markerHeight: { stops: [[12,6],[15,8]] },
    markerFill: '#f00'
  }
});
new maptalks.VectorLayer('v',[marker]).addTo(map);
marker.setInfoWindow({ custom:true, content:'<div style="padding:8px">hi</div>', autoPan:false });
marker.on('mouseover', openIW); // hover 同样会复现
function openIW(){ marker.openInfoWindow(marker.getCoordinates()); }
</script>
</body>
</html>

期望行为

InfoWindow 正常在 marker 旁边打开,不报错。

实际行为

Uncaught Error: Position is NaN

抛错的调用链:

InfoWindow.getOffset()
  → painter.getFixedExtent()
    → getVectorMarkerFixedExtent()
      → calVectorMarkerSize()          // 返回 [NaN, NaN]
        → getVectorMarkerAnchor(symbol, NaN, NaN)
          → getAlignPoint(Size(NaN, NaN), ...)
            → new Point(NaN, NaN)        // 抛出 "Position is NaN"

根因分析

calVectorMarkerSizesrc/core/util/marker.ts)直接读取原始 symbol:

const width  = getValueOrDefault(symbol['markerWidth'],  DEFAULT_MARKER_SYMBOLS.markerWidth);
const height = getValueOrDefault(symbol['markerHeight'], DEFAULT_MARKER_SYMBOLS.markerHeight);
...
const w = Math.round(width + lineWidth + shadow + padding * 2);  // 对象 + 数字 = NaN

getValueOrDefault 只在值为 undefined 时才回退到默认值:

export function getValueOrDefault<T>(v: T, d: T) { return v === undefined ? d : v; }

{ stops: [...] } 对象不是 undefined,于是被原样返回 → 参与算术得到 NaN → 一路传递到 new Point(NaN, NaN) → 抛错。

修复建议

calVectorMarkerSize 中,用 isNumber()(已在 marker.ts 中导入)对 markerWidth / markerHeight / markerLineWidth 做类型判断,非数字时回退到 DEFAULT_MARKER_SYMBOLS0 仍是合法数字(isNumber(0) === true),所以现有的零值短路分支行为不变。

-    const width = getValueOrDefault(symbol['markerWidth'], DEFAULT_MARKER_SYMBOLS.markerWidth);
-    const height = getValueOrDefault(symbol['markerHeight'], DEFAULT_MARKER_SYMBOLS.markerHeight);
+    const width = isNumber(symbol['markerWidth']) ? symbol['markerWidth'] : DEFAULT_MARKER_SYMBOLS.markerWidth;
+    const height = isNumber(symbol['markerHeight']) ? symbol['markerHeight'] : DEFAULT_MARKER_SYMBOLS.markerHeight;
     if (width === 0 || height === 0) { ... }
-    const lineWidth = getValueOrDefault(symbol['markerLineWidth'], DEFAULT_MARKER_SYMBOLS.markerLineWidth),
+    const lineWidth = isNumber(symbol['markerLineWidth']) ? symbol['markerLineWidth'] : DEFAULT_MARKER_SYMBOLS.markerLineWidth,

stops 符号是 maptalks 文档支持的功能,InfoWindow / extent 的计算应当优雅降级,而不是抛出未捕获异常导致用户代码崩溃。

补充

  • 只要矢量 marker(markerType: ellipse/pin/pie/...)用 zoom stops 控制尺寸,且挂了 InfoWindow 并打开,就能复现。
  • getImageMarkerFixedExtent 里也有类似写法(markerWidth || (img ? img.width : 0)),图片 marker 若用 stops 尺寸可能存在相关问题——本次未深入验证。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions