172 lines
3.5 KiB
Vue
172 lines
3.5 KiB
Vue
<template>
|
||
<view>
|
||
<view :class="['drawer', { 'drawer-open': isVisible }]" :style="drawerStyle">
|
||
<view class="drawer-content" @touchstart.passive="onTouchStart" @touchmove.passive="onTouchMove"
|
||
@touchend="onTouchEnd" @touchcancel="onTouchEnd">
|
||
<!-- 抽屉中间的半圆 -->
|
||
<view class="drawer-content-circle" :style="isVisible?{}:{background:`linear-gradient(to bottom,#62E8FF,#0097FF)`}" @click="whiteDrawer">
|
||
<image class="drawer-img" :src="isVisible?'/static/index/watch/whitearrow.png':'/static/index/watch/arrow.png' " />
|
||
</view>
|
||
<slot />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
defineProps,
|
||
computed,
|
||
onMounted
|
||
} from 'vue'
|
||
|
||
// 控制抽屉显示隐藏
|
||
const isVisible = ref(false)
|
||
|
||
// 接收父组件传入的宽度百分比
|
||
const props = defineProps({
|
||
widNumber: {
|
||
type: Number,
|
||
default: 26
|
||
}
|
||
})
|
||
|
||
// 获取屏幕宽度,用于拖拽计算
|
||
const screenWidth = ref(0)
|
||
onMounted(() => {
|
||
const sys = uni.getSystemInfoSync()
|
||
screenWidth.value = sys.screenWidth
|
||
})
|
||
|
||
// 拖拽状态
|
||
const startX = ref(0)
|
||
const dragging = ref(false)
|
||
const currentOffset = ref(0)
|
||
|
||
// 计算样式:宽度 + 拖拽或开关 transform
|
||
const drawerStyle = computed(() => {
|
||
const widthPct = `${props.widNumber}%`
|
||
if (dragging.value) {
|
||
// 拖动时,X 方向正值向右移动抽屉
|
||
const offset = currentOffset.value
|
||
return {
|
||
width: widthPct,
|
||
transform: `translateX(${offset}px)`,
|
||
transition: 'none'
|
||
}
|
||
} else {
|
||
// 非拖动交由 class 控制开关状态
|
||
return {
|
||
width: widthPct
|
||
}
|
||
}
|
||
})
|
||
|
||
// 打开/关闭
|
||
function openDrawer() {
|
||
isVisible.value = true
|
||
}
|
||
|
||
function closeDrawer() {
|
||
isVisible.value = false
|
||
}
|
||
|
||
function whiteDrawer() {
|
||
// 点击半圆:切换 & 旋转
|
||
isVisible.value = !isVisible.value
|
||
rotate180()
|
||
}
|
||
|
||
defineExpose({
|
||
openDrawer,
|
||
closeDrawer
|
||
})
|
||
|
||
// 拖拽事件
|
||
function onTouchStart(e) {
|
||
if (!isVisible.value) return
|
||
dragging.value = true
|
||
currentOffset.value = 0
|
||
startX.value = e.touches[0].pageX
|
||
}
|
||
|
||
function onTouchMove(e) {
|
||
if (!dragging.value) return
|
||
const delta = e.touches[0].pageX - startX.value
|
||
currentOffset.value = delta > 0 ? delta : 0
|
||
}
|
||
|
||
function onTouchEnd() {
|
||
if (!dragging.value) return
|
||
dragging.value = false
|
||
const halfPx = screenWidth.value * (props.widNumber / 100) / 2
|
||
if (currentOffset.value > halfPx) {
|
||
closeDrawer()
|
||
rotate180()
|
||
}
|
||
currentOffset.value = 0
|
||
}
|
||
|
||
// 半圆旋转
|
||
const angle = ref(0)
|
||
const boxStyle = computed(() => ({
|
||
transform: `rotate(${angle.value}deg)`,
|
||
transition: 'transform 0.6s ease'
|
||
}))
|
||
|
||
function rotate180() {
|
||
angle.value += 180
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.drawer {
|
||
position: fixed;
|
||
bottom: 0;
|
||
right: 0;
|
||
height: 85vh;
|
||
background: #eff0f4;
|
||
z-index: 1000;
|
||
border-top-left-radius: 80rpx;
|
||
border-bottom-left-radius: 80rpx;
|
||
/* 初始隐藏 */
|
||
transform: translateX(100%);
|
||
transition: transform 0.4s ease;
|
||
}
|
||
|
||
.drawer-open {
|
||
transform: translateX(0);
|
||
}
|
||
|
||
.drawer-content {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.drawer-content-circle {
|
||
position: absolute;
|
||
bottom: 270rpx;
|
||
left: -60rpx;
|
||
width: 150rpx;
|
||
height: 160rpx;
|
||
border-radius: 50%;
|
||
z-index: -1;
|
||
// background: #fff;
|
||
background: linear-gradient(to right,
|
||
#fff 0rpx,
|
||
#eff0f4 60rpx,
|
||
#eff0f4 100%);
|
||
display: flex;
|
||
align-items: center;
|
||
clip-path: inset(0 60% 0 0);
|
||
}
|
||
|
||
.drawer-img {
|
||
width: 20rpx;
|
||
height: 20rpx;
|
||
margin-left: 25rpx;
|
||
transform: rotate(180deg);
|
||
}
|
||
</style> |