基于Uniapp+vue3實現(xiàn)微信小程序地圖固定中心點范圍內(nèi)拖拽選擇位置功能及實現(xiàn)步驟
一、功能概述與實現(xiàn)步驟
1.1 功能需求
- 顯示地圖并固定中心點標記
- 繪制服務(wù)區(qū)域多邊形邊界
- 實時檢測拖拽后位置是否在服務(wù)區(qū)內(nèi)
- 提供位置確認和超出范圍提示功能
1.2 實現(xiàn)步驟分解
第一步:初始化地圖基礎(chǔ)配置
- 創(chuàng)建Map組件并設(shè)置基本屬性
- 定義服務(wù)區(qū)域多邊形坐標
- 設(shè)置地圖初始中心點
第二步:實現(xiàn)地圖交互邏輯
- 監(jiān)聽地圖拖拽事件
- 獲取拖拽后中心點坐標
- 判斷坐標是否在服務(wù)區(qū)內(nèi)
第三步:實現(xiàn)覆蓋層UI
固定中心點標記
位置信息顯示面板
操作按鈕(確認/返回服務(wù)區(qū))
二、分步驟代碼實現(xiàn)
2.1 第一步:地圖基礎(chǔ)配置
<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";
// 服務(wù)區(qū)域邊界坐標
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 第二步:地圖交互邏輯實現(xiàn)
// 當(dāng)前坐標點
const currentPos = ref({ ...center.value });
// 是否在服務(wù)區(qū)內(nèi)
const isInServiceArea = ref(true);
// 地圖拖拽事件處理
const handleMapDrag = (e) => {
if (e.type === "end") {
mapContext.value.getCenterLocation({
success: (res) => {
currentPos.value = {
latitude: res.latitude,
longitude: res.longitude
};
// 判斷是否在服務(wù)區(qū)內(nèi)
isInServiceArea.value = isPointInPolygon(
currentPos.value,
serviceAreaPolygon
);
}
});
}
};
// 射線法判斷點是否在多邊形內(nèi)
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實現(xiàn)
<!-- 在map標簽內(nèi)添加 -->
<cover-view class="center-marker"></cover-view>
<cover-view class="info-box" :class="{ 'out-of-range': !isInServiceArea }">
<cover-view v-if="isInServiceArea">
當(dāng)前位置在服務(wù)區(qū)域內(nèi)
</cover-view>
<cover-view v-else class="error">
當(dāng)前選擇位置超出服務(wù)區(qū)域
</cover-view>
<cover-view class="coords">
緯度: {{ currentPos.latitude.toFixed(6) }}
經(jīng)度: {{ currentPos.longitude.toFixed(6) }}
</cover-view>
<cover-view
v-if="!isInServiceArea"
class="recenter-btn"
@tap="centerToServiceArea"
>
查看最近的服務(wù)區(qū)域
</cover-view>
<cover-view
v-else
class="confirm-btn"
@tap="confirmLocation"
>
確定上車位置
</cover-view>
</cover-view>2.4 第四步:業(yè)務(wù)功能完善
// 返回服務(wù)區(qū)中心
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"
});
// 實際業(yè)務(wù)中可以觸發(fā)回調(diào)或跳轉(zhuǎn)
};三、完整實現(xiàn)代碼
<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">當(dāng)前位置在服務(wù)區(qū)域內(nèi)</cover-view>
<cover-view v-else class="error">當(dāng)前選擇位置超出服務(wù)區(qū)域</cover-view>
<cover-view class="coords">
緯度: {{ currentPos.latitude.toFixed(6) }}
經(jīng)度: {{ currentPos.longitude.toFixed(6) }}
</cover-view>
<cover-view
v-if="!isInServiceArea"
class="recenter-btn"
@tap="centerToServiceArea"
>
查看最近的服務(wù)區(qū)域
</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";
// 服務(wù)區(qū)域邊界
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>四、總結(jié)
本文分步驟詳細講解了如何使用Uni-app實現(xiàn)地圖位置選擇功能,從基礎(chǔ)配置到完整實現(xiàn),重點介紹了:
- 地圖基礎(chǔ)配置方法
- 多邊形區(qū)域繪制與判斷
- 交互邏輯的實現(xiàn)
- 覆蓋層UI的開發(fā)技巧
- .moveToLocation移動api 只有在真機才能實現(xiàn),微信開發(fā)者工具不支持
- 可直接復(fù)制完整代碼到單頁測試運行,歡迎補充問題
五、實現(xiàn)效果

到此這篇關(guān)于基于Uniapp+vue3實現(xiàn)微信小程序地圖固定中心點范圍內(nèi)拖拽選擇位置功能及實現(xiàn)步驟的文章就介紹到這了,更多相關(guān)uniapp vue拖拽選擇位置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3?keep-alive實現(xiàn)tab頁面緩存功能
如何在我們切換tab標簽的時候,緩存標簽最后操作的內(nèi)容,簡單來說就是每個標簽頁中設(shè)置的比如搜索條件及結(jié)果、分頁、新增、編輯等數(shù)據(jù)在切換回來的時候還能保持原樣,這篇文章介紹vue3?keep-alive實現(xiàn)tab頁面緩存功能,感興趣的朋友一起看看吧2023-04-04
vue在自定義組件上使用v-model和.sync的方法實例
自定義組件的v-model和.sync修飾符其實本質(zhì)上都是vue的語法糖,用于實現(xiàn)父子組件的"數(shù)據(jù)"雙向綁定,下面這篇文章主要給大家介紹了關(guān)于vue在自定義組件上使用v-model和.sync的相關(guān)資料,需要的朋友可以參考下2022-07-07
vue項目刷新當(dāng)前頁面的三種方式(重載當(dāng)前頁面數(shù)據(jù))
這篇文章主要介紹了vue項目刷新當(dāng)前頁面的三種方式(重載當(dāng)前頁面數(shù)據(jù)),本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-01-01

