vue2通过leaflet实现图片点位回显功能

需求:在图片上标点了,需要根据标记点在图片上进行回显功能,并且不会根据窗口大小导致标记点移位

1.效果

2.下载插件

用到的是leaflet插件:一个交互式地图 JavaScript 库,我下载是 "leaflet": "^1.9.4"

sql 复制代码
npm install leaflet

引入到项目

icon是自带的图标

javascript 复制代码
import icon from 'leaflet/dist/images/marker-icon.png';
import * as L from 'leaflet';
import 'leaflet/dist/leaflet.css';

3.主要代码详解

图片加载后需要创建map对象,注意!!如果后端传的点数据是根据图片原有大小比如图片尺寸800*800,x:20,y:20,就是在800像素上的xy的值那么直接设置leftmap的unproject为图片本身的尺寸即可

javascript 复制代码
​
       var south_west = that.leftMap.unproject([0, img_original_height], 1); //西南

       var north_east = that.leftMap.unproject([img_original_width, 0], 1);

​

如果图片原尺寸为800*800,后端传来尺寸为400*400,x:10,y:10,需要设置如下代码

javascript 复制代码
      var style = window.getComputedStyle(document.getElementById("roc_map"));
      var map_height = parseFloat(style.height);     
 var south_west = that.leftMap.unproject([0, map_height], 1); //西南
        var north_east = that.leftMap.unproject(
          [img_original_width / this_ratio, 0],
          1
        );

创建点:draggable:是否拖拽L.marker上面的这些代码全部都是坐标转换的,就是为了应对原有尺寸和后端尺寸不对应的问题

javascript 复制代码
        // 1. 获取旧图片尺寸(pointArr中保存的width/height)
                        const oldWidth = obj_.width; // 例如 1036
                        const oldHeight = obj_.height; // 例如 582
                        // 3. 根据地理边界 this_bounds 计算实际的地理坐标
                        const boundsWidth = this_bounds.getEast() - this_bounds.getWest();
                        const boundsHeight = this_bounds.getNorth() - this_bounds.getSouth();
                        const lng = this_bounds.getWest() + (boundsWidth / oldWidth) * obj_.x;
                        const lat = this_bounds.getNorth() - (+boundsHeight / oldHeight) * obj_.y; // Y轴取反
                        const DefaultIcon = L.icon({
                            iconUrl: icon,
                            iconAnchor: [10, 41]
                        });
                        // 4. 创建标记点(Leaflet的Y轴需要取反)
                        const marker = L.marker([lat, lng], {
                            draggable: false,
                            title: obj_.name,
                            icon: DefaultIcon
                        }).addTo(that.leftMap);

创建点弹窗,弹窗样式自行设计

javascript 复制代码
   const popup = L.popup().setContent('加载中...');
                        marker.bindPopup(popup);

4.完整代码

javascript 复制代码
<!--
 * @Description:
 * @Author: 请叫我欧皇!
 * @Date: 2025-04-28 14:58:23
 * @FilePath: \vue-secondMenu-test-master\src\page\test4\zongti\02.leftjs.vue
-->
<template>
    <div class="leaflet-box">
        <div id="roc_map" class="map"></div>
    </div>
</template>

