vue-map高德地图(4)地图区域描边、点标记、点信息详情弹窗

2 篇文章 0 订阅
2 篇文章 0 订阅

需求:

点击单元列表地图显示该单元的信息,重新绘点。

点击道路类型:路口、路段、其他分别显示对应信息,重新绘点。

点击事故类型:事故、拥堵、违停分别显示对应信息,重新绘点。

直接上图,最终结果如下, 分享技术点如下,如有疑问可关注博主进行提问。

1:地图区域描边。比如(杭州市、萧山区边界描绘)

2:点标记(根据数量渲染不同颜色的点,显示出数量信息(根据需求可自己变更))

3:点击点坐标显示该点信息

准备工作:安装使用高德地图

npm install vue-amap --save
// !!!引入高德地图
import VueAMap from 'vue-amap'

//!!!使用 高德地图
Vue.use(VueAMap);
//!!!高德地图初始化
VueAMap.initAMapApiLoader({
  key: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
  plugin: [
    "AMap.Autocomplete",
    "AMap.PlaceSearch",
    "AMap.Scale",
    "AMap.OverView",
    "AMap.ToolBar",
    "AMap.MapType",
    "AMap.PolyEditor",
    "AMap.CircleEditor",
    "AMap.Geocoder",
    "AMap.Geolocation",
    "AMap.MarkerClusterer",
    "AMap.DistrictSearch"
    // "AMap.Marker",
  ],
  // 默认高德 sdk 版本为 1.4.4
  v: "1.4.4",
});

一、引入组件,使用封装的GetPosition组件,封装组件便于重复引用。(组件内容即步骤二)

<!-- 组件说明 -->
<template>
  <div class="">
    <GetPosition
      class="map"
      :pointList="pointList"
      @drawClose="handleClose"
      @drawOpen="handleOpen"
    />
  </div>
</template>
<script>
import getPosition from "@/views/components/getLocation/map.vue";
export default {
  components: {
    GetPosition: getPosition,
  },
  data() {
    return {
      pointList: [
        {
          roadId: 331181207330,
          roadTude: 30.244784,
          roadLong: 120.294536,
          roadTeam: 4,
          roadType: 18,
          roadAddr: "弘惠路-学知路交叉口",
          roadTeamName: "乡镇",
          roadTypeName: "镇",
          roadName: "弘惠路-学知路交叉口",
          accidentNum: 1,
          percentage: 0,
          colorSign: 9,
          colorType: "#B9D4B9",
          accidentRoadType: null,
        },
        {
          roadId: 331181207331,
          roadTude: 30.22136,
          roadLong: 120.290936,
          roadTeam: null,
          roadType: null,
          roadAddr: "机场城市路-通惠北路路口",
          roadTeamName: null,
          roadTypeName: null,
          roadName: "机场城市路-通惠北路路口",
          accidentNum: 1,
          percentage: 0,
          colorSign: 9,
          colorType: "#B9D4B9",
          accidentRoadType: null,
        },
        {
          roadId: 331181207342,
          roadTude: 30.206022,
          roadLong: 120.287385,
          roadTeam: null,
          roadType: null,
          roadAddr: "建设四路-通惠路路口",
          roadTeamName: null,
          roadTypeName: null,
          roadName: "建设四路-通惠路路口",
          accidentNum: 1,
          percentage: 0,
          colorSign: 9,
          colorType: "#B9D4B9",
          accidentRoadType: null,
        },
        {
          roadId: 331181207345,
          roadTude: 30.236867,
          roadLong: 120.282971,
          roadTeam: null,
          roadType: null,
          roadAddr: "鸿宁路-桥园路",
          roadTeamName: null,
          roadTypeName: null,
          roadName: "鸿宁路-桥园路",
          accidentNum: 1,
          percentage: 0,
          colorSign: 9,
          colorType: "#B9D4B9",
          accidentRoadType: null,
        },
        {
          roadId: 331181207348,
          roadTude: 30.244784,
          roadLong: 120.294536,
          roadTeam: null,
          roadType: null,
          roadAddr: "弘惠路-学知路",
          roadTeamName: null,
          roadTypeName: null,
          roadName: "弘惠路-学知路",
          accidentNum: 1,
          percentage: 0,
          colorSign: 9,
          colorType: "#B9D4B9",
          accidentRoadType: null,
        },
        {
          roadId: 331181207349,
          roadTude: 30.244784,
          roadLong: 120.294536,
          roadTeam: null,
          roadType: null,
          roadAddr: "鸿慧路-学知路路口",
          roadTeamName: null,
          roadTypeName: null,
          roadName: "鸿慧路-学知路路口",
          accidentNum: 1,
          percentage: 0,
          colorSign: 9,
          colorType: "#B9D4B9",
          accidentRoadType: null,
        },
        {
          roadId: 331181207352,
          roadTude: 30.232579,
          roadLong: 120.291775,
          roadTeam: null,
          roadType: null,
          roadAddr: "鸿达路-通城高架路口",
          roadTeamName: null,
          roadTypeName: null,
          roadName: "鸿达路-通城高架路口",
          accidentNum: 1,
          percentage: 0,
          colorSign: 9,
          colorType: "#B9D4B9",
          accidentRoadType: null,
        },
      ],
    };
  },
  methods: {
    handleClose() {
      this.hidden = false;
    },
    handleOpen(markId) {
      this.hidden = true;
    },
  },
};
</script>

