乐趣区

关于前端:vue30与leaflet的搭建和简易demo

hint:
node => 14.17.5
npm => 6.14.14

抉择装置 leaflet 版本 '^1.7.1'
以实现散点图的业务为驱动👇

Map 的承载 Div

<template>
    <div id="myMap2" class="mainMap2"></div>
</template>

初始化地图并且实现了几个繁难性能点:

  1. 依据数个点位生成 polygon 并加载
  2. 随机点位加载 svg 标记点
  3. 加载标记点后从新设置标记点大小色彩再次重绘
<script>
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
import {addPloygon,addScatterPointIcon2,newScatterStyle} from "@/utils/layers"

import {onMounted,} from 'vue'
export default {
  name: 'Map',
  props: { },
  setup(props, context) {console.log(props,context);
    let mapMap = null
    let scatterLayerGroup = L.featureGroup()
    onMounted(()=>{initMap()
      addPloygon([[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04],[35, -122.04]]).addTo(mapMap)
    })
    // 获取以后可是方位随机点位的经纬度
    const getRandomLatLng = map => {let bounds = map.getBounds(),
        southWest = bounds.getSouthWest(),
        northEast = bounds.getNorthEast(),
        lngSpan = northEast.lng - southWest.lng,
        latSpan = northEast.lat - southWest.lat;

      return L.latLng(southWest.lat + latSpan * Math.random(),
        southWest.lng + lngSpan * Math.random());
    };
    const initMap = () =>{console.log('初始化地图');
      // 天地图
      const image = L.tileLayer('http://t{s}.tianditu.gov.cn/img_w/wmts?tk=bb11a33c4377f10603478ed166691455&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}', {subdomains: [0, 1, 2, 3, 4, 5, 6, 7],
      })
      // 注记
      const cia = L.tileLayer('http://t{s}.tianditu.gov.cn/cia_w/wmts?tk=bb11a33c4377f10603478ed166691455&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}', {subdomains: [0, 1, 2, 3, 4, 5, 6, 7],
        transparent: true,
        zIndex: 3,
      })
      // 天地图图组
      const tiandiMap = L.layerGroup([image, cia]);

      let myCenter = [31.95723698714103, 104.29901249999988]; // 设置地图核心
      mapMap = L.map('myMap2',{
        center: myCenter,
        minZoom:1,
        layers: [tiandiMap],
        zoom: 3
      })
      let initSiteLatlon = getRandomLatLng(mapMap)
      scatterLayerGroup.addLayer(addScatterPointIcon2(initSiteLatlon))
      scatterLayerGroup.addTo(mapMap);
    }
    const changeStyle = (val,scatterRadius) =>{let newStyle = newScatterStyle(val.color,scatterRadius.value,false)
      scatterLayerGroup.eachLayer(function (layer) {layer.setIcon(newStyle)
      });
    }
    return {changeStyle}
  }
}
</script>

这是封装的 layer 生成模块 =>layers.js
"leaflet.markercluster": "^1.5.0",

import L from 'leaflet'
/**
 * add polygon
 * @param {*} latlngs 
 */
export function addPloygon (latlngs) {var layer = L.polygon(latlngs , {color: 'rgb(80,227,194)'})
    return layer
}

/**
 * add scatterPoint
 * @param {*} latlngs 
 */
 export function addScatterPoint (latlng) {
    let circleMarker = L.circle(latlng,{
        radius:800000,
        fillColor:'red',
        fillOpacity:'1',
        color: 'red', // 色彩
    })
    circleMarker.on('mouseover',function(){circleMarker.bindPopup(`<p>Hello world!<br />This is a nice popup.</p>`).openPopup();})
    circleMarker.on('mouseout', function(){circleMarker.closePopup()
    });
    return circleMarker
}



