uniapp地图组件map+nvue案例分享,包括地图标点、动态缩放、nvue开发注意事项等

一、概要说明&效果图

1、概要说明

需求:

1、实现地图显示标记点markers,点击标记点弹出详情框
2、缩放地图时,如果缩小到一定层级只显示地区的店铺数量,反之则显示店铺标记点。模拟点聚合效果
3、实现按钮功能:返回原位、放大地图、缩小地图
4、视野范围内没有店铺marker点时,顶部弹出提示

所用技术:

1、uniappmap地图组件
2、UI框架采用uview
3、由于对地图的自定义程度较高(弹出详情页、按钮等),所以采用nvue,解决map的组件层级问题

2、效果图(仅作demo学习使用):

1、进入页面的初始状态(左),和点击marker标记点弹出详情的效果(右)
请添加图片描述请添加图片描述

2、将地图缩小到一定层级的效果(左),当前视野内没有marker点时弹出提示的效果(右)
请添加图片描述在这里插入图片描述

二、nvue下iconfont的引入和使用方法,及nvue开发注意事项

1、iconfont引入和使用

script的顶部添加如下代码:

<script>
	// #ifdef APP-NVUE
	// nvue通过weex的dom模块引入字体,相关文档地址如下:
	// https://weex.apache.org/zh/docs/modules/dom.html#addrule
	const fontUrl = '/static/iconfont/iconfont.ttf'
	const domModule = weex.requireModule('dom')
	domModule.addRule('fontFace', {
		'fontFamily': "iconfont",
		'src': `url('${fontUrl}')`
	})
	// #endif
</script>

css中加入以下代码:

<style lang="scss" scoped>
	/* #ifndef APP-NVUE */
	// 非nvue下加载字体
	@font-face {
		font-family: 'iconfont';
		src: url('/static/iconfont/iconfont.ttf') format('truetype');
	}
	/* #endif */
	.nvue-iconfont{
		font-family: iconfont;
	}
</style>

DOM中使用如下:

<text class="nvue-iconfont">&#xe677;</text>

2、nvue开发注意事项

  1. nvue只支持类选择器,也就是说标签选择器、ID选择器都不适用,DOM里要写很多的class
  2. nvue不支持/deep/::v-deep等任何的组件穿透样式,只能通过组件传参的方式修改子组件样式
  3. nvue为flex布局,且默认flex-direction: column;
  4. nvue对文字的color和字体大小fontsize的修改,只在text标签下有效。所以要养成好习惯,涉及到文字的要用text标签。且text标签不能再嵌套view标签和text标签。
  5. 涉及到滚动要用<scroll-view>标签,overflow-y: scroll和auto都是不支持的
  6. 不能使用百分比vhcalcauto等宽高的计算样式,尽量使用pxrpx

三、在地图组件上标记点markers,点击弹出详情框;实现按钮:回到原位、放大、缩小

DOM代码:

<!-- 页面返回按钮 -->
<view @click="navToBack" class="nav-back" :style="{top: `${navTop}px`}">
	<text class="nvue-iconfont">&#xe669;</text>
</view>
<map
	:style="{
		height: windowHeight + 'px',
		top: '-' + statusBarHeight + 'px'
	}"
	id="map"
	class="map"
	:show-location="true"
	theme="satellite"
	:markers="markers"
	:scale="scale"
	:latitude="latitude"
	:longitude="longitude"
	:min-scale="minScale"
	@regionchange="mapRegionchange"
	@callouttap="markertap"
	@markertap="markertap">
</map>
<!-- 按钮-返回原位 -->
<view class="tool-btn" @click="toLocation">
	<text class="nvue-iconfont">&#xe677;</text>
</view>
<!-- 按钮-放大 -->
<view class="tool-btn scale-up-btn" @click="mapScaleUp">
	<text class="nvue-iconfont">&#xe67a;</text>
</view>
<!-- 按钮-缩小 -->
<view class="tool-btn scale-down-btn" @click="mapScaleDown">
	<text class="nvue-iconfont">&#xe679;</text>
