my-openlayer 2.5.4 → 2.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/MyOl.js CHANGED
@@ -443,18 +443,56 @@ class MyOl {
443
443
  */
444
444
  destroy() {
445
445
  try {
446
- // 清理事件监听(仅在已初始化时)
446
+ // 1. 先停掉所有动画 / 解绑事件 / 释放 Overlay,再销毁地图本身
447
+ if (this._selectHandler) {
448
+ try {
449
+ this._selectHandler.destroy();
450
+ }
451
+ catch (e) {
452
+ this.errorHandler.warn('SelectHandler.destroy 失败:', e);
453
+ }
454
+ }
455
+ if (this._line) {
456
+ try {
457
+ this._line.destroyAllFlowLines();
458
+ }
459
+ catch (e) {
460
+ this.errorHandler.warn('Line.destroyAllFlowLines 失败:', e);
461
+ }
462
+ }
463
+ if (this._point) {
464
+ try {
465
+ this._point.destroyAll();
466
+ }
467
+ catch (e) {
468
+ this.errorHandler.warn('Point.destroyAll 失败:', e);
469
+ }
470
+ }
471
+ if (this._polygon) {
472
+ try {
473
+ this._polygon.destroyAll();
474
+ }
475
+ catch (e) {
476
+ this.errorHandler.warn('Polygon.destroyAll 失败:', e);
477
+ }
478
+ }
447
479
  if (this._eventManager) {
448
- this._eventManager.clear();
480
+ try {
481
+ this._eventManager.clear();
482
+ }
483
+ catch (e) {
484
+ this.errorHandler.warn('EventManager.clear 失败:', e);
485
+ }
449
486
  }
450
- // 销毁功能模块
487
+ // 2. 释放模块引用
451
488
  this._point = undefined;
452
489
  this._line = undefined;
453
490
  this._polygon = undefined;
454
491
  this._mapTools = undefined;
455
492
  this._baseLayers = undefined;
456
493
  this._selectHandler = undefined;
457
- // 销毁地图
494
+ this._eventManager = undefined;
495
+ // 3. 解绑 DOM 目标,OL 内部会回收 layers / interactions
458
496
  this.map.setTarget(undefined);
459
497
  this.errorHandler.debug('地图实例已销毁', { map: this.map });
460
498
  }
@@ -37,4 +37,9 @@ export default class Line {
37
37
  addFlowLine(data: MapJSONData, options?: FlowLineOptions): FlowLineLayerHandle | null;
38
38
  addFlowLineByUrl(url: string, options?: FlowLineOptions): Promise<FlowLineLayerHandle | null>;
39
39
  removeFlowLineLayer(layerName: string): void;
40
+ /**
41
+ * 销毁本实例创建的所有流动线动画。供 MyOl.destroy 调用,
42
+ * 确保地图销毁后所有 requestAnimationFrame / postrender 监听被回收。
43
+ */
44
+ destroyAllFlowLines(): void;
40
45
  }
package/core/line/Line.js CHANGED
@@ -141,4 +141,17 @@ export default class Line {
141
141
  }
142
142
  MapTools.removeLayer(this.map, [layerName, `${layerName}__flow-animation`]);
143
143
  }
144
+ /**
145
+ * 销毁本实例创建的所有流动线动画。供 MyOl.destroy 调用,
146
+ * 确保地图销毁后所有 requestAnimationFrame / postrender 监听被回收。
147
+ */
148
+ destroyAllFlowLines() {
149
+ Array.from(this.flowLineRegistry.values()).forEach(handle => {
150
+ try {
151
+ handle.remove();
152
+ }
153
+ catch { /* ignore */ }
154
+ });
155
+ this.flowLineRegistry.clear();
156
+ }
144
157
  }
