仿高德地图上拉【建议收藏小程序app适配】

仿高德地图上拉

在工作中遇到这样的一个需求感觉不知道画啥样子



前言

来吧看看效果

上拉哪个我是在uniapp插件库里面下载的 但是里面的有一些bug ,我对进行改造了一下
在这里插入图片描述
可以进行上拉
地图上头像也可以进行点击


一、组件?

<template>
	<view>
		<view :class="isend?'fixedbox2' :'fixedbox'"
			:style="{'height':windowHeight + 'px','width':windowWidth + 'px','top':fixboxtop +'px','border-top-left-radius':radius,'border-top-right-radius':radius}"
			@touchmove.stop="getstart($event)" @tap="tap" @touchend="getend" ref="fixbox">
			<view class="content" :style="{'height':windowHeight + 'px'}">
				<view class="tapBoxTouchLine" v-if="showLine">
					<view class="line"></view>
				</view>
				<slot />
			</view>
		</view>
	</view>
</template>

<script>
	/**
	 * tapBox 触摸上拉组件
	 * @description 触摸上拉组件,类似于高德美团上拉组件
	 * @property {String} radius 左上右上圆角
	 * @property {Number} minHeight 最低高度 占总屏幕高度占比
	 * @property {Number} minHeight 最高高度 占总屏幕高度占比
	 * @property {Number} touchHeight 允许滑动区域高度默认顶部横线高度 0为任意区域可滑动,单位rpx
	 * @property {Boolean} showLine 上否显示顶部滑动线
	 * @event {Function} tap 点击事件
	 * @event {Function} getstart 开始滑动时触发事件
	 * @event {Function} getend 滑动结束事件
	 */
	export default {
		name: "tapBox",
		data() {
			return {
				windowHeight: 0, // 屏幕高度
				windowWidth: 0, // 屏幕宽度
				firsttop: 0, // 默认高度
				fixboxtop: 0, // 实际高度
				phonetop: 200, // 默认滑动分界线 - 后面计算为最低与最高的一半
				isend: false, // 触摸结束
				isfirst: true, // 手指第一次触摸
				tapboxtop: 0, // 手指距离顶部距离
			};
		},

		props: {
			radius: {
				type: String,
				default: '50rpx'
			},
			minHeight: {
				type: Number,
				default: 0.2
			},
			maxHeight: {
				type: Number,
				default: 0.5
			},
			touchHeight: {
				type: Number,
				default: 0
			},
			showLine: {
				type: Boolean,
				default: true
			},
		},
		mounted() {
			this.$nextTick(function() {
				this.windowWidth = uni.getSystemInfoSync().windowWidth;
				this.windowHeight = uni.getSystemInfoSync().windowHeight;
				var defaultHeight = this.windowHeight * (1 - this.minHeight)
				this.firsttop = defaultHeight
				this.phonetop = (this.windowHeight * this.maxHeight - this.windowHeight * this.minHeight) /
					2
				this.fixboxtop = defaultHeight
				this.$emit('currentHeight', (this.windowHeight - this.fixboxtop))
				this.$emit('maxtHeight', (this.windowHeight * this.maxHeight))
			})
		},
		onReady() {},
		computed: {},
		methods: {
			tap(e) {
				// console.log(e)
			},
			getstart(e) {
				this.$emit('dragstart');
				var screenY;
				//#ifdef MP-WEIXIN
				screenY = e.touches[0].clientY;
				//#endif
				//#ifndef MP-WEIXIN
				screenY = e.touches[0].screenY;
				//#endif
				// console.log(screenY)
				// 这里特殊处理 解决:在滑动框内如果存在滚动元素,则会出现滑动时滑动框和内部滚动同时滑的问题
				if (this.touchHeight !== 0) {
					if (screenY - this.fixboxtop > this.touchHeight) {
						return false;
					}
				}
				this.isend = false
				if (this.isfirst) {
					this.isfirst = false
					this.tapboxtop = screenY - this.fixboxtop
				}
				this.fixboxtop = screenY - this.tapboxtop
				if (this.fixboxtop > this.firsttop) { // 往下滑 不允许
					this.fixboxtop = this.firsttop
				} else { // 往上滑
					if (this.fixboxtop <= this.windowHeight * (1 - this.maxHeight)) {
						this.fixboxtop = this.windowHeight * (1 - this.maxHeight)
					}
				}
				this.$emit('currentHeight', (this.windowHeight - this.fixboxtop))
			},
			getend() {
				this.$emit('dragend');
				this.isend = true
				this.isfirst = true
				if ((this.firsttop - this.fixboxtop) <= this.phonetop) {
					this.fixboxtop = this.firsttop
				} else {
					this.fixboxtop = this.windowHeight * (1 - this.maxHeight)
				}
				this.$emit('currentHeight', (this.windowHeight - this.fixboxtop))
			}
		},
	}