</view>
<!-- 店铺详情 -->
<u-popup :show="shopPopupShow" @close="closePopup" :round="15">
	<view class="shop-detail-box">
		<view class="shop-detail-header">
			<image :src="shopData.logoUrl" class="shop-logo"></image>
			<view class="shop-header-right">
				<text class="shop-title">{{shopData.shopName}}</text>
				<view class="shop-sub-title">
					<view class="shop-rate">
						<u-rate v-model="shopData.mark" :size="13" :gutter="2" readonly></u-rate>
						<text class="rate-text">{{`${shopData.mark}分`}}</text>
					</view>
					<view class="shop-evaluate">
						<text class="evaluate-text">{{`${shopData.evaluateNum}人评`}}</text>
						<text class="nvue-iconfont icon-arrow-right">&#xe667;</text>
					</view>
				</view>
			</view>
		</view>
		<text class="hours-text">营业时间:{{shopData.businessHours}}</text>
		<view class="shop-addree-box" @click="openLocation">
			<text class="nvue-iconfont">&#xebbb;</text>
			<text class="address-text">{{shopData.address}}</text>
			<text class="nvue-iconfont icon-arrow-right">&#xe667;</text>
		</view>
	</view>
</u-popup>

data代码:

data() {
	return {
		// 计算高度属性
		windowHeight: 800,
		statusBarHeight: 30,
		navTop: 30,
		// 地图
		_mapContext: null,
		// 默认位置坐标
		latitude: 28.682976,
		longitude: 115.857972,
		scale: 12,
		minScale: 4.5,
		// 地图标点
		markers: [],
		// 店铺标记点数据
		shopMarkers: [
			{
				id: 0,
				latitude: 28.657005,
				longitude: 115.876998,
				iconPath: '/static/img/deptIcon.png',   
				width: 18,
				height: 23, 
				alpha: 1,   //透明度
				callout: {  //自定义标记点上方的气泡窗口 点击有效
					content: '西湖区分店1#',//文本
					color: '#ffffff',//文字颜色
					fontSize: 12,//文本大小
					borderRadius: 5,//边框圆角
					padding: 5,
					bgColor: '#3f94fd',//背景颜色
					display: 'ALWAYS',//常显
				},
			},
			{
				id: 1,
				latitude: 28.62182,
				longitude: 115.925709,
				iconPath: '/static/img/deptIcon.png',   
				width: 18,
				height: 23, 
				alpha: 1,   //透明度
				callout: {  //自定义标记点上方的气泡窗口 点击有效
					content: '青云谱分店2#',//文本
					color: '#ffffff',//文字颜色
					fontSize: 12,//文本大小
					borderRadius: 5,//边框圆角
					padding: 5,
					bgColor: '#3f94fd',//背景颜色
					display: 'ALWAYS',//常显
				},
			},
			{
				id: 2,
				latitude: 28.681356,
				longitude: 115.881141,
				iconPath: '/static/img/deptIcon.png',   
				width: 18,
				height: 23, 
				alpha: 1,   //透明度
				callout: {  //自定义标记点上方的气泡窗口 点击有效
					content: '滕王阁分店3#',//文本
					color: '#ffffff',//文字颜色
					fontSize: 12,//文本大小
					borderRadius: 5,//边框圆角
					padding: 5,
					bgColor: '#3f94fd',//背景颜色
					display: 'ALWAYS',//常显
				},
			},
			// 更多数据在此不展示了,这里只做样例demo
		],
		// 店铺详情
		shopPopupShow: false,
		shopId: null,
		shopData: {
			id: 2,
			logoUrl: '/static/img/logo.png',
			shopName: '滕王阁分店3#',
			address: '南昌市东湖区仿古街58号',
			latitude: 28.681356,
			longitude: 115.881141,
			telPhone: '0791-XXXXXXX',
			businessHours: '9:00-14:30, 16:30-22:00',
			mark: 4.7,
			evaluateNum: 1356,
			averageAmount: 21,
		},
	}
}

js代码:

