【vue+leaflet】自定义控件(五)

老规矩, 一健三连, 先赞后看 先看效果图 自定义控件: 支持和自带控件有相同的增删改查功能, 处理与自带控件来回切换,互相使用的部分问题 新建一个组件 imgControl.vue

1, html

没什么东西,就一个div盒子装leaflet图层

javascript 复制代码
<template>
  <div class="imgBox">
    <div id="imageMap"></div>
  </div>
</template>

2,导入对应的包,css

javascript 复制代码
<script>
import 'leaflet/dist/leaflet.css'
import 'leaflet/dist/leaflet'
import 'leaflet/dist/leaflet-src'
import 'leaflet/dist/leaflet-src.esm'
import * as L from 'leaflet'
import 'leaflet.pm'
import 'leaflet.pm/dist/leaflet.pm.css'
export default {
  data() {
    return {
      map: null,
      bounds: [
        [0, 0],
        [0, 0],
      ], // 平面图大小
      url: require('../../../public/home/bgc0.jpg'), // 平面图地址
      devArrList: [], // 设备列表
      imageOverlay: '', // 图层
      isDrawingImageMarker: false,
      cancelButton: null, // 控件激活后旁边的取消按钮
    }
  },
 }
</script>

3, 初始化函数

初始化就开始注册自定义控件

javascript 复制代码
initMap() {
      this.map = L.map('imageMap', {
      ...// 省略, 与自定义控件无关,不知道参数的可以去往期文章观看
      })
    
      this.map.pm.addControls({
        position: 'topleft',
        .....// 省略, 与自定义控件无关,不知道参数的可以去往期文章观看
      })
      // 重点!!!
      // 自定义控件权限
      const addImageControl = L.Control.extend({
        options: { position: 'topleft' }, // 控件的位置,与默认的相同就可以
        onAdd: (map) => {
          const container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom')
          container.innerHTML = '<i class="fa fa-image" style="line-height:26px;"></i>'
          container.title = '绘制门' // 鼠标hover时候的title
          // 控件点击事件
          container.onclick = (event) => {
            // 阻止事件冒泡,防止地图点击事件触发
            event.stopPropagation()
            event.preventDefault()
            // 禁用双击放大
            map.doubleClickZoom.disable()
            // 切换绘制模式
            this.isDrawingImageMarker = !this.isDrawingImageMarker
            // 设置按钮激活样式, 激活控件旁边显示取消按钮
            if (this.isDrawingImageMarker) {
              container.style.boxShadow = 'inset 0 -1px 5px 2px rgba(81, 77, 77, 0.31)'
              // 添加自定义鼠标指针样式
              document.getElementById('imageMap').classList.add('custom-cursor')
              // 取消其他图层绘制
              map.pm.disableDraw()
              // 禁用全局编辑,拖拽,删除模式,  只让一个控件处于激活状态
              map.pm.disableGlobalEditMode()
              if (map.pm.globalDragModeEnabled()) {
                map.pm.toggleGlobalDragMode()
              }
              // 创建取消按钮及样式
              if (!this.cancelButton) {
                this.cancelButton = this.createCancelButton()
                this.cancelButton.addEventListener('click', (e) => {
                  // 阻止事件冒泡,防止首次点击不生效
                  e.stopPropagation()
                  this.onCancelButtonClick()
                })
                container.appendChild(this.cancelButton)
              }
              this.cancelButton.style.display = 'block'
            } else {
              container.style.boxShadow = 'none'
              // 移除自定义鼠标指针样式
              document.getElementById('imageMap').classList.remove('custom-cursor')
              if (this.cancelButton) {
                this.cancelButton.style.display = 'none'
                this.cancelButton = null
              }
            }
          }
          return container
        },
      })
      // 注册控件
      this.map.addControl(new addImageControl())
      // 处理地图点击事件
      this.map.on('click', (e) => {
        // 按钮激活 且点击的不是按钮 才绘制图片图层
        if (this.isDrawingImageMarker && !e.originalEvent.target.closest('.leaflet-control-custom')) {
          const marker = new L.Marker(e.latlng, {
            icon: L.icon({
              iconUrl: require('../../../public/home/doorClose.png'),
              iconSize: [24, 30],
              iconAnchor: [12, 15],
            }),
          }).addTo(this.map)
          this.map.fire('pm:create', {
            shape: 'ImageMarker',
            layer: marker,
          })
        }
      })
    },

