高德地图——态势标绘,创建点、移动位置、缩放、旋转、保存、删除

  • 拖动左侧图标到地图上某个位置,在地图上创建该类型的点,存储在list
  • 拖拽点移动,重新更新其坐标
  • 编辑点的大小及角度,清除之前的重新创建新的点
  • 删除点,清除点,但数据不能清除,更改show
  • 最后保存list数据并截图保存
  • 下次进来渲染点重新编辑
js 复制代码
<template>
  <div class="index5 w100 h100 white relative">
    <canvas
      id="myCanvas"
      style="display: none; width: 100vw; height: 100vh"
    ></canvas>
    <img
      src="./components/img/home.png"
      alt=""
      class="home pointer"
      @click="$router.back()"
    />
    <!-- 地图区域 -->
    <div id="containerMap" class="w100 h100" ref="htmlImg"></div>
    <!-- 左侧菜单 -->
    <div
      v-show="showMenu"
      class="left h80 absolute animate__animated animate__slideInLeft animate__fast"
    >
      <div class="w100 pdRem-20 h100">
        <dv-border-box-8
          :color="['rgba(2, 208, 249,.5)', '#0dcff2']"
          backgroundColor="rgba(4, 33, 66,.8)"
        >
          <div class="plrRem-20 ptbRem-10 flex flex-column w100 h100">
            <div class="title flex_l mtb-20">
              <img src="./components/img/index1-title.png" alt="" />
              作战标绘
            </div>
            <div class="flex flex-wrap w100">
              <div
                class="item plr-20 text-center mb-20 pointer"
                style="width: 50%"
                v-for="(item, index) in tab"
                :key="index"
              >
                <!-- ☆☆☆☆☆ 拖拽图片默认会打开窗口预览,换成背景图即可 -->
                <!-- <img :src="item.icon" alt="" style="width: 50px" /> -->
                <div
                  draggable="true"
                  @dragstart="onDragStart"
                  @dragend="onDragEnd"
                  :data-index="index"
                  class="img-bg"
                  :style="{
                    backgroundImage: `url(${item.icon})`,
                    backgroundRepeat: 'no-repeat',
                    backgroundPosition: 'center',
                    backgroundSize: 'cover',
                    backgroundAttachment: 'local',
                  }"
                ></div>

                <div class="mt-4">{{ item.text }}</div>
              </div>
            </div>
            <img
              src="./components/img/index5-btn4.png"
              alt=""
              style="width: 140px; margin: 60px auto 20px"
              @click="issueImg"
              class="pointer"
            />
          </div>
        </dv-border-box-8>
      </div>
    </div>
    <!-- 右下控制 -->
    <div
      v-show="showMenu && showEdit"
      class="right absolute animate__animated animate__slideInLeft animate__fast pdRem-20"
    >
      <div class="w100 h100 editBox ptRem-14 pbRem-10 plRem-10 prRem-20">
        <div class="plrRem-20 ptbRem-10 flex flex-column w100 h100 editContent">
          <div class="title flex_l mtb-10">
            <img src="./components/img/index1-title.png" alt="" />
            标绘属性
          </div>
          <div class="plrRem-20 mbRem-20 flex flex-column">
            <div class="flex1">
              <div style="width: 60px; font-size: 0.2rem">缩放:</div>
              <div class="flex-1">
                <el-slider
                  :max="10"
                  :min="0.5"
                  :step="0.5"
                  v-model="scale"
                  @change="editMarker"
                ></el-slider>
              </div>
            </div>
            <div class="flex1">
              <div style="width: 60px; font-size: 0.2rem">角度:</div>
              <div class="flex-1">
                <el-slider
                  :max="360"
                  :step="5"
                  v-model="rotate"
                  @change="editMarker"
                ></el-slider>
              </div>
            </div>
          </div>
          <div class="w100 center mb-20">
            <!-- <img
              src="./components/img/index5-btn1.png"
              alt=""
              style="width: 140px"
              @click="saveImg"
              class="pointer"
              v-if="isSave"
            /> -->
            <img
              src="./components/img/index5-btn3.png"
              alt=""
              style="width: 120px"
              @click="removeMarker"
              class="pointer"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import html2canvas from "html2canvas";

import "./components/js/flexible.js";
import {
  getPoliceDetailApi,
  uploadHtmlImg,
  dispatchHtmlImg,
} from "./components/js/api";

