first commit
This commit is contained in:
233
src/components/jx-popup/jx-popup.vue
Normal file
233
src/components/jx-popup/jx-popup.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user