This commit is contained in:
cansnow
2025-12-17 08:47:58 +08:00
parent 916cb22ecc
commit cf1ad1c24b
68 changed files with 2423 additions and 6104 deletions
@@ -0,0 +1,662 @@
<template>
<view class="ly-map-wrapper">
<!-- 地图展示 -->
<view :id="mapId" :config="config" :change:config="LyMap.init" :call="option" :change:call="LyMap.call"
class="ly-map" />
<!-- 中心图标 -->
<image v-if="showCenterIcon" :src="centerIcon||defCenterIcon" class="ly-center-icon" />
<!-- 定位图标 -->
<view v-if="showLocationIcon" class="ly-location-icon" @click="onLocation">
<image :src="locationIcon" class="icon" />
</view>
</view>
</template>
<script>
import centerIcon from '../../static/ly-map/center.png';
import locationIcon from '../../static/ly-map/location.png';
export default {
// 接口参数
props: {
// 地图key
mapKey: {
type: String,
default: ''
},
// 经纬度
lonlat: {
type: Array,
default: () => ([111.668097, 40.825417]),
},
// 缩放
zoom: {
type: Number,
default: 16,
},
// 是否显示中心定位图标
showCenterIcon: {
type: Boolean,
default: false,
},
// 中心点图标
centerIcon: {
type: String,
default: "",
},
// 是否显示用户定位图标
showLocationIcon: {
type: Boolean,
default: false,
}
},
// 数据定义
data() {
return {
// 地图容器id
mapId: this.genId(),
// 调用渲染层
option: {},
// 地图配置(传递到render中的数据)
config: {},
// 调用渲染层队列
event: [],
// 事件处理定时器
timer: null,
// 中心图标
defCenterIcon: centerIcon,
// 定位图标
locationIcon: locationIcon,
}
},
// 挂载完成后
mounted() {
// 设置渲染数据
this.config = {
mapId: this.mapId,
mapKey: this.mapKey,
lonlat: this.lonlat,
zoom: this.zoom,
};
},
// 通用方法
methods: {
// 生成唯一ID
genId() {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < 30; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return Date.now() + result;
},
// 调用
call() {
if (this.timer) return;
// 消费事件队列(生产者/消费者机制)
this.timer = setInterval(() => {
if (this.event.length) {
this.option = this.event.shift();
} else {
clearInterval(this.timer);
this.timer = null;
}
}, 10);
},
// 添加事件队列
addEvent(name, data) {
// #ifdef APP-PLUS
// tips:由于采用监听option改变来调用方法,
// 如果连续变化两次option,渲染层只会则监听到最后一次
// 导致调用丢失,所以采用事件队列形式解决,稍微延时10ms
// 等待渲染进程监听到option变化,在进行更改option
// 从性能上,几乎无感可以放心使用
const option = {
id: this.genId(),
name: `_${name}`,
data
};
this.event.push(option);
this.call();
// #endif
// #ifndef APP-PLUS
this[`_${name}`] && this[`_${name}`](data);
// #endif
},
// 设置
setOption(option) {
this.addEvent("setOption", option);
},
// 设置类型 0矢量地图 1卫星地图
geoCoder(lng,lat,uuid) {
this.addEvent("geoCoder", {
lng,lat,uuid
});
},
// 设置位置
setCenter(lon, lat, zoom) {
this.addEvent("setCenter", {
lon,
lat,
zoom
});
},
// 设置类型 0矢量地图 1卫星地图
setType(type) {
this.addEvent("setType", {
type
});
},
// 设置标注 marks:[{url, width, height, lon, lat}]
setMarkers(markers = []) {
this.addEvent("setMarkers", {
markers
});
},
// 在原有的情况下添加覆盖物
addOvers(overs) {
this.addEvent("addOvers", overs);
},
// 清除原有同类型的覆盖物,并重新创建
setOvers(overs) {
this.addEvent("setOvers", overs);
},
// 移除指定属性的覆盖物
removeOvers(prop, value) {
this.addEvent("removeOvers", {
prop,
value
});
},
// 清除指定类型的覆盖物,不指定则清除所有
clearOvers(type = "") {
this.addEvent("clearOvers", type);
},
// 设置缩放
setZoom(zoom) {
this.addEvent("setZoom", {
zoom
});
},
// 设置是使能
setEnable(option) {
this.addEvent("setEnable", option);
},
// 重置地图
resize() {
this.addEvent("resize");
},
// 开始拖拽地图
UserEvent(data) {
this.$emit('onUserEvent',data);
}
},
}
</script>
<script module="LyMap" lang="renderjs">
// 天地图接口
const TDT_API = "https://api.tianditu.gov.cn/api?v=4.0&tk=";
export default {
data() {
return {
// 初始化配置数据
_config: {},
// 地图实例
_mapInstance: {},
// 注记
_markerList: [],
// 覆盖物
_overs: {},
// 地图事件
_event: {},
};
},
methods: {
// 初始化
init(newValue, oldValue, ownerInstance, instance) {
// 防止重复渲染
if (!Object.keys(newValue).length) {
return;
}
this._config = newValue;
// 初始化地图
if (typeof window.LyMap === 'function') {
this._createMap();
} else {
const script = document.createElement('script');
script.src = TDT_API + newValue.mapKey;
script.onload = this._createMap;
document.head.appendChild(script);
}
},
// 通过监听call来调用渲染层方法
call(newValue, oldValue, ownerInstance, instance) {
if (this[newValue.name] && typeof this[newValue.name] === "function") {
this[newValue.name](newValue.data);
}
},
// 创建地图
_createMap() {
// 创建地图实例
try {
this._mapInstance = new T.Map(this._config.mapId);
this._mapInstance.addEventListener('load', () => {
this.$ownerInstance.callMethod('UserEvent',{type:'onMapLoad'});
});
setTimeout(() => {
this._setCenter({
lon: this._config.lonlat[0],
lat: this._config.lonlat[1],
zoom: this._config.zoom
});
}, 100);
this._bindEvent();
} catch (err) {
console.error(err);
}
},
_geoCoder(lng,lat, uuid) {
//创建对象
const geocode = new T.Geocoder();
geocode.getLocation(new T.LngLat(lng,lat), (res)=>{
console.log(res.getStatus ());
console.log(res.getMsg ());
console.log(res.getAddress ());
console.log(res.getAddressComponent ());
console.log(res.getLocationLevel ());
});
return geocode;
},
// 绑定事件
_bindEvent() {
try {
// 绑定开始移动事件
this._event = {};
var fn = ({type})=>{
if(this._mapInstance==null)return;
const center = this._mapInstance.getCenter();
this.$ownerInstance.callMethod('UserEvent', {
type,
lng: center.getLng(),
lat: center.getLat()
});
}
//this._mapInstance.addEventListener('movestart', fn);
this._mapInstance.addEventListener('move', fn);
this._mapInstance.addEventListener('moveend', fn);
this._mapInstance.addEventListener('zoomstart', fn);
this._mapInstance.addEventListener('zoomend', fn);
this._mapInstance.addEventListener('dragstart', fn);
this._mapInstance.addEventListener('drag', fn);
this._mapInstance.addEventListener('dragend', fn);
this._mapInstance.addEventListener('touchstart', fn);
this._mapInstance.addEventListener('touchmove', fn);
this._mapInstance.addEventListener('touchend', fn);
this._mapInstance.addEventListener('longpress', fn);
} catch (err) {
console.error(err);
}
},
// 卸载事件
_unbindEvent() {
const eventsKey = Object.keys(this._event);
eventsKey.forEach(key => this._mapInstance.removeEventListener(key));
this._event = null;
},
// 重置地图尺寸
_resize() {
try {
this._mapInstance.checkResize && this._mapInstance.checkResize();
} catch (err) {
console.error(err);
}
},
// 设置选项
_setOption(data) {
data.type && this._setType(data);
data.center && this._setCenter({
...data.center,
zoom: data.zoom
});
if (!data.center && data.zoom) this._setZoom(data);
data.markers && this._setMarkers(data);
data.overs && this.setOvers(data.overs);
},
// 设置中心点
_setCenter(data) {
try {
this._resize();
this._mapInstance.panTo(new T.LngLat(Number(data.lon), Number(data.lat)), data.zoom || this._mapInstance.getZoom());
} catch (err) {
console.error(err);
}
},
// 设置地图显示类型
_setType(data) {
try {
this._mapInstance.setMapType(data.type === 1 ? TMAP_SATELLITE_MAP : TMAP_NORMAL_MAP);
} catch (err) {
console.error(err);
}
},
// 设置地图缩放
_setZoom(data) {
try {
this._mapInstance.setZoom(data.zoom);
} catch (err) {
console.error(err);
}
},
// 设置注记
_setMarkers(data) {
try {
// 清空当前所有注记
//if (!this._markerList) this._markerList = [];
//this._markerList.forEach(marker => this._mapInstance.removeOverLay(marker));
this._markerList = [];
this._mapInstance.clearOverLays();
// 创建新注记
data.markers && data.markers.forEach(item => {
console.log(item)
const option = {};
if (item.icon) {
option.icon = new T.Icon({
iconUrl: item.icon,
iconSize: new T.Point(item.width || 24, item.height || 24),
iconAnchor: new T.Point(item.offsetX || item.width / 2, item.offsetY ||
item.height)
});
}
const marker = new T.Marker(new T.LngLat(item.lon, item.lat), option);
item.click && marker.addEventListener("click", () => this._setEvent('click', item));
this._markerList.push(marker);
this._mapInstance.addOverLay(marker);
});
} catch (err) {
console.error(err);
}
},
// 设置使能
_setEnable(data) {
// 允许地图拖拽
if (data.hasOwnProperty("drag")) {
!!data["drag"] ? this._mapInstance.enableDrag() : this._mapInstance.disableDrag();
}
// 允许双击放大,默认启用。
if (data.hasOwnProperty("doubleClickZoom")) {
!!data["doubleClickZoom"] ? this._mapInstance.enableDoubleClickZoom() : this._mapInstance
.disableDoubleClickZoom();
}
// 允许地图惯性拖拽
if (data.hasOwnProperty("inertia")) {
!!data["inertia"] ? this._mapInstance.enableInertia() : this._mapInstance.disableInertia();
}
// 允许双指操作缩放
if (data.hasOwnProperty("pinchToZoom")) {
!!data["pinchToZoom"] ? this._mapInstance.enablePinchToZoom() : this._mapInstance.disablePinchToZoom();
}
},
// 添加覆盖物
_addOvers(data = []) {
if (!this._overs) this._overs = {};
data.forEach(item => {
switch (item.type) {
// 信息窗口
case "infowindow":
this._createOverInfoWindow(item);
break;
// 文本
case "label":
this._createOverLabel(item);
break;
// 创建圆
case "circle":
this._createOverCircle(item);
break;
// 创建折线
case "polyline":
this._createOverPolyLine(item);
break;
// 创建多边形
case "polygon":
this._createOverPolygon(item);
break;
// 创建矩形
case "rect":
this._createOverRectangle(item);
break;
// 创建带箭头的折线
case "polylineArrow":
// 创建不带箭头的三次样条曲线
case "cardinalCurve":
// 创建带箭头的三次样条曲线
case "cardinalCurveArrow":
this._createOverSymbol(item);
break;
default:
console.warn(
`'${item.type}'未知的覆盖物类型,有效值为 circle,polyline,polygon,rect,polylineArrow,cardinalCurve,cardinalCurveArrow`
);
}
});
},
// 设置覆盖物
_setOvers(data = []) {
if (!this._overs) this._overs = {};
// 移除现有同类型(时间复杂度On^2,不影响使用,暂时不优化了)
data.forEach(item => this._clearOvers(item.type));
// 添加
this._addOvers(data);
},
// 清除覆盖物
_clearOvers(type = "") {
if (!this._overs) this._overs = {};
// 清除
for (const id in this._overs) {
if (this._overs[id].options.type === type || !type) {
this._mapInstance.removeOverLay(this._overs[id]);
delete this._overs[id];
}
}
},
// 移除指定属性的覆盖物
_removeOvers(data = {}) {
if (!this._overs) this._overs = {};
for (const id in this._overs) {
if (this._overs[id].options[data.prop] === data.value) {
this._mapInstance.removeOverLay(this._overs[id]);
delete this._overs[id];
}
}
},
// 创建覆盖物
_createOver(over) {
// 添加到覆盖物层
this._mapInstance.addOverLay(over);
this._overs[this._genKey()] = over;
// 绑定点击事件
if (over.options.click) {
over.addEventListener("click", () => this._setEvent('click', over.options));
}
},
// 创建信息窗口
_createOverInfoWindow(option) {
try {
// 格式化参数
const param = {
...option
};
// 弹出窗口位置的补偿值。在同一图层中打开弹出窗口时对于控制锚点比较有用。
if (option.offsetX !== undefined || option.offsetY !== undefined) {
param.offset = new T.Point(option.offsetX || 0, option.offsetY || 0);
}
// 在地图视图自动平移产生后弹出窗口和地图视图之间的边缘。
if (option.autoPanPaddingX !== undefined || option.autoPanPaddingY !== undefined) {
param.autoPanPadding = new T.Point(option.autoPanPaddingX || 0, option.autoPanPaddingY || 0);
}
const infoWin = new T.InfoWindow(option.content, param);
// 设置或改变信息浮窗所指向的地理位置坐标
if (option.lon && option.lat) {
infoWin.setLngLat(new T.LngLat(option.lon, option.lat));
}
this._createOver(infoWin);
} catch (err) {
console.error(err);
}
},
// 创建文本覆盖物
_createOverLabel(option) {
try {
// 格式化参数
const param = {
...option
};
if (option.lon && option.lat) param.position = new T.LngLat(option.lon, option.lat);
param.offset = new T.Point(option.offsetX || 0, option.offsetY || 0);
const label = new T.Label(param);
this._createOver(label);
} catch (err) {
console.error(err);
}
},
// 创建圆覆盖物
_createOverCircle(option) {
try {
const circle = new T.Circle(new T.LngLat(option.lon, option.lat), option.rad, option);
this._createOver(circle);
} catch (err) {
console.error(err);
}
},
// 创建折线覆盖物
_createOverPolyLine(option) {
try {
const points = option.points.map(point => new T.LngLat(point[0], point[1]));
const line = new T.Polyline(points, option);
this._createOver(line);
} catch (err) {
console.error(err);
}
},
// 创建多边形覆盖物
_createOverPolygon(option) {
try {
const points = option.points.map(point => new T.LngLat(point[0], point[1]));
const polygon = new T.Polygon(points, option);
this._createOver(polygon);
} catch (err) {
console.error(err);
}
},
// 创建矩形覆盖物
_createOverRectangle(option) {
try {
const lt = option.leftTop;
const rb = option.rightBottom;
const bounds = new T.LngLatBounds(new T.LngLat(lt[0], lt[1]), new T.LngLat(rb[0], rb[1]));
const rect = new T.Rectangle(bounds, option);
this._createOver(rect);
} catch (err) {
console.error(err);
}
},
// 创建符号覆盖物
_createOverSymbol(option) {
try {
let over = null;
const points = option.points.map(point => new T.LngLat(point[0], point[1]));
switch (option.type) {
case "polylineArrow":
over = new T.PolylineArrow(points, option);
break;
case "cardinalCurve":
over = new T.CardinalCurve(points, option);
break;
case "cardinalCurveArrow":
over = new T.CardinalCurveArrow(points, option);
break;
default:
break;
}
if (over) this._createOver(over);
} catch (err) {
console.error(err);
}
},
// 设置事件
_setEvent(type, target) {
this.$ownerInstance.callMethod('UserEvent', {
type:'onMapListen',
type, // 事件类型UserEvent
target, // 事件目标
});
},
// 释放地图
_dispose() {
delete this._mapInstance;
this._mapInstance = null;
this._unbindEvent();
},
// 生成唯一ID
_genKey() {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < 30; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return Date.now() + result;
},
},
};
</script>
<style lang="scss" scoped>
.ly-map-wrapper {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: #f0f0f0;
.ly-map {
width: 100%;
height: 100%;
}
.ly-center-icon {
position: absolute;
z-index: 401;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-100%);
width: 64rpx;
height: 64rpx;
}
.ly-location-icon {
position: absolute;
z-index: 401;
right: 24rpx;
bottom: 24rpx;
width: 72rpx;
height: 72rpx;
background: #ffffff;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 8rpx rgba(0, 0, 0, .15);
.icon {
width: 44rpx;
height: 44rpx;
}
}
}
</style>