import AMapLoader from "@amap/amap-jsapi-loader";
import icon1 from "./components/img/index5-1.png";
import icon2 from "./components/img/index5-2.png";
import icon3 from "./components/img/index5-3.png";
import icon4 from "./components/img/index5-4.png";
import icon5 from "./components/img/index5-5.png";
import icon6 from "./components/img/index5-6.png";
import icon7 from "./components/img/index5-7.png";
import icon8 from "./components/img/index5-8.png";
import icon9 from "./components/img/index5-9.png";

//秘钥
window._AMapSecurityConfig = {
  securityJsCode: "3e9a9aeb435372339552fc215f8d0918",
};
export default {
  data() {
    return {
      position: [],
      policeId: "",
      map: null,
      showMenu: true,
      tab: [
        {
          id: 1,
          icon: icon1,
          text: "消防车",
        },
        {
          id: 2,
          icon: icon2,
          text: "消防员",
        },
        {
          id: 3,
          icon: icon3,
          text: "水管",
        },
        {
          id: 4,
          icon: icon4,
          text: "进攻方向",
        },
        {
          id: 5,
          icon: icon5,
          text: "消防栓",
        },
        {
          id: 6,
          icon: icon6,
          text: "危化品",
        },
        {
          id: 7,
          icon: icon7,
          text: "风向",
        },
        {
          id: 8,
          icon: icon8,
          text: "指北矢标",
        },
      ],
      list: [], //地图上所有的marker
      draggingEnded: false,
      index: null, //当前拖动的索引-对应左侧菜单
      showEdit: false, //是否显示编辑
      item: {}, //当前编辑的marker
      // 编辑
      scale: 1, //缩放
      rotate: 0, //旋转角度

      //保存
      baseUrl: process.env.VUE_APP_BASE_API,
      isSave: false,
     
    };
  },
  mounted() {
    this.position = this.$route.query.position.split(",");
    this.policeId = this.$route.query.id;
    this.initAMap();
  },
  beforeDestroy() {
    this.map.destroy();
  },
  methods: {
    // -----------地图--------------
    initAMap() {
      AMapLoader.load({
        key: "130cca3be68a2ff0fd5ebb6de25e4eac", // 申请好的Web端开发者Key,首次调用 load 时必填
        version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
        plugins: [
          //   "AMap.ControlBar",
          //   "AMap.ToolBar",
          //   "AMap.Weather",
          //   "AMap.CitySearch",
          //   "AMap.Marker",

          //   "AMap.MouseTool",
          //   "AMap.PolyEditor",
          //   "AMap.Polyline",
          //   "AMap.Geolocation",
          //   "AMap.GraspRoad",
          //   "AMap.Geocoder",
          //   "AMap.GeometryUtil.ringArea",
          //   "AMap.DistrictSearch",
          //   "AMap.MoveAnimation",
          "AMap.Driving", // 驾车路线路径规划
        ], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
      })
        .then((AMap) => {
          this.map = new AMap.Map("containerMap", {
            // 设置地图容器id
            // rotateEnable: true, // 旋转
            // pitchEnable: true, // 倾斜
            zoom: 13, // 初始化地图层级
            // pitch: 50, // 倾斜角度
            // rotation: -15, // 地图旋转角度
            // viewMode: "3D", //开启3D视图,默认为关闭
            zooms: [2, 20], // 设置地图显示范围
            center: [119.419251, 32.400703], // 设置地图中心点
            mapStyle: "amap://styles/blue", //设置地图的显示样式
            WebGLParams: {
              preserveDrawingBuffer: true, // 使用html2canvas截取需要设置该属性,否则截取不到地图
            },
          });

          this.map.on("complete", () => {
            console.log("地图加载完成");
            this.isSave = true;
            this.createFireMarker();
            //可以监听到拖动到地图
            this.map.on("mouseover", this.onMouseOver);
            getPoliceDetailApi(this.policeId).then((res) => {
              if (res.data.screenshotParam) {
                this.list = JSON.parse(res.data.screenshotParam);
                this.showMarker();
              }
            });
          });
          this.map.on("click", (e) => {
            // e 是事件对象,包含了点击的位置等信息
            var lnglat = e.lnglat; // 获取经纬度坐标
            console.log("地图被点击,坐标:" + lnglat.lng + ", " + lnglat.lat);
            this.showEdit = false;
            // 在此处可以进行进一步的操作,如弹出信息窗口、绘制标记等
          });
        })
        .catch((e) => {
          console.log(e);
        });
    },
    //----------添加警情中心点标记 -------
    createFireMarker() {
      var marker = new AMap.Marker({
        position: new AMap.LngLat(...this.position),
        icon: new AMap.Icon({
          image: fireIcon,
          imageSize: new AMap.Size(32, 42),
        }),
        // zIndex: 200,
      });
      this.map.add(marker);
      // 设置地图中心点
      this.map.setCenter(new AMap.LngLat(...this.position));
    },
    onDragStart(event) {
      // console.log("Drag start", event, event.target.dataset.index);
      //event.dataTransfer.setData(
      //  "application/index",
      //  event.target.dataset.index
      //);
      // 你还可以在这里做其他事情,比如改变元素的样式或状态
      this.draggingEnded = false;
      this.index = event.target.dataset.index * 1;
    },
    onDragEnd(event) {
      // console.log(
      //   "Drag end",
      //   event,
      //   event.dataTransfer.getData("application/index")
      // );
      //const data = event.dataTransfer.getData("application/index"); // 获取拖动时设置的数据
      // console.log("接收到的数据:", data);
      // 拖拽结束时,你可以做一些清理工作,例如重置元素样式或状态
      this.draggingEnded = true;
    },
    onMouseOver(event) {
      // console.log("mouseover", event.lnglat);
      if (this.draggingEnded) {
        // 转换鼠标位置为地理坐标
        this.draggingEnded = false;
        this.createMarker([event.lnglat.lng, event.lnglat.lat]);
      }
    },
    // 页面一进来拿到上次的list渲染出来
    showMarker() {
      this.list.forEach((item, index) => {
        item.show = true;
        item.marker = null;
        item.icon = [icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8][
          item.id * 1
        ];
        var markerContent =
          '<div class="custom-content-marker" >' +
          '<img style="transform:rotate(' +
          item.rotate +
          "deg) scale(" +
          item.scale +
          ');width:60px;height:60px" src=" ' +
          item.icon +
          '">' +
          "</div>";
        item.marker = new AMap.Marker({
          position: new AMap.LngLat(...item.lnglat),
          content: markerContent,
          // 以 icon 的 [center bottom] 为原点
          offset: new AMap.Pixel(-25, -50),
          // zIndex: 200,
          cursor: "move",
          draggable: true,
        });
        this.map.add(item.marker);

        // 监听点点击事件
        this.list[index].marker.on("click", (e) => {
          this.item = this.list[index];
          this.scale = this.item.scale;
          this.rotate = this.item.rotate;
          this.showEdit = true;
          //拖动坐标获取新坐标
          console.log("点击:", this.item.id);
        });

        // 监听点拖动更新坐标
        this.list[index].marker.on("dragend", (e) => {
          this.list[index].lnglat = [
            this.list[index].marker.getPosition().lng,
            this.list[index].marker.getPosition().lat,
          ];
          this.item = this.list[index];
          this.scale = this.item.scale;
          this.rotate = this.item.rotate;
          this.showEdit = true;
          //拖动坐标获取新坐标
          console.log(
            "最新坐标:",
            [
              this.list[index].marker.getPosition().lng,
              this.list[index].marker.getPosition().lat,
            ],
            this.list,
            this.item
          );
        });
      });

      console.log("createMarker", this.list);
    },
    // 从左侧拖动过来新建点
    createMarker(lnglat) {
      let length = this.list.length;
      this.list.push({
        id: this.index,
        lnglat: lnglat,
        icon: this.tab[this.index].icon,
        marker: null,
        scale: 1,
        rotate: 0,
        show: true,
      });
      this.item = this.list[length];
      console.log("createMarker", this.index);
      var markerContent =
        '<div class="custom-content-marker" >' +
        '<img style="transform:rotate(' +
        this.item.rotate +
        "deg) scale(" +
        this.item.scale +
        ');width:60px;height:60px" src=" ' +
        this.item.icon +
        '">' +
        "</div>";
      this.item.marker = new AMap.Marker({
        position: new AMap.LngLat(...this.item.lnglat),
        content: markerContent,
        // 以 icon 的 [center bottom] 为原点
        offset: new AMap.Pixel(-25, -50),
        // zIndex: 200,
        cursor: "move",
        draggable: true,
      });
      this.map.add(this.item.marker);
      this.showEdit = true;
      this.scale = this.item.scale;
      this.rotate = this.item.rotate;
      console.log("createMarker", this.list, this.item);

      // 监听点点击事件
      this.list[length].marker.on("click", (e) => {
        this.item = this.list[length];
        this.scale = this.item.scale;
        this.rotate = this.item.rotate;
        this.showEdit = true;
        //拖动坐标获取新坐标
        console.log("点击:", this.item.id);
      });

      // 监听点拖动更新坐标
      this.list[length].marker.on("dragend", (e) => {
        this.list[length].lnglat = [
          this.list[length].marker.getPosition().lng,
          this.list[length].marker.getPosition().lat,
        ];
        this.item = this.list[length];
        this.scale = this.item.scale;
        this.rotate = this.item.rotate;
        this.showEdit = true;
        //拖动坐标获取新坐标
        console.log(
          "最新坐标:",
          [
            this.list[length].marker.getPosition().lng,
            this.list[length].marker.getPosition().lat,
          ],
          this.list,
          this.item
        );
      });
    },
    
    // 编辑点重新渲染
    editMarker() {
      var index = this.list.indexOf(this.item);
      console.log("编辑", this.rotate, this.scale, this.list[index]);
      this.list[index].scale = this.scale;
      this.list[index].rotate = this.rotate;
      this.map.remove(this.list[index].marker);
      this.list[index].marker = null;
      var markerContent =
        '<div class="custom-content-marker"  >' +
        '<img style="transform:rotate(' +
        this.list[index].rotate +
        "deg) scale(" +
        this.list[index].scale +
        ');width:60px;height:60px" src=" ' +
        this.list[index].icon +
        '">' +
        "</div>";
      console.log("编辑", markerContent);
      this.list[index].marker = new AMap.Marker({
        position: new AMap.LngLat(...this.list[index].lnglat),
        content: markerContent,
        // 以 icon 的 [center bottom] 为原点
        offset: new AMap.Pixel(-25, -50),
        // zIndex: 200,
        cursor: "move",
        draggable: true,
      });
      this.map.add(this.list[index].marker);
      // 监听点点击事件
      this.list[index].marker.on("click", (e) => {
        this.item = this.list[index];
        this.scale = this.list[index].scale;
        this.rotate = this.list[index].rotate;
        this.showEdit = true;
        //拖动坐标获取新坐标
        console.log("点击11:", this.item.id);
      });

      // 监听点拖动更新坐标
      this.list[index].marker.on("dragend", (e) => {
        this.item = this.list[index];
        this.item.lnglat = [
          this.list[index].marker.getPosition().lng,
          this.list[index].marker.getPosition().lat,
        ];
        this.scale = this.list[index].scale;
        this.rotate = this.list[index].rotate;
        this.showEdit = true;
      });
    },
    // 删除点
    removeMarker() {
      var index = this.list.indexOf(this.item);
      this.list[index].show = false;
      this.map.remove(this.list[index].marker);
      this.item = {};
      this.showEdit = false;
      console.log("删除", this.list, this.item, index);
    },

    /* -----------------------------下发作战--------------------- */
    issueImg() {
      this.showMenu = false;
      let arr = this.list.filter((item) => item.show);
      let arr1 = arr.map((item) => {
        return {
          id: item.id,
          lnglat: item.lnglat,
          scale: item.scale,
          rotate: item.rotate,
        };
      });

      html2canvas(this.$refs.htmlImg, {
        useCORS: true, //保证跨域图片的显示,如果为不添加改属性,或者值为false,地图底图不显示
        x: window.pageXOffset, //页面在水平方向的滚动距离
        y: window.pageYOffset, //页面在垂直方向的滚动距离
      }).then((canvas) => {
        var img = new Image();
        var canvas2 = document.getElementById("myCanvas");
        var ctx = canvas2.getContext("2d");
        ctx.fillStyle = "#FFFFFF"; //画布填充色
        ctx.lineWidth = "1";
        ctx.rect(20, 30, canvas2.width - 40, canvas2.height - 60); //矩形距离画布左上角水平和垂直距离,矩形的宽高
        ctx.stroke();
        img.src = canvas.toDataURL();

        var url = canvas.toDataURL("image/png");
        const blobImage = this.dataURLtoBlob(url);
        let fileName = `htmlImg_${new Date().getTime()}.jpg`;
        const fileOfBlob = new File([blobImage], fileName);
        let formData = new FormData();
        formData.append("file", fileOfBlob, fileName);
        this.isSave = false;
        uploadHtmlImg(formData).then((res) => {
          this.showMenu = true;
          this.isSave = true;
          // this.$modal.msgSuccess("上传成功");

          let saveObj = {
            policeId: this.$route.query.id,
            // policeId: 196,
            fileName: res.fileName,
            url: res.url,
            screenshotParam: JSON.stringify(arr1),
          };
          dispatchHtmlImg(saveObj).then((res) => {
            this.$modal.msgSuccess("标绘下发成功,可在小程序对应警情中查看");
          });
          console.log("http://192.168.0.19:12020" + res.url);
          // console.log(this.baseUrl + res.url);
        });
      });
    },
    // 转成bolb对象
    dataURLtoBlob(dataUrl) {
      let arr = dataUrl.split(","),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], {
        type: mime,
      });
    },
   
    
  },
};
</script>

