maptalks / maptalks/maptalks.js
使用 zoom "stops" 符号(markerWidth/markerHeight)的矢量 marker 打开 InfoWindow 时抛出 "Position is NaN"
Nobody has claimed this yet.
- Dominant language
- HTML
- Stars
- 4.5k
- Forks
- 511
- Avg merge
- 3d 58m
- Merged PRs (30d)
- 1
Description
## 问题描述
当矢量 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` 文件打开,点击按钮(或把鼠标移到红点上)即可触发错误:
```html
html,body,#map{margin:0;height:100%}#b{position:fixed;top:8px;left:8px;z-index:9}
打开 InfoWindow(复现)
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()); }
```
## 期望行为
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"
```
## 根因分析
`calVectorMarkerSize`(`src/core/util/marker.ts`)直接读取原始 symbol:
```ts
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` 时才回退到默认值:
```ts
export function getValueOrDefault(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_SYMBOLS`。`0` 仍是合法数字(`isNumber(0) === true`),所以现有的零值短路分支行为不变。
```diff
- 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` 尺寸可能存在相关问题——本次未深入验证。
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read src/core/util/marker.ts, especially calVectorMarkerSize and the existing isNumber import, then reproduce the issue with the supplied HTML example. Done means vector markers whose dimensions use zoom stops can open an InfoWindow without “Position is NaN”, while zero dimension behavior remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100