created() {
	let that = this;
	uni.getSystemInfo({
		success(res) {
			// #ifdef H5
			that.windowHeight = res.windowHeight;
			// #endif
			// #ifndef H5
			that.windowHeight = res.windowHeight + res.statusBarHeight;
			// #endif
			that.statusBarHeight = res.statusBarHeight;
			that.navTop = res.statusBarHeight + 10;
		}
	})
	this.getLocation();
	this.getShopAllList();
},
onReady() {
    this._mapContext = uni.createMapContext("map", this);
},
methods: {
	// 返回按钮
	navToBack(){
		uni.navigateBack();
	},
	// 获取当前定位
	getLocation(){
		let that = this;
		uni.getLocation({
			type: 'gcj02',
			success(res){
				that.longitude = res.longitude;
				that.latitude = res.latitude;
			},
			fail:function(e){
				console.log("获取位置信息失败", e.errMsg);
			}
		})
	},
	// 获取所有店铺列表数据
	getShopAllList(){
		let that = this;
		// 模拟向后端获取数据
		setTimeout(()=>{
			this.markers = this.shopMarkers;
			this._mapContext.addMarkers({
				markers: this.markers,
				clear: true
			})
		}, 1000);
	},
	// 点击地图marker点
	markertap(e){
		this.shopId = e.detail.markerId;
		// 此处根据拿到的id向后端发送请求,获取详情数据
		this.shopPopupShow = true;
	},
	// 回到原位,恢复默认缩放
	toLocation(){
		this._mapContext.moveToLocation({
			longitude: this.longitude,
			latitude: this.latitude
		});
		this.updateMapScale(0, 12);
	},
	// 放大地图
	mapScaleUp(){
		this.updateMapScale(1);
	},
	// 缩小地图
	mapScaleDown(){
		this.updateMapScale(2);
	},
	//设置地图缩放等级
	updateMapScale(type, level){
		this._mapContext.getScale({
			success: res=>{
				this.scale = res.scale;
				this.$nextTick(()=>{
					// 指定缩放级别
					if(type == 0){
						this.scale = level
					}
					// 放大
					else if(type == 1 && this.scale<20){
						this.scale = this.scale+1>20 ? 20 : this.scale+1
					}
					// 缩小
					else if(type == 2 && this.scale>this.minScale){
						this.scale = this.scale-1<this.minScale ? this.minScale : this.scale-1
					}
				})
			}
		})
	},
}

css代码:

$boxShadow: 0 0 8px #ccc;
.nav-back {
    position: fixed;
    z-index: 9;
    left: 10px;
    padding: 6px;
    background-color: #fff;
    width: 38px;
    height: 38px;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
	box-shadow: $boxShadow;
	.nvue-iconfont{
		font-size: 20px;
		font-weight: bold;
	}
}
.map{
	width: 750rpx;
	position: relative;
	z-index: 0;
}
// 右侧按钮组
.tool-btn{
	position: fixed;
    right: 15px;
    bottom: 350px;
    background-color: #ffffff;
    border-radius: 8px;
    width: 38px;
    height: 38px;
    align-items: center;
    justify-content: center;
	box-shadow: $boxShadow;
	.nvue-iconfont{
		color: #222;
		font-size: 22px;
	}
	&.scale-up-btn{
		bottom: 300px;
		.nvue-iconfont{
			font-size: 17px;
		}
	}
	&.scale-down-btn{
		bottom: 250px;
		.nvue-iconfont{
			font-size: 17px;
		}
	}
}
// 店铺详情的样式这里就不放了,不是重点

四、不同缩放层级,地图展示的markers点不同,模拟点聚合的效果

1、增加一组地区的markers数据,记录地区的经纬度、当前地区的店铺数量
2、通过视野变化事件,实时判断当前地图的缩放层级,决定显示哪一组markers数据

data数据,增加以下属性:

scaleLevel: 0, //0-店铺级别;1-地区级别
// 地区标记点数据
cityMarkers: [
	{
		id: 'nc',
		latitude: 28.682976,
		longitude: 115.857972,
		iconPath: '/static/img/transparent.png', 
		width: 10,
		height: 10,
		alpha: 1,   //透明度
		callout: {  //自定义标记点上方的气泡窗口 点击有效
			content: '南昌(7)',//文本
			color: '#ffffff',//文字颜色
			fontSize: 13,//文本大小
			borderRadius: 5,//边框圆角
			padding: 10,
			bgColor: '#3f94fd',//背景颜色
			display: 'ALWAYS',//常显
		},
	},
	{
		id: 'fz',
		latitude: 26.074286,
		longitude: 119.296411,
		iconPath: '/static/img/transparent.png', 
		width: 10,
		height: 10,
		alpha: 1,   //透明度
		callout: {  //自定义标记点上方的气泡窗口 点击有效
			content: '福州(10)',//文本
			color: '#ffffff',//文字颜色
			fontSize: 13,//文本大小
			borderRadius: 5,//边框圆角
			padding: 10,
			bgColor: '#3f94fd',//背景颜色
			display: 'ALWAYS',//常显
		},
	},
	{
		id: 'xm',
		latitude: 24.479627,
		longitude: 118.08891,
		iconPath: '/static/img/transparent.png', 
		width: 10,
		height: 10,
		alpha: 1,   //透明度
		callout: {  //自定义标记点上方的气泡窗口 点击有效
			content: '厦门(12)',//文本
			color: '#ffffff',//文字颜色
			fontSize: 13,//文本大小
			borderRadius: 5,//边框圆角
			padding: 10,
			bgColor: '#3f94fd',//背景颜色
			display: 'ALWAYS',//常显
		},
	},
	// 更多数据在此不展示
]

js里,增加对视野变化事件的处理:

// 地图视野发生变化
mapRegionchange(e){
	let that = this;
	// 监听当前地图的缩放级别,根据不同缩放级别展示不同的marker数据点
	this._mapContext.getScale({
		success: res=>{
			// 缩放到地区级别
			if(res.scale<=8 && that.scaleLevel==0){
				that.markers = that.cityMarkers;
				that._mapContext.addMarkers({
					markers: that.markers,
					clear: true
				})
				that.scaleLevel = 1;
			}
			// 缩放到店铺级别
			else if(res.scale>8 && that.scaleLevel==1){
				that.markers = that.shopMarkers;
				that._mapContext.addMarkers({
					markers: that.markers,
					clear: true
				})
				that.scaleLevel = 0;
			}
		}
	})
},

修改点击marker的事件,当缩放级别为地区级时,点击地区,地图将会放大移动到当前地区:

// 点击地图marker点
markertap(e){
	let that = this;
	// 1、当前缩放为店铺级别,查看店铺详情
	if(this.scaleLevel == 0){
		this.shopId = e.detail.markerId;
		this.shopPopupShow = true;
	}
	// 2、当前缩放为地区级别,将地图中心移到当前地区,缩放放大
	else if(this.scaleLevel == 1){
		let city = this.cityMarkers.filter(item=>item.id==e.detail.markerId)[0];
		this._mapContext.moveToLocation({
			longitude: Number(city.longitude),
			latitude: Number(city.latitude),
			success(res){
				setTimeout(()=>{
					that.updateMapScale(0, 12);
				}, 500)
			}
		});
	}
},

五、视野范围内没有marker点时进行提示

地图里没有marker点的时候,应该给用户提示,不然光溜溜的不好看嘿嘿

DOM代码:

<!-- 提示 -->
<u-transition
	mode="fade-down"
	:show="notifyShow"
	:custom-style="notifyStyle"
	:style="{top: `${navTop}px`}"
>
	<text class="notify-text">可视范围没找到店铺,请移动或缩放地图</text>
</u-transition>

data代码增加以下属性:

// 提示相关属性
notifyShow: false,
notifyTimer: null,
notifyStyle: {
    position: "fixed",
    left: '60px',
    width: "300px",
    height: "38px",
    backgroundColor: "#ffffff",
    borderRadius: "8px",
	alignItems: "center",
	justifyContent: "center",
},

同样也是通过视野变化事件来判断的,修改上面的mapRegionchange方法

// 地图视野发生变化
mapRegionchange(e){
	let that = this;
	// 1、检查是否有marker点在视野范围内
	this._mapContext.getRegion({
		success: res=>{
			let northeast = res.northeast;
			let southwest = res.southwest;
			let flag = false;
			for(let i=0; i<that.markers.length; i++){
				let item = that.markers[i];
				if(item.latitude>southwest.latitude
				&& item.longitude>southwest.longitude
				&& item.latitude<northeast.latitude
				&& item.longitude<northeast.longitude){
					flag = true;
					break
				}
			}
			// 视野范围内没有marker,弹出提示框
			if(!flag){
				// 定时器初始化之前,要执行清除操作,
				that.clearTimer()
				that.notifyShow = true;
				this.notifyTimer = setTimeout(() => {
				    that.notifyShow = false;
					// 倒计时结束,清除定时器,隐藏toast组件
					that.clearTimer()
				}, 1500);
			}else{
				that.clearTimer();
			}
		}
	})
	// 2、监听当前地图的缩放级别,根据不同缩放级别展示不同的marker数据点
	// 同上面的代码
},
clearTimer() {
	this.notifyShow = false
	// 清除定时器
	clearTimeout(this.notifyTimer)
	this.notifyTimer = null
},

小结

OK,到此为止,就把实现目标需求的代码全部完成啦~

关于标记点的聚合,一开始想用joinCluster实现,但是它是根据点之间的距离进行聚合,没办法对于地区进行聚合,不是我所希望的效果。所以另辟蹊径,也不知道这么对不对。

第一次做map地图,以上纯为个人思路,如果有更好的实现方式欢迎大家一起讨论哈

在这里插入图片描述

