vue openlayers地图加载大量点位时优化

vue openlayers地图加载大量点位时优化

如果一次性加载上万个带标题的点位,会造成地图卡顿, 优化方法是只加载当前视口内的点位,且只显示屏幕中心的点位的标题, 每次拖动地图只加载视口内的点位

  • 工具类OlViewportPointUtil.js
js 复制代码
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import Point from 'ol/geom/Point';
import Feature from 'ol/Feature';
import Style from 'ol/style/Style';
import Icon from 'ol/style/Icon';
import Text from 'ol/style/Text';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
import { containsCoordinate, intersects } from 'ol/extent';
import { fromLonLat } from 'ol/proj';

/**
 * 显示可见范围内的点位
 */
export default class OlViewportPointUtil {
  constructor(map, options = {}) {
    this.map = map;
    this.allPoints = [];

    this.vectorSource = null;
    this.vectorLayer = null;

    this.options = {
      style: this.getDefaultStyle.bind(this),
      debounceTime: 100,
      minZoom: 3,
      ...options,
    };

    this.initLayer();
    this.bindMapEvent();
  }

  initLayer() {
    this.vectorSource = new VectorSource();
    this.vectorLayer = new VectorLayer({
      source: this.vectorSource,
      zIndex: 999,
      declutter: true,
      declutterMode: 'label',
      style: this.options.style,
    });
    this.map.addLayer(this.vectorLayer);
  }

  getDefaultStyle(feature, resolution) {
    // 最小缩放等级判断
    const currentZoom = this.map.getView().getZoom();
    if (currentZoom < this.options.minZoom) {
      return null;
    }

    // 文字显示判断(中心区域才显示)
    const showLabel = getIsShowFeatureText(this.map, feature);
    const pointData = feature.get('data');

    return new Style({
      image: new Icon({
        src: require('../assets/点位图标.png'),
        scale: 0.5,
        anchor: [0.5, 1],
      }),
      text: showLabel
        ? new Text({
            text: pointData?.name || '', // 从 feature 里取标题
            font: '14px Microsoft YaHei',
            fill: new Fill({ color: '#ffffff' }),
            stroke: new Stroke({ color: '#1a5a96', width: 2 }),
            offsetY: -25,
            textAlign: 'center',
          })
        : null,
    });
  }

  setDataSource(points = []) {
    if (!points || points.length === 0) return;
    this.allPoints = points;
    this.refreshView();
  }

  refreshView() {
    this.vectorSource.clear();
    const extent = this.map.getView().calculateExtent(this.map.getSize());
    const features = [];

    for (const point of this.allPoints) {
      const jd = parseFloat(point.jd);
      const wd = parseFloat(point.wd);
      // const coord = fromLonLat([jd, wd]);
      const coord = [jd, wd];

      if (containsCoordinate(extent, coord)) {
        const feature = new Feature({
          geometry: new Point(coord),
          data: point, // 把数据存到 feature 里
        });
        features.push(feature);
      }
    }

    console.log('当前视口内点位数量:', features.length);
    this.vectorSource.addFeatures(features);
  }

  bindMapEvent() {
    let timer = null;
    this.map.on('moveend', () => {
      clearTimeout(timer);
      timer = setTimeout(() => {
        this.refreshView();
      }, this.options.debounceTime);
    });
  }

  clear() {
    this.vectorSource.clear();
    this.allPoints = [];
  }

  destroy() {
    this.clear();
    this.map.removeLayer(this.vectorLayer);
  }
}

/**
 * 只显示屏幕中心附近的名称
 */
function getIsShowFeatureText(map, feature) {
  const view = map.getView();
  const mapSize = map.getSize();
  if (!mapSize) return false;

  const viewExtent = view.calculateExtent(mapSize);
  const [minx, miny, maxx, maxy] = viewExtent;
  const w = maxx - minx;
  const h = maxy - miny;
  const centerBuffer = 0.3;
  const centerExtent = [minx + w * centerBuffer, miny + h * centerBuffer, maxx - w * centerBuffer, maxy - h * centerBuffer];
  const geom = feature.getGeometry();
  return geom && intersects(geom.getExtent(), centerExtent);
}
  • 使用
js 复制代码
import OlViewportPointUtil from './olViewportPointUtil.js';

const list = [{name: '点位1', jd: '经度', wd: '纬度' }, ... ...]
this.pointUtil = new OlViewportPointUtil(this.map);
this.pointUtil.setDataSource(list);
相关推荐
郭老二1 小时前
【前端】vue3:编译步骤
前端
不在逃避q2 小时前
Trae/Vs Code/Cursor命令行无法跑npm命令
前端·arcgis·npm
夏殇之殁2 小时前
包中创建自定义列表项。 . 使用自定义列表项进行数据绑定 . 将天气预报数据保存到本地内存表,通过LiveBindings进行显示。 ...
服务器·前端·javascript
The Chosen One9852 小时前
高进度算法模板速记(待完善)
java·前端·算法
陈随易2 小时前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师3 小时前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户059540174463 小时前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈3 小时前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen4 小时前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒5 小时前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端