基于Uni-app+vue3实现微信小程序地图固定中心点范围内拖拽选择位置功能(分步骤详解)

一、功能概述与实现步骤

1.1 功能需求

  • 显示地图并固定中心点标记

  • 绘制服务区域多边形边界

  • 实时检测拖拽后位置是否在服务区内

  • 提供位置确认和超出范围提示功能

1.2 实现步骤分解

第一步:初始化地图基础配置
  1. 创建Map组件并设置基本属性

  2. 定义服务区域多边形坐标

  3. 设置地图初始中心点

第二步:实现地图交互逻辑
  1. 监听地图拖拽事件

  2. 获取拖拽后中心点坐标

  3. 判断坐标是否在服务区内

第三步:实现覆盖层UI
  1. 固定中心点标记

  2. 位置信息显示面板

  3. 操作按钮(确认/返回服务区)

    二、分步骤代码实现

    2.1 第一步:地图基础配置

    javascript 复制代码
    <template>
      <view class="map-container">
        <map
          id="map"
          style="width: 100%; height: 80vh"
          :latitude="center.latitude"
          :longitude="center.longitude"
          :polygons="polygons"
          @regionchange="handleMapDrag"
          :show-location="true"
        >
          <!-- 覆盖层将在第三步添加 -->
        </map>
      </view>
    </template>
    
    <script setup>
    import { ref, onMounted } from "vue";
    
    // 服务区域边界坐标
    const serviceAreaPolygon = [
      { latitude: 34.808, longitude: 113.55 },
      { latitude: 34.805, longitude: 113.58 },
      // ...其他坐标点
      { latitude: 34.808, longitude: 113.55 } // 闭合多边形
    ];
    
    // 中心点位置
    const center = ref({
      latitude: 34.747, 
      longitude: 113.625
    });
    
    // 多边形配置
    const polygons = ref([{
      points: serviceAreaPolygon,
      strokeWidth: 2,
      strokeColor: "#1E90FF",
      fillColor: "#1E90FF22"
    }]);
    
    // 初始化地图上下文
    const mapContext = ref(null);
    onMounted(() => {
      mapContext.value = uni.createMapContext("map");
    });
    </script>

    2.2 第二步:地图交互逻辑实现

    javascript 复制代码
    // 当前坐标点
    const currentPos = ref({ ...center.value });
    // 是否在服务区内
    const isInServiceArea = ref(true);
    
    // 地图拖拽事件处理
    const handleMapDrag = (e) => {
      if (e.type === "end") {
        mapContext.value.getCenterLocation({
          success: (res) => {
            currentPos.value = {
              latitude: res.latitude,
              longitude: res.longitude
            };
            // 判断是否在服务区内
            isInServiceArea.value = isPointInPolygon(
              currentPos.value,
              serviceAreaPolygon
            );
          }
        });
      }
    };
    
    // 射线法判断点是否在多边形内
    function isPointInPolygon(point, polygon) {
      const { latitude, longitude } = point;
      let inside = false;
      
      for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
        const xi = polygon[i].longitude, yi = polygon[i].latitude;
        const xj = polygon[j].longitude, yj = polygon[j].latitude;
        
        const intersect = ((yi > latitude) !== (yj > latitude))
          && (longitude < (xj - xi) * (latitude - yi) / (yj - yi) + xi);
        
        if (intersect) inside = !inside;
      }
      
      return inside;
    }

    2.3 第三步:覆盖层UI实现

    javascript 复制代码
    <!-- 在map标签内添加 -->
    <cover-view class="center-marker"></cover-view>
    
    <cover-view class="info-box" :class="{ 'out-of-range': !isInServiceArea }">
      <cover-view v-if="isInServiceArea">
        当前位置在服务区域内
      </cover-view>
      <cover-view v-else class="error">
        当前选择位置超出服务区域
      </cover-view>
      
      <cover-view class="coords">
        纬度: {{ currentPos.latitude.toFixed(6) }} 
        经度: {{ currentPos.longitude.toFixed(6) }}
      </cover-view>
      
      <cover-view 
        v-if="!isInServiceArea" 
        class="recenter-btn" 
        @tap="centerToServiceArea"
      >
        查看最近的服务区域
      </cover-view>
      
      <cover-view 
        v-else 
        class="confirm-btn" 
        @tap="confirmLocation"
      >
        确定上车位置
      </cover-view>
    </cover-view>

    2.4 第四步:业务功能完善

    javascript 复制代码
    // 返回服务区中心
    const centerToServiceArea = () => {
      const center = getPolygonCenter(serviceAreaPolygon);
      currentPos.value = { ...center };
      isInServiceArea.value = true;
      
      mapContext.value.moveToLocation({
        latitude: center.latitude,
        longitude: center.longitude
      });
    };
    
    // 计算多边形中心点
    function getPolygonCenter(polygon) {
      let latSum = 0, lngSum = 0;
      polygon.forEach(point => {
        latSum += point.latitude;
        lngSum += point.longitude;
      });
      return {
        latitude: latSum / polygon.length,
        longitude: lngSum / polygon.length
      };
    }
    
    // 确认位置
    const confirmLocation = () => {
      uni.showToast({
        title: `位置已确认: ${currentPos.value.latitude.toFixed(6)}, ${currentPos.value.longitude.toFixed(6)}`,
        icon: "none"
      });
      // 实际业务中可以触发回调或跳转
    };

    三、完整实现代码

    javascript 复制代码
    <template>
      <view class="map-container">
        <map
          id="map"
          style="width: 100%; height: 80vh"
          :latitude="center.latitude"
          :longitude="center.longitude"
          :polygons="polygons"
          @regionchange="handleMapDrag"
          :show-location="true"
        >
          <cover-view class="center-marker"></cover-view>
          
          <cover-view class="info-box" :class="{ 'out-of-range': !isInServiceArea }">
            <cover-view v-if="isInServiceArea">当前位置在服务区域内</cover-view>
            <cover-view v-else class="error">当前选择位置超出服务区域</cover-view>
            
            <cover-view class="coords">
              纬度: {{ currentPos.latitude.toFixed(6) }} 
              经度: {{ currentPos.longitude.toFixed(6) }}
            </cover-view>
            
            <cover-view 
              v-if="!isInServiceArea" 
              class="recenter-btn" 
              @tap="centerToServiceArea"
            >
              查看最近的服务区域
            </cover-view>
            
            <cover-view 
              v-else 
              class="confirm-btn" 
              @tap="confirmLocation"
            >
              确定上车位置
            </cover-view>
          </cover-view>
        </map>
      </view>
    </template>
    
    <script setup>
    import { ref, onMounted } from "vue";
    
    // 服务区域边界
    const serviceAreaPolygon = [
      { latitude: 34.808, longitude: 113.55 },
      { latitude: 34.805, longitude: 113.58 },
      { latitude: 34.79, longitude: 113.61 },
      { latitude: 34.765, longitude: 113.625 },
      { latitude: 34.735, longitude: 113.62 },
      { latitude: 34.71, longitude: 113.6 },
      { latitude: 34.7, longitude: 113.57 },
      { latitude: 34.715, longitude: 113.54 },
      { latitude: 34.75, longitude: 113.53 },
      { latitude: 34.808, longitude: 113.55 }
    ];
    
    const center = ref(getPolygonCenter(serviceAreaPolygon));
    const currentPos = ref({ ...center.value });
    const isInServiceArea = ref(true);
    const mapContext = ref(null);
    
    const polygons = ref([{
      points: serviceAreaPolygon,
      strokeWidth: 2,
      strokeColor: "#1E90FF",
      fillColor: "#1E90FF22"
    }]);
    
    onMounted(() => {
      mapContext.value = uni.createMapContext("map");
    });
    
    const handleMapDrag = (e) => {
      if (e.type === "end") {
        mapContext.value.getCenterLocation({
          success: (res) => {
            currentPos.value = {
              latitude: res.latitude,
              longitude: res.longitude
            };
            isInServiceArea.value = isPointInPolygon(
              currentPos.value,
              serviceAreaPolygon
            );
          }
        });
      }
    };
    
    function isPointInPolygon(point, polygon) {
      const { latitude, longitude } = point;
      let inside = false;
      
      for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
        const xi = polygon[i].longitude, yi = polygon[i].latitude;
        const xj = polygon[j].longitude, yj = polygon[j].latitude;
        
        const intersect = ((yi > latitude) !== (yj > latitude))
          && (longitude < (xj - xi) * (latitude - yi) / (yj - yi) + xi);
        
        if (intersect) inside = !inside;
      }
      
      return inside;
    }
    
    function getPolygonCenter(polygon) {
      let latSum = 0, lngSum = 0;
      polygon.forEach(point => {
        latSum += point.latitude;
        lngSum += point.longitude;
      });
      return {
        latitude: latSum / polygon.length,
        longitude: lngSum / polygon.length
      };
    }
    
    const centerToServiceArea = () => {
      const center = getPolygonCenter(serviceAreaPolygon);
      currentPos.value = { ...center };
      isInServiceArea.value = true;
      
      mapContext.value.moveToLocation({
        latitude: center.latitude,
        longitude: center.longitude
      });
    };
    
    const confirmLocation = () => {
      uni.showToast({
        title: `位置已确认: ${currentPos.value.latitude.toFixed(6)}, ${currentPos.value.longitude.toFixed(6)}`,
        icon: "none"
      });
    };
    </script>
    
    <style scoped>
    .map-container {
      width: 100%;
      height: 100vh;
      position: relative;
    }
    
    .center-marker {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 20px;
      height: 20px;
      background-color: #5ca7fc;
      border-radius: 50%;
      border: 2px solid white;
      z-index: 999;
    }
    
    .info-box {
      position: absolute;
      top: 20%;
      left: 50%;
      transform: translateX(-50%);
      background: rgba(255, 255, 255, 0.9);
      padding: 12px 16px;
      border-radius: 8px;
      width: 80%;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }
    
    .info-box.out-of-range {
      background: rgba(255, 240, 240, 0.9);
    }
    
    .coords {
      font-size: 12px;
      color: #666;
      margin: 8px 0;
    }
    
    .error {
      color: #f56c6c;
      font-weight: bold;
    }
    
    .recenter-btn, .confirm-btn {
      margin-top: 10px;
      padding: 8px 12px;
      border-radius: 4px;
      text-align: center;
      font-size: 14px;
    }
    
    .recenter-btn {
      background: #606266;
      color: white;
    }
    
    .confirm-btn {
      background: #409eff;
      color: white;
    }
    </style>

    四、总结

    本文分步骤详细讲解了如何使用Uni-app实现地图位置选择功能,从基础配置到完整实现,重点介绍了:

  • 地图基础配置方法

  • 多边形区域绘制与判断

  • 交互逻辑的实现

  • 覆盖层UI的开发技巧

  • .moveToLocation移动api 只有在真机才能实现,微信开发者工具不支持

  • 可直接复制完整代码到单页测试运行,欢迎补充问题

五、实现效果

相关推荐
bug总结2 小时前
深入理解 uni-app 的 uni.createSelectorQuery()
uni-app
真上帝的左手2 小时前
25. 移动端-uni-app
uni-app
小徐_23335 小时前
Trae 辅助下的 uni-app 跨端小程序工程化开发实践分享
前端·uni-app·trae
斯~内克5 小时前
UniApp 页面传参方式详解
网络协议·udp·uni-app
大聪明了8 小时前
uniapp vue3 使用 pinia
javascript·vue.js·uni-app
有梦想的刺儿19 小时前
uniapp手机端video标签层级过高问题
uni-app
&白帝&21 小时前
Uniapp 获取系统信息:uni.getSystemInfo 与 uni.getSystemInfoSync
uni-app
熬耶1 天前
Uniapp之微信小程序自定义底部导航栏形态
微信小程序·小程序·uni-app
加蓓努力我先飞1 天前
小兔鲜儿-小程序uni-app(二)
uni-app