</script>

<style lang="scss" scoped>
	.tapBoxTouchLine {
		margin-top: 20rpx;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.line {
		margin: 0px;
		vertical-align: middle;
		border-bottom: 8rpx solid rgb(214, 215, 217);
		width: 60rpx;
		transform: scaleY(0.5);
		border-top-color: rgb(214, 215, 217);
		border-right-color: rgb(214, 215, 217);
		border-left-color: rgb(214, 215, 217);
	}

	// 1F1F1F
	.fixedbox {
		position: fixed;
		left: 0;
		background-color: #302F30;
		padding: 0 12px;
		z-index: 999999;
	}

	.fixedbox2 {
		position: fixed;
		left: 0;
		background-color: #302F30;
		padding: 0 12px;
		transition-property: top;
		transition-duration: .8s;
		z-index: 999999;
	}
</style>

二、使用步骤

1.父组件使用

代码如下(示例):

<template>
	<view style="width: 100%; height: 100%;">

		<view class='countDetail_body'>


			<!-- 这是地图 -->
			<view>
				<!-- 是否开启实时路况 enable-traffic='true'  	enable-3D='true'  	  是否开启卫星图  enable-rotate='true' 是否支持旋转  -->
				<map :enable-scroll="enableMapScroll" :markers="markers" scale='10' :enable-satellite='checkedWeixin'
					enable-building='true' :latitude="latitude" :longitude="longitude" id="map" ref='map'
					show-location='true' @callouttap='callouttap'
					:style="'height:'+windowHeight+'px;width:'+windowWidth+'px;'">
					<cover-view slot="callout">
						<block v-for="(item, index) in customCalloutMarkerIds" :key="index">
							<cover-view :marker-id="item" style="height: 188rpx">

								<cover-image class="icon" src="/static/onlineBg.png"
									:animation="animationData"></cover-image>
								<cover-image class="avtarClass"
									:src="markers[index].customCallout.avatar"></cover-image>
							</cover-view>
						</block>

					</cover-view>
				</map>
			</view>
		</view>

		<view class="floatBtn" :style="{bottom:(currentHeight+20)+'px'}">
			<view class="header-avatar">
				客服
			</view>
		</view>
		<bab-Touchbox :minHeight="0.3" :maxHeight="0.95" :touchHeight="64" @currentHeight="setScrollHeight"
			@maxtHeight="setScrollViewHeight" @dragstart="onDragStart" @dragend="onDragEnd">
			<view class="flexRow">

			</view>

			<scroll-view :style="{'height':scrollViewHeight - 64 +'px'}" scroll-y="true">
				<view>
					<view style="width: 100%;height: 20rpx;"></view>
					<list style="display: flex; flex-direction: row; flex-wrap: wrap;">
						<cell v-for="(item,index) in domesticpavilionList" :key="index"
							style="width: 50%; box-sizing: border-box; padding: 5px;">
							<view style="
			            background-color: #fff;
			            border-radius: 10px; 
			            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); 
			            overflow: hidden; 
			            padding: 10px;   
			            height: 164px; 
			          ">
								<text style="font-size: 12px; margin-left: 10px;">{{item.brandName}}</text>
								<view style="width: 100%;height: 10rpx;"></view>
								<u--image @click="onDetails(item)" width="100%" height="7rem" :src="item.brandLogoUrl"
									:fade="true" duration="450" radius="12" :lazy-load="true">
									<template v-slot:loading>
										<u-loading-icon></u-loading-icon>
									</template>
								</u--image>

							</view>
						</cell>
					</list>
				</view>


				<view v-if="loading" style="width: 100%; height: 4rem;">
					<u-loadmore loadmoreText="加载中..." color="#CCC" lineColor="#CCC" dashed line />
				</view>
				<view v-if="!hasMore && !loading && domesticpavilionList.length!=0" style="width: 100%; height: 4rem;">
					<u-loadmore loadmoreText="️哎呀,看的真快到底了哦~" color="#CCC" lineColor="#CCC" dashed line />
				</view>
			</scroll-view>
		</bab-Touchbox>

		<u-popup :show="show" :round="10" closeable @close="close" @open="open">
			<view style="padding: 17px 3px;margin: 1rem;">

			</view>
		</u-popup>

	</view>
