douyin/src/components/dialog/FromBottomDialog.vue
2024-04-02 17:24:18 +08:00

264 lines
6.2 KiB
Vue

<template>
<!-- <transition name="from-bottom"> -->
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
:css="false"
>
<div
ref="dialog"
class="FromBottomDialog"
v-if="modelValue"
:class="[mode, showHengGang ? '' : 'no-heng-gang']"
:style="{ 'max-height': height }"
@touchstart="start"
@touchmove="move"
@touchend="end"
>
<slot name="header"></slot>
<div class="heng-gang" :class="mode" v-if="showHengGang">
<div class="content"></div>
</div>
<slot></slot>
</div>
</transition>
</template>
<script>
import Dom from '../../utils/dom'
import bus, { EVENT_KEY } from '@/utils/bus'
export default {
name: 'FromBottomDialog',
props: {
modelValue: {
type: Boolean,
default: false
},
mode: {
type: String,
default: 'dark'
// default: 'light'
// default: 'white'
},
maskMode: {
type: String,
default: 'dark'
},
height: {
type: String,
default: 'calc(var(--vh, 1vh) * 70)'
},
showHengGang: {
type: Boolean,
default: true
},
pageId: {
type: String,
default: null,
required: true
},
borderRadius: {
type: String,
default: '15rem 15rem 0 0'
},
tag: {
type: String,
default: ''
}
},
watch: {
modelValue(newVal) {
let page = document.getElementById(this.pageId)
if (newVal) {
this.pagePosition = this.$getCss2(page, 'position')
page.style.position = 'absolute'
this.scroll = document.documentElement.scrollTop
document.body.style.position = 'fixed'
document.body.style.top = -this.scroll + 'px'
let maskTemplate = `<div class="Mask fade-in ${this.maskMode}"></div>`
let mask = new Dom().create(maskTemplate)
mask.on('click', (e) => {
this.$stopPropagation(e)
this.hide(false)
})
page.appendChild(mask.els[0])
} else {
let page = document.getElementById(this.pageId)
page.style.position = this.pagePosition || 'fixed'
document.body.style.position = 'static'
document.documentElement.scrollTop = this.scroll
let mask = new Dom('.Mask').replaceClass('fade-in', 'fade-out')
setTimeout(() => {
mask.remove()
}, 250)
}
}
},
data() {
return {
scroll: 0,
startLocationY: 0,
moveYDistance: 0,
startTime: 0,
pagePosition: null
}
},
computed: {},
created() {},
methods: {
beforeEnter(el) {
this.$setCss(el, 'transition-duration', `250ms`)
this.$setCss(el, 'transform', `translate3d(0,${this.height},0)`)
},
enter(el, done) {
setTimeout(() => {
this.$setCss(el, 'transform', `translate3d(0,0,0)`)
}, 0)
setTimeout(() => {
// this.$setCss(el, 'transition-duration', `0ms`)
this.$setCss(el, 'transform', `none`)
done()
}, 250)
},
afterEnter() {},
beforeLeave(el) {
this.$setCss(el, 'transition-duration', `250ms`)
this.$setCss(el, 'transform', `translate3d(0,0,0)`)
},
leave(el, done) {
//ref获取不到
let maxHeight = new Dom('.FromBottomDialog').css('max-height')
this.$setCss(el, 'transform', `translate3d(0,${maxHeight},0)`)
setTimeout(done, 250)
},
afterLeave() {},
hide(val = false) {
this.$emit('update:modelValue', val)
this.$emit('cancel')
},
start(e) {
if (this.$refs.dialog.scrollTop !== 0) return
this.startLocationY = e.touches[0].pageY
this.startTime = Date.now()
this.$setCss(this.$refs.dialog, 'transition-duration', `0ms`)
},
move(e) {
if (this.$refs.dialog.scrollTop !== 0) return
this.moveYDistance = e.touches[0].pageY - this.startLocationY
if (this.moveYDistance > 0) {
bus.emit(EVENT_KEY.DIALOG_MOVE, {
tag: this.tag,
e: this.moveYDistance
})
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,${this.moveYDistance}px,0)`)
}
},
end() {
//点击
if (Date.now() - this.startTime < 150 && Math.abs(this.moveYDistance) < 30) {
return
}
//滑动
if (this.$refs.dialog.scrollTop !== 0) return
let clientHeight = this.$refs.dialog.clientHeight
this.$setCss(this.$refs.dialog, 'transition-duration', `250ms`)
if (Math.abs(this.moveYDistance) > clientHeight / 2) {
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,${clientHeight}px,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: this.tag, isClose: true })
setTimeout(this.hide, 250)
} else {
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,0,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: this.tag, isClose: false })
setTimeout(() => {
this.$setCss(this.$refs.dialog, 'transform', 'none')
// this.$setCss(this.$refs.dialog, 'transition-duration', `0ms`)
}, 250)
}
this.moveYDistance = 0
}
}
}
</script>
<style scoped lang="less">
@import '../../assets/less/index';
.FromBottomDialog {
z-index: 9;
position: fixed;
width: 100%;
overflow-y: auto;
padding-top: 24rem;
bottom: 0;
left: 0;
box-sizing: border-box;
border-radius: v-bind(borderRadius);
transition: all 0.3s;
&.dark {
background: var(--main-bg);
}
&.light {
background: whitesmoke;
}
&.white {
background: white;
}
&.no-heng-gang {
padding-top: 0;
}
.heng-gang {
border-radius: 15rem 15rem 0 0;
z-index: 3;
width: 100%;
position: fixed;
height: 30rem;
display: flex;
transform: translateY(-24rem);
justify-content: center;
align-items: center;
&.dark {
background: var(--main-bg);
.content {
background: var(--second-btn-color);
}
}
&.light {
background: whitesmoke;
.content {
background: darkgray;
}
}
&.white {
background: white;
.content {
background: darkgray;
}
}
.content {
border-radius: 2px;
height: 4rem;
width: 30rem;
}
}
}
</style>