<think>好的,我需要帮助用户解决在UniApp中实现地图标记点随缩放调整的问题。首先,用户提到了地图标记点的自适应调整,这通常涉及到根据地图缩放级别动态改变标记的大小或样式。结合用户提供的引用内容,特别是引用[3]中的代码示例,里面有使用circles和scale属性,这可能与动态调整有关。 首先,我应该回顾UniApp地图组件文档,确认有哪些属性和事件可用。地图组件通常有scale属性控制缩放级别,而markers数组定义标记点。用户希望标记点随缩放调整,可能需要监听地图缩放变化事件,然后更新markers的样式,比如图标大小。 引用[2]提到微信小程序的map组件使用scale参数,并且可以通过regionchange事件监听地图区域变化。在UniApp中,类似的事件可能是regionchange,当用户缩放或拖动地图时触发。所以需要在页面中监听这个事件,并在回调函数中获取当前的缩放级别。 接下来,如何根据缩放级别调整标记点的大小?每个标记的iconPath可以设置不同的图标,或者调整iconSize。例如,当scale较大时(即放得较大),可能需要显示更大的图标,反之则缩小。因此,需要动态计算每个标记的iconSize,可能根据scale的值进行比例调整。 引用[3]中的示例使用了circles的radius,这可能与圆形覆盖物有关,但用户的问题是关于标记点(markers)。不过,其处理缩放的方式可能有参考价值,比如通过setData更新circles的radius。类似地,可以更新markers的iconSize。 可能的实现步骤包括: 1. 在地图组件中绑定regionchange事件。 2. 在事件处理函数中获取当前的scale值。 3. 根据scale计算需要的图标大小。 4. 更新markers数组中的iconSize属性,并触发视图更新。 需要注意的是,频繁更新markers可能会导致性能问题,所以可能需要适当的节流处理,或者只在缩放结束时触发更新(根据regionchange的type判断是begin还是end)。 另外,引用[1]提到视野自适应的实现,这可能包括计算所有标记点的范围并调整scale和中心点。虽然用户的问题不是视野自适应,但相关的坐标计算可能有帮助,比如确保在缩放时标记点保持在合适的位置。 最后,可能需要测试不同的缩放级别对图标大小的影响,调整计算方式,使视觉效果更自然。同时,确保在不同平台(如微信小程序)上的兼容性,因为UniApp需要编译到不同平台,某些API或行为可能有所差异。</think>在UniApp中实现地图标记点随缩放动态调整,需结合地图组件的事件监听与数据响应机制。以下是具体实现步骤: --- ### 一、基础实现原理 1. **监听地图缩放事件** 使用`@regionchange`事件捕获地图视野变化,通过回调参数判断是否为缩放操作[^3]。 2. **动态计算标记点尺寸** 根据当前缩放级别(`scale`值)按比例调整标记点图标大小(`iconSize`)。 3. **响应式更新数据** 修改`markers`数组中的`iconSize`属性,触发视图重新渲染。 --- ### 二、代码实现示例 ```html <template> <map id="mapId" :markers="markers" :scale="currentScale" @regionchange="handleRegionChange" show-scale ></map> </template> <script> export default { data() { return { currentScale: 16, // 初始缩放级别 originalIconSize: 20, // 基准图标大小(scale=16时的尺寸) markers: [{ id: 1, latitude: 39.909, longitude: 116.39742, iconPath: '/static/marker.png', iconSize: [20, 20] // 初始大小 }] }; }, methods: { handleRegionChange(e) { if (e.type === 'end') { // 仅处理缩放结束事件 const mapContext = uni.createMapContext('mapId', this); mapContext.getScale({ success: (res) => { this.currentScale = res.scale; this.updateMarkersSize(); } }); } }, updateMarkersSize() { // 根据缩放级别计算新尺寸(示例公式,可调整) const newSize = this.originalIconSize * (this.currentScale / 16); this.markers = this.markers.map(marker => ({ ...marker, iconSize: [newSize, newSize] })); } } }; </script> ``` --- ### 三、关键优化点 1. **节流控制** 在`handleRegionChange`中添加防抖/节流逻辑,避免频繁触发更新: ```javascript import { throttle } from 'lodash'; methods: { handleRegionChange: throttle(function(e) { /* ... */ }, 500) } ``` 2. **非线性缩放公式** 使用对数函数优化尺寸计算,避免极端缩放级别下图标过大/过小: ```javascript const newSize = this.originalIconSize * Math.log2(this.currentScale + 1); ``` 3. **多级图标策略** 预定义不同缩放级别对应的图标(更精细控制): ```javascript const iconLevels = [ { scale: 10, size: 10 }, { scale: 15, size: 20 }, { scale: 20, size: 30 } ]; ``` --- ### 四、注意事项 1. **平台差异** iOS与Android对`scale`值的解释可能不同,需真机测试校准公式参数[^2]。 2. **性能优化** 标记点数量较多时,建议使用`wx.createSelectorQuery`替代`createMapContext`获取缩放级别。 3. **视觉连续性** 添加CSS过渡效果使图标大小变化更平滑: ```css map { transition: transform 0.3s ease; } ``` ---
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值