</template>
<script>
	
	import {
		getToken,
		removeToken,
		setToken
	} from "@/utils/data";

	import {
		modifySysPersonalInfo,
		getMyself
	} from "@/api/sys/sys_userinfo_api.js";
	import {
		getBrandUserInfoList,
		getDomesticPavilionBrandPage
	} from '@/api/firm/firm_api.js';

	export default {
		data() {
			return {
				enableMapScroll: true,
				currentHeight: 0,
				scrollViewHeight: 0, //用于计算滚动区域高度
				windowWidth: 0,
				windowHeight: "",
				typeFlag: 0,
				checkedWeixin: true,
				avatarIndex: "",
				positionNameFlag: "",
				positionName: '',
				animationData: {},
				latitude: 22.852542, 
				longitude: 113.081452,
				markers: [],
				customCalloutMarkerIds: [],

				show: false,
				src: '',

				pageNum: 1,
				pageSize: 10,
				loading: false,
				hasMore: true,
				keyword: '',
				domesticpavilionList: []
			}
		},
		created() {


		},
		onLoad() {
			this.windowHeight = uni.getSystemInfoSync().windowHeight - 200;
			this.windowWidth = uni.getSystemInfoSync().windowWidth
			console.log(this.windowWidth)
		},
		mounted() {
			console.log("--| 调用 |--")	
			this.loadMore()
		},
		computed: {
			windowHeight() {
				var systemInfo = uni.getSystemInfoSync();
				return systemInfo.windowHeight;
			},
			windowWidth() {
				return uni.getSystemInfoSync().windowWidth;
			}
		},

		methods: {
			async onGetBrandUserInfoList() {
				const res = await getBrandUserInfoList()
				this.userList = res
			},
			setScrollHeight(val) { // 实时返回的滑动组件高度
				this.currentHeight = val
			},
			setScrollViewHeight(val) { //最大高度
				this.scrollViewHeight = val
			},

			open() {

			},
			close() {
				this.show = false
			},

			start(event) {
				this.startY = event.touches[0].clientY; // 记录触摸开始位置
			},
			end(event) {
				this.endY = event.changedTouches[0].clientY; // 记录触 摸结束位置
				this.checkSwipe(); // 检查滑动方向
			},
			checkSwipe() {
				if (this.startY - this.endY > 50) { // 向上滑动
					this.loadMore();
				}
			},

			async loadMore() {
				if (this.loading || !this.hasMore) return;
				this.loading = true;

				try {
					let requestData = {
						pageNum: this.pageNum,
						pageSize: this.pageSize
					};

					if (this.keyword !== '') {
						requestData.brandName = this.keyword;
					}
					const response = await getDomesticPavilionBrandPage(requestData);
					const {
						records,
						total
					} = response;
					console.log("拿到的数据");

					this.domesticpavilionList = [...this.domesticpavilionList, ...records];
					this.pageNum++;
					if (this.domesticpavilionList.length >= total) {
						this.hasMore = false;
					}
				} catch (error) {
					console.error('Error loading images:', error);
				} finally {
					this.loading = false;
				}
			},

			onDragStart() {
				this.enableMapScroll = false; // 禁止地图滚动
			},
			onDragEnd() {
				this.enableMapScroll = true; // 恢复地图滚动
			},
			onDetails(item) {
				this.isLoading = true;
				const itemStr = encodeURIComponent(JSON.stringify(item));
				setTimeout(() => {
					uni.navigateTo({
						url: `/pages/domesticpavilion/productlist/productlist?domesticItem=${itemStr}`,
						complete: () => {
							this.isLoading = false; // 跳转完成隐藏加载
						}
					});
				}, 50);
			}
		},
	}
</script>
<style scoped>
	.header-avatar {
		width: 50px;
		height: 50px;
		background-color: #302F30;
		border-radius: 100px;
		display: fixed;
		align-items: center;
		justify-content: center;
		color: #fff;
	}

	.header {
		width: 60%;
		position: fixed;
		top: 100rpx;
		left: 32rpx;
		right: 32rpx;
		z-index: 10;
		flex-direction: row;
		justify-content: space-between;
		flex-wrap: nowrap;
	}

	.floatBtn {
		position: fixed;
		right: 40rpx;
		z-index: 10;
	}

	.flexRow {
		background-color: #007AFF;
		display: flex;
		flex-direction: row;
		justify-content: flex-start;
	}

	.icon {
		width: 50px;
		height: 60px;
	}

	.avtarClass {
		width: 45px;
		height: 45px;
		position: absolute;
		top: 6rpx;
		left: 6rpx;
		border-radius: 50%;
	}

	.callout {
		position: relative;
	}
</style>

2.上面我把一些我的接口导入删了,可根据自己业务改


总结

感觉还是可以的。没有解决不了的问题。一起加油~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰哥力挽狂澜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值