@@ -33,7 +33,7 @@ export default class MapTools {
33
33
  * 使用 Canvas clip 实现裁剪,支持多个闭合区域
34
34
  * 注意:此方法会修改 baseLayer 的 prerender 和 postrender 事件
35
35
  */
36
- static setMapClip(baseLayer: any, data: MapJSONData): any;
36
+ static setMapClip(baseLayer: BaseLayer, data: MapJSONData): BaseLayer;
37
37
  /**
38
38
  * 移除图层
39
39
  * @param layerName 图层名称
@@ -84,7 +84,7 @@ class MapTools {
84
84
  : event.frameState.coordinateToPixelTransform;
85
85
  ctx.save();
86
86
  ctx.beginPath();
87
- features.forEach((feature) => {
87
+ features.forEach(feature => {
88
88
  const geometry = feature.getGeometry();
89
89
  if (!geometry)
90
90
  return;
@@ -112,15 +112,15 @@ class MapTools {
112
112
  };
113
113
  if (type === 'MultiPolygon') {
114
114
  // MultiPolygon: [Polygon, Polygon] -> Polygon: [OuterRing, InnerRing, ...]
115
- coordinates.forEach((polygonCoords) => {
116
- polygonCoords.forEach((ringCoords) => {
115
+ coordinates.forEach(polygonCoords => {
116
+ polygonCoords.forEach(ringCoords => {
117
117
  drawRing(ringCoords);
118
118
  });
119
119
  });
120
120
  }
121
121
  else if (type === 'Polygon') {
122
122
  // Polygon: [OuterRing, InnerRing, ...]
123
- coordinates.forEach((ringCoords) => {
123
+ coordinates.forEach(ringCoords => {
124
124
  drawRing(ringCoords);
125
125
  });
126
126
  }
@@ -4,7 +4,21 @@ import VectorSource from "ol/source/Vector";
4
4
  import { PointOptions, ClusterOptions, PointData, VueTemplatePointInstance, TwinkleItem, PulsePointOptions, PulsePointLayerHandle } from '../../types';
5
5
  export default class Point {
6
6
  private map;
7
+ /** 由本实例创建的纯图层(addPoint / addClusterPoint)。destroyAll 时统一移除。 */
8
+ private readonly managedLayers;
9
+ /** 由本实例创建的、需要主动 remove 的句柄(Pulse / DOM / Vue 模板)。 */
10
+ private readonly managedDisposables;
11
+ /** VueTemplatePoint 实例复用,避免每次调用 addVueTemplatePoint 都 new 一个无主实例。 */
12
+ private vueTemplatePoint?;
7
13
  constructor(map: Map);
14
+ /** @internal */
15
+ private trackLayer;
16
+ /** @internal */
17
+ private trackDisposable;
18
+ /**
19
+ * 销毁本实例创建的所有图层与动画句柄。供 MyOl.destroy 调用。
20
+ */
21
+ destroyAll(): void;
8
22
  /**
9
23
  * 创建文本样式
10
24
  * @private
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  import Feature from "ol/Feature";
3
2
  import { Point as olPoint } from "ol/geom";
4
3
  import { Text, Style, Fill, Stroke, Icon } from "ol/style";
@@ -13,8 +12,48 @@ import PointOverlay from './PointOverlay';
13
12
  import PointPulseLayer from './PointPulseLayer';
14
13
  export default class Point {
15
14
  constructor(map) {
15
+ /** 由本实例创建的纯图层(addPoint / addClusterPoint)。destroyAll 时统一移除。 */
16
+ this.managedLayers = new Set();
17
+ /** 由本实例创建的、需要主动 remove 的句柄(Pulse / DOM / Vue 模板)。 */
18
+ this.managedDisposables = new Set();
16
19
  this.map = map;
17
20
  }
