mapboxGL列表和地图联动

2021-09-10 11:02:21 浏览数 (1)

概述

列表和地图联动是webgis中一个非常常见的功能,本文讲一下在mapboxGL中结合vue如何实现此功能。

效果

实现思路

1. 获取数据

获取数据并将数据保存起来;

2. 列表展示

列表简单的用ulli来实现。

3. 地图展示

数据获取之后,通过document.createElement()的方式创建marker,将结果在地图上展示。

4. 列表地图联动

联动存在两个交互:鼠标移动和点击选中,所以需要两个变量用来记录当前鼠标经过的和点击选中的,如果鼠标经过的和点击选中发生变化的时候,去设置对应的样式即可。

实现代码

代码语言:javascript复制
<template>
  <div class="search-panel">
    <div
      class="search-result">
    <h4 class="title">
      查询结果
    </h4>
    <ul
      class="result-list">
      <li
        v-for="(item, index) in searchResult"
        :key="index"
        :class="getClass(item)"
        @mouseover="currentMarker = item"
        @mouseout="currentMarker = {}"
        @click="selectMarker = item">
        <div class="marker">
          <span>{{ index   1 }}</span>
        </div>
        <div class="info">
          <b>{{ item.name }}</b><br>
          {{ item.addr }}
        </div>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'Search',
  data() {
    return {
      searchResult: [], //记录查询结果
      markers: [],
      currentMarker: {},
      selectMarker: {},
      markerPopup: null
    };
  },
  methods: {
    init() {
      this.markerPopup = new mapboxgl.Popup({
        closeButton: false,
        closeOnClick: false,
        className: 'marker-popup',
        offset: [0, -15],
        anchor: 'bottom'
      });
      // 造测试数据
      this.searchByKeyword();
    },
    getRandomPos() {
      const bbox = [79.740682, 20.10212, 134.0345, 44.8279];
      const lon = Math.random() * (bbox[2] - bbox[0]   1)   bbox[0];
      const lat = Math.random() * (bbox[3] - bbox[1]   1)   bbox[1];
      return [lon, lat];
    },
    getClass(item) { // 设置列表样式
      let cls = item.id === this.currentMarker.id ? 'active' : '';
      cls  = item.id === this.selectMarker.id ? ' select' : '';
      return cls;
    },
    setMarkersClass() { // 设置地图marker样式
      this.searchResult.forEach(item => {
        const dom = document.getElementById(item.id);
        if (item.id === this.currentMarker.id) {
          dom.classList.add('active');
        } else {
          dom.classList.remove('active');
        }
        if (item.id === this.selectMarker.id) {
          dom.classList.add('select');
        } else {
          dom.classList.remove('select');
        }
      });
    },
    searchByKeyword() {
      const that = this;
      if (this.keyword !== '') {
        that.loading = true;
        that.searchResult = [];
        this.removeMarkers();
        setTimeout(() => {
          for (let i = 0; i < 10;   i) {
            that.searchResult.push({
              id: 'marker'   i,
              name: '大石地铁站',
              addr: '广东省广州市番禺区大石地铁站',
              pos: that.getRandomPos()
            });
          }
          // 添加markers
          that.addMarkers2Map();
          that.loading = false;
        }, 2000);
      }
    },
    addMarkers2Map() {
      const that = this;
      const map = window.map;
      that.searchResult.forEach((item, index) => {
        const ele = document.createElement('div');
        ele.setAttribute('class', 'map-marker');
        ele.setAttribute('id', item.id);
        ele.innerText = (index   1).toString();
        const option = {
          element: ele
        };
        const marker = new mapboxgl.Marker(option)
          .setLngLat(item.pos)
          .addTo(map);
        that.markers.push(marker);

        ele.onmouseover = () => {
          that.currentMarker = item;
        };
        ele.onmouseout = () => {
          that.currentMarker = {};
        };
        ele.onclick = () => {
          that.selectMarker = item;
        };
      });
    },
    removeMarkers() {
      this.markers.forEach(marker => {
        marker.remove();
      });
    },
    showMarkerInfo() {
      const that = this;
      if (this.selectMarker.id) {
        const description = `
            <h5>
               ${this.selectMarker.name}
               <span class="close el-icon-close" id="popupClose"></span>
            </h5>
            <ul>
              <li>温度: 20℃</li>
              <li>湿度: 52%</li>
              <li>降水: 1mm</li>
              <li>风速: 4m/s</li>
              <li>风向: 无持续风向</li>
            </ul>
          `;
        this.markerPopup
          .setLngLat(this.selectMarker.pos)
          .setHTML(description)
          .addTo(window.map);
        // 添加关闭事件
        document.getElementById('popupClose').onclick = () => {
          that.selectMarker = {};
        };
        window.map.flyTo({
          center: this.selectMarker.pos
        });
      } else {
        this.markerPopup.remove();
      }
    }
  },
  watch: {
    currentMarker(val) {
      this.setMarkersClass();
    },
    selectMarker(val) {
      this.setMarkersClass();
      this.showMarkerInfo();
    }
  }
};
</script>

<style scoped lang="scss">
@import '../../assets/css/map';
.search-panel {
  position: absolute;
  top: $padding;
  left: $padding;
  z-index: 99;
  .search-result {
    width: 315px;
    background-color: $bg-color;
    border-radius: 4px;
    padding-bottom: 10px;
    .title {
      margin: 0;
      padding: 10px;
      border-bottom: 1px solid #ccc;
      i {
        float: right;
        font-size: 14px;
        &:hover,
        &.active {
          cursor: pointer;
          color: $active-color;
          text-decoration: underline;
        }
      }
    }
  }
  .result-list {
    margin: 10px 0;
    min-height: 100px;
    li {
      padding: 8px 10px;
      overflow: hidden;
      border-bottom: 1px dashed #ddd;
      &:hover,
      &.active,
      &.select {
        cursor: pointer;
        .marker span {
          background-image: url('../../assets/images/marker-blue.png');
        }
      }
      &.select {
        background-color: rgba(0, 0, 0, 0.05);
      }
      .marker {
        float: left;
        width: 35px;
        text-align: center;
        line-height: 30px;
        span {
          width: 26px;
          height: 26px;
          line-height: 20px;
          text-align: center;
          display: inline-block;
          background-image: url('../../assets/images/marker-red.png');
          background-size: 100%;
          color: white;
          border-radius: 100%;
        }
      }
      .info {
        float: left;
      }
    }
  }
}
</style>
<style lang="scss">
@import '../../assets/css/map';
.map-marker {
  width: 26px;
  height: 26px;
  line-height: 20px;
  text-align: center;
  background-image: url('../../assets/images/marker-red.png');
  background-size: 100%;
  color: white;
  border-radius: 100%;
  cursor: pointer;
  &:hover,
  &.active,
  &.select {
    background-image: url('../../assets/images/marker-blue.png');
  }
}
.marker-popup {
  color: white;
  .mapboxgl-popup-content {
    background-color: $black65;
    margin: 0;
    padding: 8px;
    white-space: nowrap;
    font-size: 12px;
    h5 {
      margin: 0 0 3px 0;
      padding: 3px 0;
      font-size: 14px;
      span {
        float: right;
        cursor: pointer;
      }
    }
    label {
      display: inline-block;
      text-align: right;
      width: 65px;
    }
  }
  .mapboxgl-popup-tip {
    border-top-color: $black65 !important;
  }

  .mapboxgl-popup-close-button {
    color: white;
  }
}
</style>

0 人点赞