仿高德地图上拉
在工作中遇到这样的一个需求感觉不知道画啥样子
前言
来吧看看效果
上拉哪个我是在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.上面我把一些我的接口导入删了,可根据自己业务改
总结
感觉还是可以的。没有解决不了的问题。一起加油~~~