Vue3 Vite H5 拖拽条组件,可下拉收起/关闭,上拉展开

同样是懒人想找现成的但没找到,参考其他朋友提供的思路现写一个。
有小bug,等领导让改的时候再说(。)

效果

代码

简单的demo。

  1. DragBar.vue
<template>
    <div
        class="popup__top"
        @touchstart="onTouchstart"
        @touchmove="onTouchmove"
        @touchend="onTouchend"
        >
        <div class="popup__top-bar"></div>
    </div>
</template>

<script setup>
const emit = defineEmits(["action", "update:modelValue"]);
const props = defineProps({
    modelValue: { // 父组件高度
        type: Number,
        required: false,
        default: 100,
    },
    parentClass: { // 父组件类名
        type: String,
        required: true,
    }
});

// 追踪高度变化
const startY = ref(0); // 记录拖拽的起点位置
const startH = ref(props.modelValue); // 当前状态下的稳定高度
const updateStartH = () => {
    nextTick(() => {
        startH.value = document.getElementsByClassName(props.parentClass)[0].scrollHeight; // 拿到css设置的稳定高度
        emit("update:modelValue", startH.value);
    })
}
onMounted(()=>{
    updateStartH();
})
watch(
    () => {
        return {...props.parentClass};
    }, 
    (newVal, oldVal) => {
        updateStartH();
    }
)

// 监听拖拽事件
const onTouchstart = (e) => {
  startY.value = e.touches[0].clientY;
}
const onTouchmove = (e) => {
    const moveY = e.touches[0].clientY;
    const dif = moveY - startY.value
    if(props.modelValue) {
        emit("update:modelValue", startH.value - dif); // 拖拽过程中动态改变父组件面板高度
    }
}
const onTouchend = (e) => {
    const moveY = e.changedTouches[0].clientY;
    emit("update:modelValue", startH.value); // 拖拽结束,先恢复稳定高度
    if (moveY - startY.value >= 50) { // 向父组件传递down或up的动作。判断阈值50可自由修改
        emit("action", "down");
    } else if (moveY - startY.value <= -50) {
        emit("action", "up");
    }
}
</script>

<style lang="scss" scoped>
.popup {
  &__top {
    padding: 0 28px;
  }
  &__top-bar {
    width: 90px;
    height: 4px;
    background: #b1cef0;
    margin: 20px 0 15px 0;
    border-radius: 2px;
  }
}
</style>
  1. 父组件DragPanel.vue
<template>
    <div v-show="visible" :class="['drag-panel', isHigh ? 'high':'']" :style="{ height: height + 'px' }">
            <!-- 拖拽条 -->
            <drag-bar v-model="height" :parent-class="className" @action="dragAction"></drag-bar>
            <!-- 内容 -->
            <div class="drag-content">
                <van-search v-model="keyword" placeholder="请输入关键字搜索" shape="round" left-icon=""  @search="onSearch">
                    <template #action>
                        <div @click="onSearch">搜索</div>
                    </template>
                </van-search>
            </div>
            <!-- 额外展示的列表(展开/收起) -->
            <div class="drag-list" v-show="isHigh">
                <ul>
                    <li class="drag-list-item" v-for="(item, index) in list" :key="index">
                        <div class="drag-list-item-text">{{ item.name }}</div>
                    </li>
                </ul>
            </div>
    </div>
</template>

<script setup>
import DragBar from "./DragBar.vue";

// DragBar需要的参数
const height = ref();
const className = ref("drag-panel");

// 面板是否可见
const visible = ref(true); // 标记可见/隐藏状态
const handleVisible = (flag) => {
    if(flag !== undefined) {
        visible.value = flag;
        return;
    }
    visible.value = !visible.value;
}

// 面板是否展开
const isHigh = ref(false); // 标记展开/收起状态
const list = ref([
    {name: "结果1"},
    {name: "结果2"},
    {name: "结果3"},
    {name: "结果4"},
]);
const handleIsHigh = (flag) => {
    if(flag){
        isHigh.value = true;
        className.value = "drag-panel high";
    } else {
        isHigh.value = false;
        height.value = "";
        className.value = "drag-panel";
    }
}

// 搜索事件
const keyword = ref("");
const onSearch = async () => {
    if(!keyword.value) return;
    handleIsHigh(true);
}

// 拖拽事件
const dragAction = (actionName) => {
    if(isHigh.value && actionName === "down") {
        handleIsHigh(false);
    }else if(!isHigh.value && actionName === "up") {
        handleIsHigh(true);
    }else if(!isHigh.value && actionName === "down") {
        handleVisible(false);
    }
}
</script>

<style lang="scss" scoped>
.drag-panel {
    position: absolute;
    bottom: 0;
    height: 150px; // 必须设置
    &.high {
        height: 550px; // 必须设置
    }
}

</style>

可以结合vant的popup使用,理解起来可能稍微复杂一点。

        <van-popup
            ref="popupRef"
            v-model:show="visible"
            position="bottom"
            :overlay="false"
            :style="{ height: height + 'px' }"
            :class="['popup', isHigh ? 'high' : '']"
        >
            <!-- 拖拽条 -->
            <drag-bar v-model="height" :parent-class="className" @action="dragAction"></drag-bar>
            <!-- 内容 -->
            <div class="popup-content">
                ...
            </div>
        </van-popup>
posted @ 2023-04-18 11:09  宇宙野牛  阅读(371)  评论(0编辑  收藏  举报