> 技术文档 > uniapp 仿美团外卖详情页滑动面板组件[可自定义内容、自定义高度]

uniapp 仿美团外卖详情页滑动面板组件[可自定义内容、自定义高度]

示例代码:

               export default { name: \'BottomSlidePanel\', props: { // 是否显示面板 visible: { type: Boolean, default: false }, // 面板高度(px) height: { type: Number, default: 400 }, // 最小显示高度(收起时显示的高度) minHeight: { type: Number, default: 60 }, // 是否显示遮罩 showMask: { type: Boolean, default: true }, // 点击遮罩是否关闭 maskClosable: { type: Boolean, default: true }, // 初始是否展开 defaultExpanded: { type: Boolean, default: true } }, data() { return { isShow: false, isExpanded: false, showOverlay: false, panelHeight: 0, startY: 0, currentY: 0, isDragging: false, startTime: 0 } }, watch: { visible: { handler(newVal) { if (newVal) {  this.show() } else {  this.hide() } }, immediate: true } }, mounted() { this.panelHeight = this.height this.isExpanded = this.defaultExpanded }, methods: { show() { this.isShow = true this.$nextTick(() => { setTimeout(() => {  if (this.defaultExpanded) { this.expand()  } else { this.collapse()  } }, 50) }) }, hide() { this.isShow = false this.showOverlay = false this.isExpanded = false }, expand() { this.isExpanded = true this.showOverlay = this.showMask this.$emit(\'expand\') this.$emit(\'change\', { expanded: true }) }, collapse() { this.isExpanded = false this.showOverlay = false this.$emit(\'collapse\') this.$emit(\'change\', { expanded: false }) }, toggle() { if (this.isExpanded) { this.collapse() } else { this.expand() } }, handleOverlayTap() { if (this.maskClosable) { this.hide() this.$emit(\'update:visible\', false) } }, onTouchStart(e) { this.isDragging = true this.startY = e.touches[0].clientY this.currentY = this.startY this.startTime = Date.now() }, onTouchMove(e) { if (!this.isDragging) return e.preventDefault() this.currentY = e.touches[0].clientY const deltaY = this.currentY - this.startY // 根据滑动方向和当前状态判断是否允许滑动 if (this.isExpanded && deltaY > 0) { // 展开状态下向下滑动,允许收起 return } else if (!this.isExpanded && deltaY  velocityThreshold || Math.abs(deltaY) > threshold) { if (deltaY > 0 && this.isExpanded) {  // 向下滑动且当前展开,收起面板  this.collapse() } else if (deltaY < 0 && !this.isExpanded) {  // 向上滑动且当前收起,展开面板  this.expand() } } }, getPanelTransform() { if (!this.isShow) { return \'translateY(100%)\' } else if (this.isExpanded) { return \'translateY(0)\' } else { return `translateY(calc(100% - ${this.minHeight}px))` } } }}.bottom-slide-panel { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; pointer-events: none;}.overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; // background-color: rgba(0, 0, 0, 0); transition: background-color 0.3s ease; pointer-events: none; &.overlay-visible { // background-color: rgba(0, 0, 0, 0.4); pointer-events: auto; }}.panel { position: absolute; left: 0; right: 0; bottom: 0; background-color: #ffffff; border-radius: 32rpx 32rpx 0 0; box-shadow: 0 -4rpx 32rpx rgba(0, 0, 0, 0.1); transform: translateY(100%); transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); pointer-events: auto; display: flex; flex-direction: column;}.drag-handle { display: flex; justify-content: center; align-items: center; height: 60rpx; padding: 16rpx 0; cursor: pointer; flex-shrink: 0;}.drag-bar { width: 80rpx; height: 6rpx; background-color: #e5e5e5; border-radius: 3rpx; transition: background-color 0.2s ease;}.drag-handle:active .drag-bar { background-color: #d0d0d0;}.panel-content { flex: 1; padding: 0 32rpx 32rpx; -webkit-overflow-scrolling: touch;}

使用示例:

                附近推荐    📍  星巴克咖啡 距离您 200m · 营业中        更多推荐    推荐地点 {{ item }}  {{ item * 100 }}m      import BottomSlidePanel from \'@/components/BottomSlidePanel.vue\'export default { components: { BottomSlidePanel }, data() { return { panelVisible: false } }, methods: { showPanel() { this.panelVisible = true }, hidePanel() { this.panelVisible = false }, togglePanel() { this.panelVisible = !this.panelVisible }, onPanelExpand() { console.log(\'面板展开\') }, onPanelCollapse() { console.log(\'面板收起\') }, onPanelChange(e) { console.log(\'面板状态改变:\', e.expanded) } }}.demo-page { height: 100vh; position: relative;}.map-container { width: 100%; height: 100%; position: relative; background-color: #f0f0f0;}.map-image { width: 100%; height: 100%;}.control-buttons { position: absolute; top: 100rpx; left: 32rpx; right: 32rpx; display: flex; gap: 20rpx;}.btn { flex: 1; height: 80rpx; background-color: #007aff; color: white; border: none; border-radius: 8rpx; font-size: 28rpx;}.panel-header { padding: 0 0 32rpx 0; border-bottom: 1rpx solid #f0f0f0;}.title { font-size: 36rpx; font-weight: 600; color: #333;}.content-section { padding-top: 32rpx;}.info-card { display: flex; align-items: center; padding: 24rpx; background-color: #fff7e6; border-radius: 12rpx; border: 1rpx solid #ffd591; margin-bottom: 32rpx;}.card-icon { font-size: 48rpx; margin-right: 24rpx;}.card-content { flex: 1;}.card-title { display: block; font-size: 32rpx; font-weight: 600; color: #333; margin-bottom: 8rpx;}.card-desc { font-size: 26rpx; color: #666;}.action-buttons { display: flex; gap: 24rpx; margin-bottom: 48rpx;}.action-btn { flex: 1; height: 88rpx; border-radius: 12rpx; font-size: 32rpx; border: none; &.primary { background-color: #ff6b35; color: white; } &.secondary { background-color: #f5f5f5; color: #333; }}.more-content { margin-top: 32rpx;}.section-title { display: block; font-size: 32rpx; font-weight: 600; color: #333; margin-bottom: 24rpx;}.item-list { display: flex; flex-direction: column; gap: 24rpx;}.list-item { display: flex; justify-content: space-between; align-items: center; padding: 24rpx 0; border-bottom: 1rpx solid #f0f0f0;}.item-name { font-size: 30rpx; color: #333;}.item-distance { font-size: 26rpx; color: #999;}

效果展示: