233 lines
4.3 KiB
Vue
233 lines
4.3 KiB
Vue
<template>
|
|
<view
|
|
class="jx-popup"
|
|
v-show="hideen"
|
|
:class="{ popupAnimation: isPopup }"
|
|
@tap="maskClice"
|
|
@touchmove.prevent
|
|
>
|
|
<!-- jx-popup 内容 -->
|
|
<view class="popup-center" :class="[type, popupActive]" @tap.stop>
|
|
<slot></slot>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
/**
|
|
* jx-popup 组件
|
|
* @param (open) 打开popup
|
|
* @param (close) 关闭popup
|
|
* @emit (mask) mask 触发事件
|
|
* @param (isMask) 是否允许点击 mask 关闭
|
|
* @param (type) popup 打开方式
|
|
*/
|
|
|
|
import { ref, computed } from 'vue'
|
|
|
|
/**
|
|
* props
|
|
*/
|
|
interface PropType {
|
|
isMask?: boolean
|
|
time?: number
|
|
type: string
|
|
}
|
|
const props = withDefaults(defineProps<PropType>(), {
|
|
isMask: false,
|
|
time: 300,
|
|
})
|
|
|
|
/**
|
|
* emit
|
|
*/
|
|
const emit = defineEmits<{
|
|
(e: 'mask'): void
|
|
}>()
|
|
|
|
/**
|
|
* 打开popup
|
|
*/
|
|
// mask 动画
|
|
const isPopup = ref<boolean>(false)
|
|
// 动画 class
|
|
const popupActive = ref<string>('')
|
|
// 使用 v-show 避免重新渲染组件
|
|
const hideen = ref<boolean>(false)
|
|
let openTimer: any = null
|
|
function open() {
|
|
let ruler = ['top', 'right', 'bottom', 'left', 'center']
|
|
if (!ruler.includes(props.type)) {
|
|
return console.error(
|
|
'this is param need is top or right or bottom or left or center'
|
|
)
|
|
}
|
|
// 先显示了在执行动画
|
|
hideen.value = true
|
|
// 执行动画
|
|
clearTimeout(openTimer)
|
|
openTimer = setTimeout(() => {
|
|
isPopup.value = true
|
|
switch (props.type) {
|
|
case 'top':
|
|
popupActive.value = 'topAnimation'
|
|
break
|
|
case 'right':
|
|
popupActive.value = 'rightAnimation'
|
|
break
|
|
case 'bottom':
|
|
popupActive.value = 'bottomAnimation'
|
|
break
|
|
case 'left':
|
|
popupActive.value = 'leftAnimation'
|
|
break
|
|
case 'center':
|
|
popupActive.value = 'centerAnimation'
|
|
break
|
|
}
|
|
// 20 兼容 app 渲染过快
|
|
}, 20)
|
|
}
|
|
|
|
/**
|
|
* 关闭popup
|
|
*/
|
|
let closeTimer: any = null
|
|
function close(fun?: Function) {
|
|
isPopup.value = false
|
|
popupActive.value = ''
|
|
clearTimeout(closeTimer)
|
|
closeTimer = setTimeout(() => {
|
|
hideen.value = false
|
|
fun && fun()
|
|
}, props.time)
|
|
}
|
|
|
|
/**
|
|
* 点击 mask 是否允许关闭
|
|
*/
|
|
let maskTimer: any = null
|
|
function maskClice() {
|
|
if (props.isMask) return
|
|
// 遮罩层事件
|
|
emit('mask')
|
|
isPopup.value = false
|
|
popupActive.value = ''
|
|
// 动画执行完了在隐藏
|
|
clearTimeout(maskTimer)
|
|
maskTimer = setTimeout(() => {
|
|
hideen.value = false
|
|
}, props.time)
|
|
}
|
|
|
|
/**
|
|
* 暴露方法提供父组件调用
|
|
*/
|
|
defineExpose({
|
|
open, // 打开 popup
|
|
close, // 关闭 popup
|
|
})
|
|
|
|
const transitionTImer = computed(() => {
|
|
return `${props.time / 1000}s`
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.jx-popup {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
height: 100%;
|
|
width: 100%;
|
|
background-color: rgba(0, 0, 0, 0.4);
|
|
opacity: 0;
|
|
transition: all v-bind(transitionTImer);
|
|
.popup-center {
|
|
transition: all v-bind(transitionTImer);
|
|
}
|
|
|
|
.top {
|
|
position: absolute;
|
|
top: 0;
|
|
width: 100%;
|
|
transform: translateY(-100%);
|
|
}
|
|
|
|
.right {
|
|
position: absolute;
|
|
right: 0;
|
|
height: 100%;
|
|
overflow: auto;
|
|
transform: translateX(100%);
|
|
}
|
|
|
|
.bottom {
|
|
position: absolute;
|
|
bottom: 0;
|
|
width: 100%;
|
|
transform: translateY(100%);
|
|
}
|
|
|
|
.left {
|
|
position: absolute;
|
|
left: 0;
|
|
height: 100%;
|
|
overflow: auto;
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
.center {
|
|
position: absolute;
|
|
width: auto;
|
|
height: auto;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%) scale(0);
|
|
animation: centerScale v-bind(transitionTImer);
|
|
}
|
|
}
|
|
|
|
// mask 遮罩层
|
|
.popupAnimation {
|
|
z-index: 999999999999;
|
|
opacity: 1;
|
|
}
|
|
|
|
// top 顶部弹出
|
|
.topAnimation {
|
|
transform: translateY(0%) !important;
|
|
}
|
|
|
|
// right 右侧弹出
|
|
.rightAnimation {
|
|
transform: translateX(0%) !important;
|
|
}
|
|
|
|
// bottom 底部弹出
|
|
.bottomAnimation {
|
|
transform: translateY(0%) !important;
|
|
}
|
|
|
|
// left 左侧弹出
|
|
.leftAnimation {
|
|
transform: translateX(0%) !important;
|
|
}
|
|
|
|
// 居中弹出
|
|
.centerAnimation {
|
|
transform: translate(-50%, -50%) scale(1) !important;
|
|
}
|
|
|
|
@keyframes centerScale {
|
|
0% {
|
|
transform: translate(-50%, -50%) scale(0);
|
|
}
|
|
85% {
|
|
transform: translate(-50%, -50%) scale(1.02);
|
|
}
|
|
100% {
|
|
transform: translate(-50%, -50%) scale(1);
|
|
}
|
|
}
|
|
</style> |