<script>
import icon from 'leaflet/dist/images/marker-icon.png';
import * as L from 'leaflet';
import 'leaflet/dist/leaflet.css';
export default {
    data() {
        return {
            pointArr: [
                {
                    x: 80,
                    y: 50,
                    name: '测试111',
                    group: ['测试111'],
                    point_id: 1,
                    sn: '123456789',
                    width: 800,
                    height: 572
                },
                {
                    x: 200,
                    y: 0,
                    name: '测试222',
                    group: ['测试222'],
                    point_id: 2,
                    sn: '123456789',
                    width: 800,
                    height: 572
                },
                {
                    x: 200,
                    y: 200,
                    name: '测试333',
                    group: ['测试222'],
                    point_id: 3,
                    sn: '123456789',
                    width: 800,
                    height: 572
                }
            ],
            leftMap: null
        };
    },
    mounted() {
        this.initLeaflet();
    },
    methods: {
        initLeaflet() {
            let that = this;
            let pointArr = this.pointArr;
            var mark_map = {};
            var this_img = new Image();
            let imgs = 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg';
            // this_img.src = "../images/01.jpg";
            this_img.src = imgs;

            this_img.onload = function () {
                var img_original_width = this_img.width,
                    img_original_height = this_img.height;
                // 在初始化地图前检查是否已存在地图实例
                if (that.leftMap) {
                    that.leftMap.remove(); // 移除旧地图
                    that.leftMap = null;
                }
                that.leftMap = new L.Map('roc_map', {
                    minZoom: 1,
                    maxZoom: 4,
                    center: [0, 0],
                    zoom: 1,
                    crs: L.CRS.Simple,
                    zoomControl: false,
                    attributionControl: false
                    // zoomControl: false, // 禁用默认的缩放控件
                });

                var south_west = that.leftMap.unproject([0, img_original_height], 1); //西南
                var north_east = that.leftMap.unproject([img_original_width, 0], 1);
                var this_bounds = new L.LatLngBounds(south_west, north_east);

                L.imageOverlay(imgs, this_bounds).addTo(that.leftMap);
                that.leftMap.fitBounds(this_bounds);
                init_p();

                //   init_grid_2();
                function init_p() {
                    for (var i = 0; i < pointArr.length; i++) {
                        let obj_ = pointArr[i];

                        // 1. 获取旧图片尺寸(pointArr中保存的width/height)
                        const oldWidth = obj_.width; // 例如 1036
                        const oldHeight = obj_.height; // 例如 582
                        // 3. 根据地理边界 this_bounds 计算实际的地理坐标
                        const boundsWidth = this_bounds.getEast() - this_bounds.getWest();
                        const boundsHeight = this_bounds.getNorth() - this_bounds.getSouth();
                        const lng = this_bounds.getWest() + (boundsWidth / oldWidth) * obj_.x;
                        const lat = this_bounds.getNorth() - (+boundsHeight / oldHeight) * obj_.y; // Y轴取反
                        const DefaultIcon = L.icon({
                            iconUrl: icon,
                            iconAnchor: [10, 41]
                        });
                        // 4. 创建标记点(Leaflet的Y轴需要取反)
                        const marker = L.marker([lat, lng], {
                            draggable: false,
                            title: obj_.name,
                            icon: DefaultIcon
                        }).addTo(that.leftMap);
                        const popup = L.popup().setContent('加载中...');
                        marker.bindPopup(popup);
                        // 最新监测时间:2025-12-23 00:00:00 累计变化量X:1234.45mm 累计变化量Y:12345.567mm
                        // .bindPopup(that.getBindPopup(obj_))
                        marker.on('click', async function (e) {
                            that.sendPointInfo = { ...obj_ };

                            // Show loading message
                            marker
                                .bindPopup("<div style='width:260px;height:150px;font-size:12px' class='popup'>加载中...</div>")
                                .openPopup();

                            try {
                                const popupContent = `<div style='width:260px;height:150px;font-size:13px' class='popup'>
                <div class="title">点编号:${obj_.name}</div>
                <ul>
                  <li>sn:${obj_.sn}</li>
                </ul>
              </div>`;
                                popup.setContent(popupContent);

                                //                             }
                            } catch (error) {
                                popup.setContent('请求数据时出错');
                            }
                        });

                        mark_map[obj_.point_id] = marker;
                    }
                }
            };
        }
    }
};
</script>

<style lang="scss" scoped>
.leaflet-box {
    width: 100%;
    height: 800px;
    .map {
        width: 50%;
        height: 70%;
        margin: 40px;
    }
}
</style>

文章到此结束,希望对你有所帮助~