屁话少说上效果!
具体就是根据 touch的 start、move、end 来实现 然后就是代码!
<template>
<transition name="van-slide-up">
<div
class="w-list-container"
v-show="!wrapIsHideAll"
:style="`pointer-events: ${wrapIsWait ? 'none' : 'auto'}`"
>
<!-- 背景块 点了也能关闭 -->
<div v-show="wrapIsShowAll" class="bg" @click="handleClickBg"></div>
<div class="weather-wrap" ref="listWrapperRef">
<div class="list-wrap">
<!-- 手拖块 -->
<div
class="touch-bar-box"
:style="`height: ${touchBarHeight}`"
@click.stop="() => {}"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
>
<!-- 自定义块 -->
<slot name="diy-touch"></slot>
</div>
<slot></slot>
</div>
</div>
</div>
</transition>
</template>
<script>
// vh
const TRIGGER_VALUE = 16; // 滑动的距离
export const SHOW_Y_VALUE = 17; // 显示
export const HIDE_Y_VALUE = 100; // 隐藏
export const WAIT_Y_VALUE = 85; // 等待[露出一部分
let initialed = false;
let endDistance = HIDE_Y_VALUE; // 当前 endDistance状态
const clientHeight = window.screen.height;
// 只可能 等待 或 显示 的时候能触发这个方法
function getNextStatusValue(v, isToUp) {
if (v === WAIT_Y_VALUE) {
return isToUp ? SHOW_Y_VALUE : HIDE_Y_VALUE;
}
// 只可能往下
return WAIT_Y_VALUE;
}
// 获得滑动时/后 的相关参数
function getParams(e, startY) {
let distanceVH = ((e.pageY - startY) / clientHeight) * 100;
const isToUp = distanceVH < 0; // 是否向上滑
let distanceAbsVH = Math.abs(distanceVH); // 移动的距离
const needSetEndDistance = distanceAbsVH >= TRIGGER_VALUE; // 是否要设置 endDistance,就是这次滑动是否算有效滑动
return {
distanceAbsVH,
needSetEndDistance,
isToUp
};
}
export default {
props: {
// 触摸条的高
touchBarHeight: {
type: String,
default: "118px"
}
},
data() {
name: 'WList',
return {
startY: 0,
wrapIsShowAll: false, // 展示了全部
wrapIsHideAll: false, // 隐藏了全部
wrapIsWait: true // 等待中
};
},
mounted() {
this.setTransform(endDistance);
},
methods: {
async setTransform(y, unit = "vh", cb) {
const isString = typeof y === "string";
this.$refs.listWrapperRef.style.transform = `translate3d(0, ${
isString ? y : y + unit
}, 0)`;
if (!isString) {
endDistance = y;
} else {
console.log("y是个string, 注意可能会有bug");
}
this.updateWrapStatus();
if (typeof cb === "function") {
await this.$nextTick();
cb();
}
},
// 给外部调用 ,其实就是把状态改成 等待中
show(v = WAIT_Y_VALUE) {
this.setTransform(v);
},
hide() {
this.setTransform(HIDE_Y_VALUE);
},
// 手动更新状态【因为自动的话,或者有依赖关系 感觉会卡!】
updateWrapStatus() {
this.wrapIsShowAll = endDistance === SHOW_Y_VALUE;
this.wrapIsHideAll = endDistance === HIDE_Y_VALUE;
this.wrapIsWait = endDistance === WAIT_Y_VALUE;
// 通知外层 隐藏了就true 另外两个显示都是 false
this.$emit("toggle", this.wrapIsHideAll);
if (this.wrapIsShowAll) {
this.$emit("show", this);
}
if (this.wrapIsWait) {
this.$emit("wait", this);
}
},
// 点了背景时设置为 等待中
handleClickBg() {
this.setTransform(WAIT_Y_VALUE);
},
setInitialed(v) {
initialed = v;
},
setStartY(v) {
this.startY = v;
},
handleTouchStart(e) {
// console.log('开始了新的点击!')
// if (!canTouch || initialed) return;
if (initialed) {
console.log("还没准备好");
return;
}
// 记录开始位置
this.setStartY(e.touches[0].pageY);
// 防止 handleTouchStart 重复触发
this.setInitialed(true);
},
handleTouchMove(e) {
// console.log('handleTouchMove', e)
// if (!canTouch || !initialed) return;
if (!initialed) {
console.log("还没准备好");
return;
}
// 实时计算滑动距离
let { distanceAbsVH, isToUp } = getParams(e.touches[0], this.startY);
if (endDistance === WAIT_Y_VALUE) {
// 等待的情况
if (isToUp) {
// 往上滑
// console.log('等待的情况 还向上滑', distanceAbsVH)
if (WAIT_Y_VALUE - distanceAbsVH < SHOW_Y_VALUE) {
// console.log('过了!过最小值了!', WAIT_Y_VALUE - distanceAbsVH, SHOW_Y_VALUE)
distanceAbsVH = SHOW_Y_VALUE;
} else {
// console.log('未过最大值', WAIT_Y_VALUE - distanceAbsVH, SHOW_Y_VALUE)
distanceAbsVH = WAIT_Y_VALUE - distanceAbsVH;
}
} else {
// 往下滑
// console.log('等待的情况 还向下滑', distanceAbsVH)
if (WAIT_Y_VALUE + distanceAbsVH > HIDE_Y_VALUE) {
// console.log('过了!过最大值了!', WAIT_Y_VALUE + distanceAbsVH, HIDE_Y_VALUE)
distanceAbsVH = HIDE_Y_VALUE;
} else {
// console.log('未过最大值', WAIT_Y_VALUE + distanceAbsVH, HIDE_Y_VALUE)
distanceAbsVH = WAIT_Y_VALUE + distanceAbsVH;
}
}
} else if (endDistance === SHOW_Y_VALUE) {
// 已全显示的情况
if (isToUp) {
// 往上滑
// console.log('已全显示的情况 还向上滑', distanceAbsVH)
distanceAbsVH = SHOW_Y_VALUE;
} else {
// 往下滑
distanceAbsVH =
SHOW_Y_VALUE + distanceAbsVH > WAIT_Y_VALUE
? WAIT_Y_VALUE
: SHOW_Y_VALUE + distanceAbsVH;
}
}
this.$refs.listWrapperRef.style.transform = `translate3d(0, ${distanceAbsVH}vh, 0)`; // 赋值
},
handleTouchEnd(e) {
let nextEndDistance;
let { needSetEndDistance, isToUp } = getParams(
e.changedTouches[0],
this.startY
);
console.log(
"最终 表示是不是一个有效的移动 needSetEndDistance",
needSetEndDistance
);
if (needSetEndDistance) {
console.log("如果有效 就基于现在的位置 去上or下 isToUp", isToUp);
nextEndDistance = getNextStatusValue(endDistance, isToUp);
} else {
nextEndDistance = endDistance;
}
console.log("松开后把 y 设置为", nextEndDistance);
// 停止滑动时的位置 单位vh
this.setTransform(nextEndDistance, undefined, () => {
// console.log('关闭禁用!')
this.setInitialed(false);
nextEndDistance = undefined;
});
}
}
};
</script>
<style lang="less">
.w-list-container {
width: 100vw;
height: 100vh;
position: fixed;
z-index: 100;
top: 0;
left: 0;
overflow: hidden;
.bg {
position: absolute;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
// opacity: 0;
background: transparent;
}
.weather-wrap {
user-select: none;
width: 90vw;
height: 100vh;
margin-left: 5vw;
background: transparent;
border: 8px;
transition: all 0.3s ease 0s;
position: fixed;
bottom: 0;
z-index: 3;
transform: translate3d(0, 100vh, 0);
}
.list-wrap {
user-select: none;
.touch-bar-box {
position: absolute;
pointer-events: auto;
top: 0;
z-index: 1;
width: 100%;
// background: #733acb;
// opacity: .6;
}
}
}
</style>
外部使用的话
<template>
<div class="weather-home-container" v-loading="isLoading">
<div id="map" v-loading="isLoading"></div>
<WList
ref="WListRef"
@toggle="$listeners['toggle-w-list']"
@show="handleShowWList"
@wait="handleWaitWList"
>
<WWeather ref="WWeatherRef" />
<template #diy-touch>
<div class="touch-block" @click="handleChangeTouchBlock"></div>
</template>
</WList>
</div>
</template>
WList是上面那个组件 WWeather是内容
有帮助到你的话 点个赞~谢谢🙏