<style lang='scss' src=''>
</style>

其中class根据需求写特定样式。

          positionList是要描绘的点信息。

          方法是点击点显示的弹出窗的关闭和开启

二、(封装的map.vue组件) 地区描边+对点信息进行描绘(map.vue组件,vscode编译者建议copy代码后下载Better Comments插件查看,效果更好)

<template>
  <div class="my-container">
    <div id="map-demo" ref="map" v-show="showFlag"></div>
    <div class="bg-loading" v-show="!showFlag"></div>
  </div>
</template>

<script>
//todo引入弹出窗信息
import createInfoWindow from "@/utils/amap";
export default {
  props: ["pointList"],
  data() {
    return {
      map: null,
      infoWindow: null,
      mapStyle: "amap://styles/33ac9bd25289b5229362e1f52b481f49", // 使用的自定义地图样式,可以根据需求显示/隐藏内容,改变主题颜色等,具体的使用下面会写
      winInfo: [],
      winTitle: "",
      markList: [],
      color: "#fff",
      showFlag: false,
    };
  },
  watch: {
    //todo 监听不同的点信息进行视图更新
    pointList(val) {
      this.showFlag = false;
      this.carGPSIP();
      setTimeout(() => {
        this.showFlag = true;
      }, 1000);
    },
  },
  mounted() {
    this.carGPSIP();
  },
  methods: {
    carGPSIP() {
      let self = this;
      this.map = new AMap.Map("map-demo", {
        resizeEnable: true,
        zoom: 9, //级别
        center: [120.280798, 30.218143], //中心点坐标
        viewMode: "2D", //使用3D视图
        mapStyle: "amap://styles/darkblue", //todo 地图颜色变更,参考官方颜色darkblue\dark等
      }); //初始化地图
      var district = null;
      var polygons = [];
      //todo 行政区域边界描边
      function drawBounds() {
        //加载行政区划插件
        if (!district) {
          //实例化DistrictSearch
          var opts = {
            subdistrict: 0,
            //获取边界不需要返回下级行政区
            extensions: "all", //返回行政区边界坐标组等具体信息
            level: "district", //查询行政级别为 市
          };
          district = new AMap.DistrictSearch(opts);
        }
        //行政区查询
        district.search("萧山区", function (status, result) {
          self.map.remove(polygons); //清除上次结果
          polygons = [];
          var bounds = result.districtList[0].boundaries;
          if (bounds) {
            for (var i = 0, l = bounds.length; i < l; i++) {
              //生成行政区划polygon
              var polygon = new AMap.Polygon({
                strokeWeight: 1,
                path: bounds[i],
                fillOpacity: 0.4,
                fillColor: "#004055",
                strokeColor: "#0091ea",
              });
              polygons.push(polygon);
            }
          }
          self.map.add(polygons);
          self.map.setFitView(polygons); //视口自适应
        });
      }
      //todo 遍历生成多个标记点
      drawBounds();
      if (this.pointList) {
        for (var i = 0, marker; i < this.pointList.length; i++) {
          //todo 渲染点信息,颜色可以自己根据数量判断,我们是让后端返回颜色
          var marker = new AMap.Marker({
            position: [this.pointList[i].roadLong, this.pointList[i].roadTude], //不同标记点的经纬度
            map: self.map,
            content: `<div style="background:${this.pointList[i].colorType}" class="pulse pulse1" > 
                    <div class="ring"></div> 
                    <div class="ring"></div> 
                    <div class="ring"></div> 
                    <p class="num"> ${this.pointList[i].accidentNum}</p>
            </div>`,
          });
          //todo 弹窗信息
          marker.title = ``;
          marker.content = `
        <div class="tip-box">
        <div class="box-content">
          <div class="box-left">
            <div>
               <h2>
                ${this.pointList[i].accidentNum}
               </h2>
               <span class="num-desc">
                 事故总数量
               </span>
            </div>
          </div>
          <div class="box-right">
            <span>
             位置大类:${
               this.pointList[i].roadTeamName ||
               this.pointList[i].sourceTeamName ||
               ""
             }
            </span>
            <span>
              位置小类:${
                this.pointList[i].roadTypeName ||
                this.pointList[i].sourceTypeName ||
                ""
              }
            </span>
            <span>
              详细地址:${
                this.pointList[i].roadAddr || this.pointList[i].sourceAddr
              }
            </span>
          </div>
        </div>
        </div>
        `;
          marker.footer = `
         <div class="box-unit-name">
          <span>
              <span>${
                this.pointList[i].roadName || this.pointList[i].sourceName
              }</span>
          </span>
         <span class="el-icon-arrow-left title">
         </span>
        </div>`;
          marker.markId =
            this.pointList[i].roadId || this.pointList[i].sourceId;
          marker.accNum = this.pointList[i].accidentNum;
          marker.on("click", self.markerClick);
        }
      }
      //实例化信息窗体
      this.infoWindow = new AMap.InfoWindow({
        isCustom: true, //使用自定义窗体
        content: self.winInfo,
        footer: self.winFooter,
        offset: new AMap.Pixel(15, -55),
      });
      this.map.setFitView();
    },
    // 点标记点击事件
    markerClick(e) {
      if (e.target.accNum > 0) {
        this.$emit("drawOpen", e.target.markId);
      }
      let self = this;
      this.winInfo = e.target.content;
      this.winTitle = e.target.title;
      this.winFooter = e.target.footer;
      // 设置窗体内容
      this.infoWindow.setContent(
        createInfoWindow.createInfoWindow(
          self.winTitle,
          self.winInfo,
          self.winFooter,
          // 关闭窗体
          function () {
            self.map.clearInfoWindow();
            self.$emit("drawClose");
          }
        )
      );
      // 打开窗体
      self.infoWindow.open(self.map, e.target.getPosition());
    },
  },
};
</script>