21
+ /** @internal */
22
+ trackLayer(layer) {
23
+ this.managedLayers.add(layer);
24
+ return layer;
25
+ }
26
+ /** @internal */
27
+ trackDisposable(handle) {
28
+ this.managedDisposables.add(handle);
29
+ return handle;
30
+ }
31
+ /**
32
+ * 销毁本实例创建的所有图层与动画句柄。供 MyOl.destroy 调用。
33
+ */
34
+ destroyAll() {
35
+ this.managedDisposables.forEach(handle => {
36
+ try {
37
+ handle.remove();
38
+ }
39
+ catch { /* ignore */ }
40
+ });
41
+ this.managedDisposables.clear();
42
+ this.managedLayers.forEach(layer => {
43
+ try {
44
+ this.map.removeLayer(layer);
45
+ }
46
+ catch { /* ignore */ }
47
+ });
48
+ this.managedLayers.clear();
49
+ if (this.vueTemplatePoint) {
50
+ try {
51
+ this.vueTemplatePoint.removeAllPoints();
52
+ }
53
+ catch { /* ignore */ }
54
+ this.vueTemplatePoint = undefined;
55
+ }
56
+ }
18
57
  /**
19
58
  * 创建文本样式
20
59
  * @private
@@ -135,20 +174,28 @@ export default class Point {
135
174
  pointFeatureList.push(pointFeature);
136
175
  });
137
176
  const PointVectorLayer = new VectorLayer({
138
- layerName: options.layerName,
177
+ properties: {
178
+ name: options.layerName,
179
+ layerName: options.layerName
180
+ },
139
181
  source: new VectorSource({
140
182
  features: pointFeatureList
141
183
  }),
142
184
  zIndex: options.zIndex || ConfigManager.DEFAULT_POINT_OPTIONS.zIndex,
143
185
  });
144
186
  this.configureLayer(PointVectorLayer, options);
187
+ this.trackLayer(PointVectorLayer);
145
188
  return PointVectorLayer;
146
189
  }
147
190
  addClusterPoint(pointData, options) {
148
191
  if (!ValidationUtils.validatePointData(pointData)) {
149
192
  return null;
150
193
  }
151
- return PointClusterLayer.create(this.map, pointData, options, this.createClusterStyle.bind(this));
194
+ const layer = PointClusterLayer.create(this.map, pointData, options, this.createClusterStyle.bind(this));
195
+ if (layer) {
196
+ this.trackLayer(layer);
197
+ }
198
+ return layer;
152
199
  }
153
200
  /**
154
201
  * 添加高性能闪烁点图层。
@@ -160,80 +207,19 @@ export default class Point {
160
207
  if (!ValidationUtils.validatePointData(pointData)) {
161
208
  return null;
162
209
  }
163
- return PointPulseLayer.create(this.map, pointData, options);
210
+ const handle = PointPulseLayer.create(this.map, pointData, options);
211
+ this.trackDisposable(handle);
212
+ return handle;
164
213
  }
165
- // // 在流域中心添加闪烁点位
166
- // addTwinkleLayerFromPolygon(twinkleList: any[], className: string, key: string, json: MapJSONData, options?: PolygonOptions) {
167
- // new MapTools(this.map).removeLayer('twinklePoint')
168
- // // 计算多边形的中心点坐标
169
- // const calculatePolygonCenter = (polygonCoordinates: any) => {
170
- // const polygon = turf.polygon(polygonCoordinates[0]);
171
- // const centroid = turf.centroid(polygon);
172
- // return centroid.geometry.coordinates;
173
- // };
174
- // const features: any[] = json.features
175
- // const vectorSource = new VectorSource({
176
- // format: new GeoJSON(),
177
- // });
178
- // twinkleList.forEach(item => {
179
- // const feature = features.find((ele: any) => {
180
- // return ele.properties.BASIN === item.idx
181
- // })
182
- // if (!feature) return
183
- // feature.properties.level = item.lev
184
- // const geojson = new GeoJSON();
185
- // const olFeature = geojson.readFeature(feature);
186
- // if (Array.isArray(olFeature)) {
187
- // vectorSource.addFeatures(olFeature);
188
- // } else {
189
- // vectorSource.addFeature(olFeature);
190
- // }
191
- // if (feature) {
192
- // const polygonCenter = calculatePolygonCenter(feature.geometry.coordinates)
193
- // item.lgtd = polygonCenter[0]
194
- // item.lttd = polygonCenter[1]
195
- // }
196
- // })
197
- // const basinLayer = new VectorLayer({
198
- // name: 'twinklePoint',
199
- // layerName: 'twinklePoint',
200
- // source: vectorSource,
201
- // style: function (feature: any) {
202
- // if (options?.style) {
203
- // if (typeof options.style === 'function') {
204
- // return options.style(feature);
205
- // } else {
206
- // return options.style;
207
- // }
208
- // }
209
- // return new Style({
210
- // stroke: new Stroke({
211
- // color: 'rgb(139,188,245)',
212
- // width: 3
213
- // }),
214
- // fill: new Fill({ color: 'rgba(255, 255, 255, 0)' }),
215
- // text: new Text({
216
- // text: feature.values_['BASIN'] || "",
217
- // font: '14px Calibri,sans-serif',
218
- // fill: new Fill({ color: '#FFF' }),
219
- // stroke: new Stroke({
220
- // color: '#409EFF', width: 2
221
- // }),
222
- // })
223
- // })
224
- // },
225
- // zIndex: 21
226
- // } as any)
227
- // this.map.addLayer(basinLayer)
228
- // this.addTwinkleLayer(twinkleList.map(item => ({...item, className: item[key]})), className, key)
229
- // }
230
214
  /**
231
215
  * 添加闪烁点
232
216
  * @param twinkleList 闪烁点数据
233
217
  * @param callback
234
218
  */