4, 取消按钮及其点击事件

设置取消按钮的样式

javascript 复制代码
    // 取消按钮
    createCancelButton() {
      const button = document.createElement('div')
      button.className = 'cancel-button'
      button.innerHTML = '取消'
      button.onclick = (event) => {
        // 阻止事件冒泡
        event.stopPropagation()
        this.onCancelButtonClick()
      }
      return button
    },
    // 取消按钮点击事件
    onCancelButtonClick() {
      this.isDrawingImageMarker = false
      const control = document.querySelector('.leaflet-control-custom')
      if (control) control.style.boxShadow = 'none'
      if (this.cancelButton) {
        this.cancelButton.style.display = 'none'
        this.cancelButton = null
      }
      // 移除自定义鼠标指针样式
      document.getElementById('imageMap').classList.remove('custom-cursor')
    },

5, 初始化图层及绘制方法注册,绘制已图层的回显

主图层绘制, 绘制的图层回显, 部分系统事件,图层事件注册

javascript 复制代码
    initDate() {
      // 循环遍历图层删除
      this.map.eachLayer((layer) => {
        if (layer._latlngs != null || layer._latlng != null) {
          layer.remove()
        }
      })
      // 获取图片宽高
      let img = new Image()
      img.src = this.url
      img.onload = () => {
        let w = img.width
        let h = img.height
        this.bounds = [
          [0, 0],
          [h, w],
        ]
        // 添加图片,更换图片
        if (this.imageOverlay) {
          this.imageOverlay.setUrl(this.url)
          this.imageOverlay.setBounds(this.bounds)
        } else {
          this.imageOverlay = L.imageOverlay(this.url, this.bounds).addTo(this.map)
        }
        this.map.fitBounds(this.bounds)
      }
      // 循环绘制图层
      this.devArrList.forEach((rs) => {
        if (rs.point) {
          let re = JSON.parse(rs.point)
          // 画图片,门
          if (re.imageDoor && re.imageDoor.length) {
            re.imageDoor.forEach((e) => {
              let icon = L.divIcon({
                html,
                className: 'my-div-icon',
                iconAnchor: [20, 10],
                iconSize: [40, 20],
              })
              L.marker(e.latlng, {
                icon,
                type: 'imageDoor',
                textName: e.options.textName,
              })
                .addTo(this.map)
                .on('click', this.imageDoorClick)
            })
          }
        }
      })
      // 监听图层绘制完成
      this.map.on('pm:create', this.createClick)
      this.map.on('pm:remove', this.removeClick)
      this.map.on('pm:drawstart', this.drawstartClick)
      this.map.on('pm:drawend', this.drawendClick)
      this.map.on('pm:globalremovalmodetoggled', this.globalDeleteClick)
      this.map.on('pm:globaleditmodetoggled', this.globalEditClick)
      this.map.on('pm:globaldragmodetoggled', this.globalDragClick)
      // 禁止背景图拖拽
      this.map.dragging.disable()
      // 禁止双击缩放
      this.map.doubleClickZoom.disable()
      // 禁止滚动缩放
      this.map.scrollWheelZoom.disable()
    },

6, 注册的事件方法,逻辑处理模块

1, 监听绘制过程中点击右键快速结束绘制 2, 与系统控件来回切换处理自定义控件的样式及取消按钮的显影