<style lang="scss" scoped>
#map-demo {
  width: 100%;
  height: 100%;
}
::v-deep .content-window-card {
  position: relative;
  box-shadow: none;
  bottom: 0;
  left: 0;
  width: auto;
  padding: 0;
  p {
    height: 2rem;
  }
}
.custom-info {
  position: relative;
  /* border: solid 1px silver; */
}

::v-deep .info-top {
  position: relative;
  background: #fc4a3d;
  height: 8px;
  line-height: 20px;
  font-size: 14px;
  color: #fff;
  padding: 0 10px;
  span:nth-child(2) {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    width: 200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: center;
  }

  img {
    position: absolute;
    top: 2px;
    right: 5px;
    transition-duration: 0.25s;
    width: 15px;
  }

  img:hover {
    box-shadow: 0px 0px 5px #000;
  }
}

::v-deep .info-middle {
  background: #100b0d !important;
  font-size: 12px;
  padding: 0px 6px;
  line-height: 20px;
  color: #fff;
  img {
    float: left;
    margin-right: 6px;
  }
  .tip-box {
    display: flex;
    flex-direction: column;
    padding: 1px 0 20px 0;
    .box-content {
      height: 120px;
      display: flex;
      justify-content: space-around;
      .box-left {
        width: 150px;
        position: relative;
        h2 {
          font-size: 60px;
          height: 0px;
          line-height: 0px;
          color: #f94a37;
          margin-left: 20px;
          font-weight: 900;
        }
        .num-desc {
          position: absolute;
          bottom: 20px;
          right: 20px;
          font-size: 16px;
          color: #666;
          font-weight: 800;
        }
      }
      .box-right {
        width: 200px;
        display: flex;
        flex-direction: column;
        span {
          max-width: 190px;
          max-height: 50px;
          padding: 0 5px;

          display: inline-block;
          height: 25px;
          line-height: 25px;
        }
        span:nth-child(1) {
        }
        span:nth-child(2) {
          // width: 100px;
          color: #fb473f;
          background: #4d272a;
        }
        span:nth-child(3) {
          height: 50px;
          color: rgba(255, 255, 255, 0.8);
          font-size: 14px;
          overflow: hidden;
          /* text-overflow: ellipsis; */
          display: -webkit-box;
          -webkit-line-clamp: 2;
          line-clamp: 2;
          -webkit-box-orient: vertical;
        }
      }
    }
  }
}