<style scoped lang="scss">
@import "./components/css/rem.scss";
.index5 {
  .home {
    width: 50px;
    position: fixed;
    right: 20px;
    top: 20px;
    z-index: 9999;
  }
  .left {
    width: 280px;
    left: 0;
    top: 10%;
    .img-bg {
      width: 50px;
      height: 50px;
      margin: auto;
    }
  }
  .right {
    width: 360px;
    // height: 280px;
    right: 50px;
    bottom: 50px;

    .editBox {
      background: #0005495d;
      background-image: url("./components/img/index5-bg-1.png");
      background-repeat: no-repeat; /* 防止重复平铺 */
      background-position: center; /* 居中对齐 */
      background-size: 100% 100%; /* 根据容器大小等比例缩放至最大尺寸 */
      background-attachment: local;
      .editContent {
        // background-image: url("./components/img/index5-bg-2.png");
        // background-repeat: no-repeat; /* 防止重复平铺 */
        // background-position: center; /* 居中对齐 */
        // background-size: 100% 100%; /* 根据容器大小等比例缩放至最大尺寸 */
        // background-attachment: local;
      }
    }
  }
  ::v-deep .el-slider__runway {
    background-color: rgba(0, 255, 255, 0.253);
  }
  ::v-deep .el-slider__bar {
    background-color: aqua;
  }
  ::v-deep .el-slider__button {
    border: 2px solid aqua;
  }

  ::v-deep .amap-marker-label {
    background: rgba(0, 0, 0, 0);
    color: aqua;
    border: none;
  }
  ::v-deep .amap-icon {
    width: 50px !important;
    height: 50px !important;
  }
  .title {
    font-size: 0.2rem;
    font-weight: 700;
    text-shadow: 0px 3px 7px #006cbf;
    img {
      width: 0.4rem;
      height: 0.4rem;
    }
  }

  .carInfo {
    position: absolute;
    border-radius: 10px;
    border: 1px solid cyan;
    background: rgba(0, 50, 73, 0.8);
    // left: 120px;
    // top: 230px;
    // background: url("./components/img/car-bg.png") no-repeat center center;
    background-size: 100% 100%;
    padding: 16px 10px 14px 20px;
    ::v-deep .dv-water-pond-level text {
      font-size: 15px;
      font-weight: normal !important;
      color: #fff !important;
    }
  }
}
.main {
  height: 1600px;
  width: 100%;
  background: #d4edff;
}
.svg {
  border: 1px solid red;
  width: 100px;
  height: 100px;
}
</style>
相关推荐
前端老宋Running8 分钟前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔8 分钟前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户44455436542610 分钟前
Android的自定义View
前端
WILLF11 分钟前
HTML iframe 标签
前端·javascript
枫,为落叶28 分钟前
Axios使用教程(一)
前端
小章鱼学前端33 分钟前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah34 分钟前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript
流星稍逝36 分钟前
手搓一个简简单单进度条
前端
倚栏听风雨1 小时前
详解 TypeScript 中,async 和 await
前端
小皮虾1 小时前
告别服务器!小程序纯前端“图片转 PDF”工具,隐私安全又高效
前端·javascript·微信小程序