Vue3 + Pinia + Vite5 仿抖音,完全度90% . Vue3 + Pinia + Vite5 imitate TikTok with 90% completeness
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

165 lines
3.6 KiB

<template>
<transition name="from-bottom">
<div ref="dialog" class="FromBottomDialog" v-if="modelValue" :class="mode" :style="{height}"
@touchstart="start"
@touchmove="move"
@touchend="end"
>
<div class="heng-gang" :class="mode">
<div class="content"></div>
</div>
<slot></slot>
</div>
</transition>
</template>
<script>
export default {
name: "FromBottomDialog",
props: {
modelValue: {
type: Boolean,
default: false
},
mode: {
type: String,
default: 'dark'
},
height: {
type: String,
default: '70vh'
}
},
watch: {
modelValue(newVal) {
if (newVal) {
this.scroll = document.documentElement.scrollTop
document.body.style.position = 'fixed'
document.body.style.top = -this.scroll + 'px'
} else {
document.body.style.position = 'static'
document.documentElement.scrollTop = this.scroll
}
this.$store.commit('setMaskDialog', newVal)
},
maskDialog(newVal) {
this.hide(newVal)
}
},
data() {
return {
scroll: 0,
startLocationY: 0,
moveYDistance: 0,
startTime: 0
}
},
computed: {
maskDialog() {
return this.$store.state.maskDialog
},
},
created() {
},
methods: {
hide(val = false) {
this.$emit('update:modelValue', val)
},
start(e) {
if (this.$refs.dialog.scrollTop !== 0) return
this.$setCss(this.$refs.dialog, 'transition-duration', `0ms`)
this.startLocationY = e.touches[0].pageY
this.startTime = Date.now()
},
move(e) {
if (this.$refs.dialog.scrollTop !== 0) return
this.moveYDistance = e.touches[0].pageY - this.startLocationY
if (this.moveYDistance > 0) {
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,${this.moveYDistance}px,0)`)
}
},
end(e) {
if (this.$refs.dialog.scrollTop !== 0) return
let clientHeight = this.$refs.dialog.clientHeight
if (Math.abs(this.moveYDistance) > clientHeight / 2) {
this.$setCss(this.$refs.dialog, 'transition-duration', `300ms`)
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,${clientHeight}px,0)`)
setTimeout(this.hide, 300)
} else {
this.$setCss(this.$refs.dialog, 'transition-duration', `300ms`)
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,0,0)`)
setTimeout(()=>{
this.$setCss(this.$refs.dialog, 'transform', 'none')
this.$setCss(this.$refs.dialog, 'transition-duration', `0ms`)
}, 300)
}
}
}
}
</script>
<style scoped lang="scss">
@import "../assets/scss/index";
.from-bottom-enter-active,
.from-bottom-leave-active {
transition: transform 0.2s ease;
}
.from-bottom-enter-from,
.from-bottom-leave-to {
transform: translate3d(0, 100vh, 0);
}
.FromBottomDialog {
z-index: 9;
position: fixed;
width: 100%;
overflow: auto;
bottom: 0;
box-sizing: border-box;
border-radius: .5rem .5rem 0 0;
&.dark {
background: $main-bg;
}
&.light {
background: white;
}
.heng-gang {
border-radius: .5rem .5rem 0 0;
z-index: 3;
width: 100%;
position: fixed;
padding-top: 1rem;
display: flex;
justify-content: center;
&.dark {
background: $main-bg;
.content {
background: $second-btn-color;
}
}
&.light {
background: white;
.content {
background: darkgray;
}
}
.content {
border-radius: 2px;
height: .4rem;
width: 3rem;
margin-bottom: 1rem;
}
}
}
</style>