javascript 复制代码
  // 开始绘制
    drawstartClick(e) {
      // 关闭门图片绘制
      this.onCancelButtonClick()
      // 绘制圆形右键快速结束绘制
      if (e.shape == 'CircleMarker') {
        // 监听右键事件
        this.map.on('contextmenu', () => {
          this.map.pm.Draw[e.shape].disable()
        })
      }
    },
    // 结束绘制
    drawendClick(e) {
      console.log('结束绘制', e)
      // 卸载右键事件
      this.map.off('contextmenu')
    },
    // 删除控件
    globalDeleteClick(e) {
      // console.log('删除', e)
      // enabled ture开始删除才取消激活门绘制
      // if (e.enabled) {
        this.onCancelButtonClick()
      // }
    },
    // 编辑控件
    globalEditClick(e) {
      // console.log('编辑', e)
      // enabled ture开始编辑才取消激活门绘制
      if (e.enabled) {
        this.onCancelButtonClick()
      }
    },
    // 拖拽控件
    globalDragClick(e) {
      // console.log('拖拽', e)
      // enabled ture开始拖拽才取消激活门绘制
      if (e.enabled) {
        this.onCancelButtonClick()
      }
    },
    // 图层绘制完成
    createClick(e) {
      console.log('图层绘制完成', e)
      if (e.shape == 'ImageMarker') {
        // 画图片门
        e.layer.on('click', this.imageDoorClick)
        e.layer.options.type = 'imageDoor'
      }
    },
    // 图层删除
    removeClick(e) {
      console.log('图层删除', e)
    },
    // 图片门点击
    imageDoorClick(e) {
      console.log('图片门点击', e)
    },
    // 图片门拖拽
    dragendImageDoorClick(e) {
      console.log('图片门拖拽', e)
    },

7, 调用方法

javascript 复制代码
  mounted() {
    this.initMap()
    this.initDate()
  },

8, 自定义的样式,激活的鼠标指针样式

css 复制代码
.imgBox {
  width: calc(100% - 20px);
  height: 100%;
  padding: 0 10px;
  #imageMap {
    height: 100%;
    box-sizing: border-box;
  }
}
.leaflet-container {
  font-size: 0.2rem;
  background: #fff;
}
.leaflet-div-icon {
  background: transparent;
  text-align: center;
  color: #fff;
}
/deep/.my-div-icon {
  color: #000;
  text-align: center;
}
// 控件样式
/deep/.leaflet-control-custom {
  background-color: white;
  background-image: url('../../../public/home/doorClose.png');
  background-size: 80% 80%;
  background-repeat: no-repeat;
  background-position: center;
  width: 30px;
  height: 30px;
  cursor: pointer;
  &:hover {
    background-color: #f4f4f4;
  }
}
// 取消按钮
/deep/.cancel-button {
  background-color: #666666;
  color: white;
  width: 52px;
  text-align: center;
  line-height: 30px;
  height: 30px;
  cursor: pointer;
  position: absolute;
  border-radius: 0px 3px 3px 0px;
  top: 0px;
  left: 31px;
  z-index: 2;
}
// 鼠标指针
/deep/.custom-cursor {
  cursor: url('/home/cursor.ico') 12 15, auto !important;
}
// 门
/deep/.door {
  position: relative;
  .name {
    position: absolute;
    left: 7px;
    top: 7px;
  }
}

9, 引入组件

直接导入就可以了

javascript 复制代码
<template>
  <div class="imgBox">
    <ImgControl />
  </div>
</template>
<script>
import ImgControl from '@/components/imgLayout/imgControl.vue'
export default {
  components: {
    ImgControl,
  },
  data() {
    return {}
  },
  methods: {},
  mounted() {},
}
</script>
<style lang="less" scoped>
.imgBox {
  width: 100%;
  height: 100%;
}
</style>

如果主页是在模态框中导入这个组件, 会出现多次点击模态框组件内容的图层内容不会重新绘制的问题, 给组件绑定一个key更新组件就可以了 例如:

javascript 复制代码
  <!-- key 模态框关闭,再开启时更新重载组件 -->
        <ImgLayout v-if="isDevBind" ref="layout" :key="updateKey" />
        ....
        updateKey:new Date().getTime()

到这里基本上就可以实现自定义控件通过组件的形式在主页面中使用了 组件的增删改查方法前几篇文章都有写, 这里就省略了, 简洁一点 实例代码及使用到的图片已上传, 点击前往获取--->

相关推荐
kyriewen2 分钟前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技3 分钟前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人14 分钟前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实14 分钟前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha25 分钟前
三目运算符
linux·服务器·前端
晓晨的博客32 分钟前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect40 分钟前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript
donecoding1 小时前
别再让 pnpm 跟着 nvm 跑了!独立安装终极指南
前端·node.js·前端工程化
不可能的是1 小时前
从 /simplify 指令深挖 Claude Code 多 Agent 协同机制
javascript
GISer_Jing1 小时前
AI全栈转型_TS后端学习路线
前端·人工智能·后端·学习