/**
 * add scatterPoint VIP
 * @param {*} latlngs 点经纬度
 */
 export function addScatterPointIcon2 (latlng) {
    var iconSettings = {mapIconUrl: '<svg version="1"xmlns="http://www.w3.org/2000/svg"viewBox="0 0 149 178"><path fill="{mapIconColor}"stroke="#FFF"stroke-width="6"stroke-miterlimit="30"d="M126 23l-6-6A69 69 0 0 0 74 1a69 69 0 0 0-51 22A70 70 0 0 0 1 74c0 21 7 38 22 52l43 47c6 6 11 6 16 0l48-51c12-13 18-29 18-48 0-20-8-37-22-51z"/><circle fill="{mapIconColorInnerCircle}"cx="74"cy="75"r="61"/><circle fill="#FFF"cx="74"cy="75"r="{pinInnerCircleRadius}"/><image x="34"y="35"width="80"height="80"xlink:href="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.51tfb.com%2Fuploadfile%2F20190316%2F201903160950595c8c568363cdc.jpg&refer=http%3A%2F%2Fwww.51tfb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633143290&t=bf91e17fa94f787ffafaa729eb843a6f"/></svg>',
        mapIconColor: 'red',
        mapIconColorInnerCircle: '#fff',
        pinInnerCircleRadius:48
    };
    var divIcon = L.divIcon({
        className: "leaflet-data-marker",
      html: L.Util.template(iconSettings.mapIconUrl, iconSettings), //.replace('#','%23'),
      iconAnchor  : [12, 32],
      iconSize    : [30, 30],
      popupAnchor : [0, -28]
    });
    var marker = L.marker(latlng, {
        icon: divIcon,
        id: 'scatter'
    });
    return marker
}
/**
 * new scatterPoint style
 * marker.setIcon(newScatterStyle);
 * @param {*} color 色彩
 * @param {*} radius 半径
 * @param {*} isVip 是否是 vip
 */
 export function newScatterStyle (color,radius,isVip) {
    var iconSettings
     if(isVip){
        iconSettings = {mapIconUrl: '<svg version="1"xmlns="http://www.w3.org/2000/svg"viewBox="0 0 149 178"><path fill="{mapIconColor}"stroke="#FFF"stroke-width="6"stroke-miterlimit="30"d="M126 23l-6-6A69 69 0 0 0 74 1a69 69 0 0 0-51 22A70 70 0 0 0 1 74c0 21 7 38 22 52l43 47c6 6 11 6 16 0l48-51c12-13 18-29 18-48 0-20-8-37-22-51z"/><circle fill="{mapIconColorInnerCircle}"cx="74"cy="75"r="61"/><circle fill="#FFF"cx="74"cy="75"r="{pinInnerCircleRadius}"/><image x="34"y="35"width="80"height="80"xlink:href="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.51tfb.com%2Fuploadfile%2F20190316%2F201903160950595c8c568363cdc.jpg&refer=http%3A%2F%2Fwww.51tfb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633143290&t=bf91e17fa94f787ffafaa729eb843a6f"/></svg>',
            mapIconColor: color,
            mapIconColorInnerCircle: '#fff',
            pinInnerCircleRadius:61
        };
     }else{
        iconSettings = {mapIconUrl: '<svg version="1"xmlns="http://www.w3.org/2000/svg"viewBox="0 0 149 178"><path fill="{mapIconColor}"stroke="#FFF"stroke-width="6"stroke-miterlimit="30"d="M126 23l-6-6A69 69 0 0 0 74 1a69 69 0 0 0-51 22A70 70 0 0 0 1 74c0 21 7 38 22 52l43 47c6 6 11 6 16 0l48-51c12-13 18-29 18-48 0-20-8-37-22-51z"/></svg>',
            mapIconColor: color,
            mapIconColorInnerCircle: '#fff',
            pinInnerCircleRadius:61
        };
     }
    var divIcon = L.divIcon({
        className: "leaflet-data-marker",
      html: L.Util.template(iconSettings.mapIconUrl, iconSettings), //.replace('#','%23'),
      iconAnchor  : [12, 32],
      iconSize    : [radius, radius],
      popupAnchor : [0, -28]
    });
    return divIcon
}

总结:以前都是用的 supermap 的 leaflet 库,首次用 vue3.0 配合原生 leaflet。目标也是为了去相熟原生 leaflet 的文档,不得不说他的文档写的很不错,比 openlayer 好很多。学习中成长吧,为初学者留下一点案例材料。
原作者地址:https://segmentfault.com/u/yo…