235
219
  addDomPoint(twinkleList, callback) {
236
- return PointOverlay.create(this.map, twinkleList, callback);
220
+ const handle = PointOverlay.create(this.map, twinkleList, callback);
221
+ this.trackDisposable(handle);
222
+ return handle;
237
223
  }
238
224
  /**
239
225
  * 添加vue组件为点位
@@ -251,8 +237,12 @@ export default class Point {
251
237
  throw new Error('Vue template is required');
252
238
  }
253
239
  try {
254
- const vueTemplatePoint = new VueTemplatePoint(this.map);
255
- return vueTemplatePoint.addVueTemplatePoint(pointDataList, template, options);
240
+ if (!this.vueTemplatePoint) {
241
+ this.vueTemplatePoint = new VueTemplatePoint(this.map);
242
+ }
243
+ const handle = this.vueTemplatePoint.addVueTemplatePoint(pointDataList, template, options);
244
+ this.trackDisposable(handle);
245
+ return handle;
256
246
  }
257
247
  catch (error) {
258
248
  throw new Error(`Failed to create Vue template points: ${error}`);
@@ -10,12 +10,24 @@ import { PolygonOptions, MapJSONData, PointData, HeatMapOptions, ImageLayerData,
10
10
  */
11
11
  export default class Polygon {
12
12
  private map;
13
- [key: string]: any;
13
+ /**
14
+ * 由本实例创建的所有图层句柄(含 mask 子图层等)。destroyAll 时统一回收,避免地图销毁后图层泄漏。
15
+ */
16
+ private readonly managedLayers;
14
17
  /**
15
18
  * 构造函数
16
19
  * @param map OpenLayers 地图实例
17
20
  */
18
21
  constructor(map: Map);
22
+ /**
23
+ * 跟踪一个本实例创建的图层,便于销毁阶段统一清理。
24
+ * @internal
25
+ */
26
+ private trackLayer;
27
+ /**
28
+ * 销毁本实例创建的所有图层。供 MyOl.destroy 调用。
29
+ */
30
+ destroyAll(): void;
19
31
  /**
20
32
  * 添加地图边框图层
21
33
  * @param data 图层数据,必须是有效的 GeoJSON 格式
@@ -20,11 +20,37 @@ export default class Polygon {
20
20
  * @param map OpenLayers 地图实例
21
21
  */
22
22
  constructor(map) {
23
+ /**
24
+ * 由本实例创建的所有图层句柄(含 mask 子图层等)。destroyAll 时统一回收,避免地图销毁后图层泄漏。
25
+ */
26
+ this.managedLayers = new Set();
23
27
  if (!map) {
24
28
  throw new Error('Map instance is required');
25
29
  }
26
30
  this.map = map;
27
31
  }
32
+ /**
33
+ * 跟踪一个本实例创建的图层,便于销毁阶段统一清理。
34
+ * @internal
35
+ */
36
+ trackLayer(layer) {
37
+ this.managedLayers.add(layer);
38
+ return layer;
39
+ }
40
+ /**
41
+ * 销毁本实例创建的所有图层。供 MyOl.destroy 调用。
42
+ */
43
+ destroyAll() {
44
+ this.managedLayers.forEach(layer => {
45
+ try {
46
+ this.map.removeLayer(layer);
47
+ }
48
+ catch {
49
+ // ignore
50
+ }
51
+ });
52
+ this.managedLayers.clear();
53
+ }
28
54
  /**
29
55
  * 添加地图边框图层
30
56
  * @param data 图层数据,必须是有效的 GeoJSON 格式
@@ -105,6 +131,7 @@ export default class Polygon {
105
131
  });
106
132
  layer.setVisible(mergedOptions.visible);
107
133
  this.map.addLayer(layer);
134
+ this.trackLayer(layer);
108
135
  // 如果需要适应视图
109
136
  if (mergedOptions.fitView) {
110
137
  this.fitViewToLayer(layer);
@@ -152,6 +179,7 @@ export default class Polygon {
152
179
  });
153
180
  layer.setVisible(mergedOptions.visible);
154
181
  this.map.addLayer(layer);
182
+ this.trackLayer(layer);
155
183
  // 如果需要适应视图
156
184
  if (mergedOptions.fitView) {
157
185
  source.once('featuresloadend', () => {
@@ -211,7 +239,11 @@ export default class Polygon {
211
239
  * @param options
212
240
  */
213
241
  setOutLayer(data, options) {
214
- return PolygonMaskLayer.setOutLayer(this.map, data, options);
242
+ const layer = PolygonMaskLayer.setOutLayer(this.map, data, options);
243
+ if (layer) {
244
+ this.trackLayer(layer);
245
+ }
246
+ return layer;
215
247
  }
216
248
  /**
217
249
  * 添加图片图层
@@ -221,7 +253,9 @@ export default class Polygon {
221
253
  * @throws 当数据格式无效时抛出错误
222
254
  */
223
255
  addImageLayer(imageData, options) {
224
- return PolygonImageLayer.addImageLayer(this.map, imageData, options);
256
+ const layer = PolygonImageLayer.addImageLayer(this.map, imageData, options);
257
+ this.trackLayer(layer);
258
+ return layer;
225
259
  }
226
260
  /**
227
261
  * 添加热力图图层
@@ -229,7 +263,11 @@ export default class Polygon {
229
263
  * @param options 热力图配置
230
264
  */
231
265
  addHeatmap(pointData, options) {
232
- return PolygonHeatmapLayer.addHeatmap(this.map, pointData, options);
266
+ const layer = PolygonHeatmapLayer.addHeatmap(this.map, pointData, options);
267
+ if (layer) {
268
+ this.trackLayer(layer);
269
+ }
270
+ return layer;
233
271
  }
234
272
  /**
235
273
  * 添加遮罩图层
@@ -239,10 +277,17 @@ export default class Polygon {
239
277
  * @throws 当数据格式无效时抛出错误
240
278
  */
241
279
  addMaskLayer(data, options) {
242
- return PolygonMaskLayer.addMaskLayer(this.map, data, options);
280
+ const layer = PolygonMaskLayer.addMaskLayer(this.map, data, options);
281
+ this.trackLayer(layer);
282
+ return layer;
243
283
  }
244
284
  removePolygonLayer(layerName) {
245
285
  new MapTools(this.map).removeLayer(layerName);
246
- this[layerName] = null;
286
+ // managedLayers 中也移除对应记录
287
+ this.managedLayers.forEach(layer => {
288
+ if (layer.get('layerName') === layerName || layer.get('name') === layerName) {
289
+ this.managedLayers.delete(layer);
290
+ }
291
+ });
247
292
  }
248
293
  }