::v-deep .info-bottom {
  background: #6ad345;
  width: 100%;
  text-align: center;
  .box-unit-name {
    cursor: pointer;
    position: absolute;
    left: 0;
    bottom: -30px;
    width: 200px;
    font-size: 16px;
    height: 30px;
    line-height: 30px;
    background: #4d272a;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    span:nth-child(1) {
      margin-left: 5px;
      color: rgba(255, 255, 255, 0.8);
    }
    span:nth-child(2) {
      color: #c54141;
      margin-left: 20px;
    }
    .title {
      font-size: 18px;
      font-weight: 900;
    }
  }
}
::v-deep .pulse {
  height: 20px;
  width: 20px;
  border-radius: 100%;
  position: relative;
  .num {
    position: absolute;
    width: 20px;
    height: 20px;
    top: -15px;
    text-align: center;
    color: #4d272a;
    font-weight: 500;
    font-size: 16px;
    z-index: 999;
  }

  .ring {
    position: absolute;
    background-color: inherit;
    height: 100%;
    width: 100%;
    border-radius: 100%;
    opacity: 0.8;
    -webkit-animation: pulsing 2s ease-out infinite;
    animation: pulsing 2s ease-out infinite;
  }

  .ring:nth-of-type(1) {
    -webkit-animation-delay: -0.5s;
    animation-delay: -0.5s;
  }

  .ring:nth-of-type(2) {
    -webkit-animation-delay: -1s;
    animation-delay: -1s;
  }

  .ring:nth-of-type(3) {
    -webkit-animation-delay: -1.5s;
    animation-delay: -1.5s;
  }

  @-webkit-keyframes pulsing {
    100% {
      transform: scale(1.75);
      opacity: 0;
    }
  }

  @keyframes pulsing {
    100% {
      transform: scale(1.75);
      opacity: 0;
    }
  }
}
::v-deep .pulse1 {
  position: relative;
}
::v-deep .pulse2 {
  position: relative;
}
::v-deep .pulse3 {
  position: relative;
}
::v-deep .pulse4 {
  position: relative;
}
::v-deep .pulse5 {
  position: relative;
}
.bg-loading {
  background: #000;
  width: 100%;
  height: 100%;
}
</style>

  amap.js自定义窗口信息

function createInfoWindow(title, content, footer, callback) {
  var info = document.createElement("div");
  info.className = "custom-info input-card content-window-card";

  //可以通过下面的方式修改自定义窗体的宽高
  info.style.width = "400px";
  // 定义顶部标题
  var top = document.createElement("div");
  var closeX = document.createElement("img");
  top.className = "info-top";

  closeX.onclick = callback;

  top.innerHTML = title;
  top.appendChild(closeX);
  info.appendChild(top);

  // 定义中部内容
  var middle = document.createElement("div");
  middle.className = "info-middle";
  middle.style.backgroundColor = "white";
  middle.innerHTML = content;
  info.appendChild(middle);

  // 定义底部内容
  var bottom = document.createElement("div");
  bottom.className = "info-bottom";
  bottom.onclick = callback;
  bottom.innerHTML = footer;
  info.appendChild(bottom);
  return info;
}

export default {
  createInfoWindow,
};

有问题请留言~或联系作者,会一一解答。

 前端之家企鹅群:610408494

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liuwenjie_

感谢打赏,问题留言~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值