上面补充一下聚合图的实例👇

<template>
  <div>
    <p>32</p>
    <div id="myMap2" class="mainMap2"></div>
  </div>
</template>

<script>
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'

import "leaflet.markercluster/dist/MarkerCluster.css"
import "leaflet.markercluster/dist/MarkerCluster.Default.css"
import "leaflet.markercluster";

// 定义 markar 款式
import icon from "leaflet/dist/images/marker-icon.png";
import iconShadow from "leaflet/dist/images/marker-shadow.png";
let DefaultIcon = L.icon({
  iconUrl: icon,
  shadowUrl: iconShadow
});
L.Marker.prototype.options.icon = DefaultIcon;


import { 
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onRenderTracked,
  onRenderTriggered,
  watch,
  computed
} from 'vue'
export default {
  name: 'HelloWorld',
  props: {msg: String,},
  setup(props, context) {console.log(props,context);
    let mapMap = null
    console.log('这里的 setup 函数执行就相当于 vue2 的钩子 beforeCreate 和 created')
    onBeforeMount(()=>{console.log('setup 的钩子和 vue2.0 的钩子同时存在的时候,setup 里的钩子会先执行')
    })
    onMounted(()=>{initMap()
      console.log('渲染完了')
    })
    const getRandomLatLng = map => {let bounds = map.getBounds(),
        southWest = bounds.getSouthWest(),
        northEast = bounds.getNorthEast(),
        lngSpan = northEast.lng - southWest.lng,
        latSpan = northEast.lat - southWest.lat;

      return L.latLng(southWest.lat + latSpan * Math.random(),
        southWest.lng + lngSpan * Math.random());
    };
    const createMakerByLatlng = (latlng, options) => {return L.marker(latlng, options);
    };
    const createMakerCluster = () => {return L.markerClusterGroup();
    };
    const initMap = () =>{console.log('初始化地图');
      // 天地图
      
      const image = L.tileLayer('http://t{s}.tianditu.gov.cn/img_w/wmts?tk=bb11a33c4377f10603478ed166691455&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}', {subdomains: [0, 1, 2, 3, 4, 5, 6, 7],
      })
      // 注记
      const cia = L.tileLayer('http://t{s}.tianditu.gov.cn/cia_w/wmts?tk=bb11a33c4377f10603478ed166691455&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}', {subdomains: [0, 1, 2, 3, 4, 5, 6, 7],
        transparent: true,
        zIndex: 3,
      })
      // 天地图图组
      const tiandiMap = L.layerGroup([image, cia,]);

      let myCenter = [31.95723698714103, 104.29901249999988]; // 设置地图核心
      mapMap = L.map('myMap2',{
        center: myCenter,
        layers: [tiandiMap],
        zoom: 1
      })
      console.log(3);
      console.log(mapMap);
      let cluster = createMakerCluster();
      for (let i = 0; i < 10000; i++) {let latlng = getRandomLatLng(mapMap);
        let maker = createMakerByLatlng(latlng);
        cluster.addLayer(maker);
      }

      mapMap.addLayer(cluster);
    }
    onBeforeUpdate(()=>{console.log('更新前')
    })
    onUpdated(()=>{console.log('更新后')
    })
    onBeforeUnmount(()=>{console.log('卸载前:vue3.0 的这个生命周期命名要区别于 vue2.0 的 beforeDestroy')
    })
    onUnmounted(()=>{console.log('卸载后:vue3.0 的这个生命周期命名要区别于 vue2.0 的 destroyed')
    })
    onRenderTracked((event) => {console.log(event);
      console.log('新增的钩子状态跟踪函数(跟踪全副的,须要本人去查看哪个变动了):其实就是跟踪 return 里数据的变动')
    })
    onRenderTriggered(event => {console.log(event);
      console.log('新增的钩子状态跟踪函数 ( 区别在与准确跟踪变动的那个数据')
    })
    watch(props.msg, (val, oldVal) => {console.log(val, oldVal);
    })
    return {}}
}
</script>

<style>
.mainMap2{
    width: 900px;
    height: 900px;
    border: 2px solid red;
}
</style>
退出移动版