package/index.d.ts CHANGED
@@ -13,4 +13,5 @@ export { default as ValidationUtils } from './utils/ValidationUtils';
13
13
  export type { BaseOptions, StyleOptions, TextOptions } from './types';
14
14
  export type { PointOptions, LineOptions, FlowLineOptions, FlowLineLayerHandle, PolygonOptions, PulsePointOptions, PulsePointLayerHandle } from './types';
15
15
  export type { OptionsType } from './types';
16
- export type { MapInitType, MapLayersOptions, HeatMapOptions, ImageLayerData, MaskLayerOptions, FeatureColorUpdateOptions, PointData, PulsePointIconOptions, LineData, ClusterOptions, MeasureHandlerType, VueTemplatePointOptions, MapJSONData, FeatureData, AnnotationType, TiandituType, MapLayers, AnnotationLayerOptions, SelectOptions, SelectMode, SelectCallbackEvent, ProgrammaticSelectOptions } from './types';
16
+ export type { MapInitType, MapLayersOptions, HeatMapOptions, ImageLayerData, MaskLayerOptions, FeatureColorUpdateOptions, PointData, PulsePointIconOptions, LineData, ClusterOptions, MeasureHandlerType, VueTemplatePointOptions, VueTemplatePointInstance, TwinkleItem, MapJSONData, FeatureData, AnnotationType, TiandituType, MapLayers, AnnotationLayerOptions, SelectOptions, SelectMode, SelectCallbackEvent, ProgrammaticSelectOptions } from './types';
17
+ export { VueTemplatePointState } from './types';
package/index.js CHANGED
@@ -11,3 +11,5 @@ export { SelectHandler } from './core/select';
11
11
  export { ErrorHandler, MyOpenLayersError, ErrorType } from './utils/ErrorHandler';
12
12
  // 验证工具
13
13
  export { default as ValidationUtils } from './utils/ValidationUtils';
14
+ // Vue 模板点位状态枚举(运行时值,需要单独导出)
15
+ export { VueTemplatePointState } from './types';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "my-openlayer",
3
3
  "private": false,
4
- "version": "2.5.4",
4
+ "version": "2.5.5",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "main": "index.js",