Write the Code. Change the World.

5月 06

前边那个弹窗还有一些瑕疵,看得到的瑕疵。比如,使用贝塞尔曲线来模拟弹簧那种伸缩效果时,会露底。如果说是变魔术,用土话讲,那就是穿帮了。还有就是弹窗的动画的时间没有定义的地方。这次都加上了。先看看效果。

Code

popupLayout.vue

<template>
    <view v-if="controller.open" class="popup-layer" catchtouchmove="true">
        <view v-if="direction == 'top'" class="top-bg-color" :class="{ 'top-bg-show': controller.bg, 'top-bg-hide': !controller.bg }" :style="{ opacity: controller.bgOpacity }" @tap="close"></view>

        <view v-if="direction == 'bottom'" class="bottom-bg-color" :class="{ 'bottom-bg-show': controller.bg, 'bottom-bg-hide': !controller.bg }" :style="{ opacity: controller.bgOpacity }" @tap="close"></view>

        <view v-if="direction == 'left'" class="left-bg-color" :class="{ 'left-bg-show': controller.bg, 'left-bg-hide': !controller.bg }" :style="{ opacity: controller.bgOpacity }" @tap="close"></view>

        <view v-if="direction == 'right'" class="right-bg-color" :class="{ 'right-bg-show': controller.bg, 'right-bg-hide': !controller.bg }" :style="{ opacity: controller.bgOpacity }" @tap="close"></view>

        <view class="popup-content" :style="_location">
            <slot></slot>

            <view v-if="timingFun == 'cubic-bezier' && contentBgColor" :style="{background:contentBgColor}" :class="'content-bg-' + direction"></view>
        </view>
    </view>
</template>

<script setup>
import { reactive, defineProps, defineEmits, defineExpose, computed, onMounted, watch } from 'vue';

const controller = reactive({
    open: false,
    lock: false,
    bg: false,
    bgOpacity: 0,
    translate: -100,
    time: null
});

const emits = defineEmits(['onClose']);

const timingFuns = {
    'cubic-bezier': 'transition-timing-function: cubic-bezier(.15,-0.88,.41,1.68);',
    ease: 'transition-timing-function: easy;',
    'ease-in': 'transition-timing-function: ease-in;',
    'ease-out': 'transition-timing-function: ease-out;',
    'ease-in-out': 'transition-timing-function: ease-in-out;',
    linear: 'transition-timing-function: linear;'
};

const props = defineProps({
    direction: {
        type: String,
        default: 'top' // 方向  top,bottom,left,right
    },
    bgOpacity: {
        type: Number,
        default: 0.38
    },
    timingFun: {
        type: String,
        default: 'cubic-bezier'
    },
    contentBgColor: {
        type: String,
        default: ''
    },
    transitionDuration: {
        type: String,
        default: '0.5s'
    }
});

watch(() => props.timingFun, (newValue, oldValue) => {}, {
    immediate: true
});

const _translate = computed(() => {
    const transformObj = {
        top: `transform:translateY(${-controller.translate}%)`,
        bottom: `transform:translateY(${controller.translate}%)`,
        left: `transform:translateX(${-controller.translate}%)`,
        right: `transform:translateX(${controller.translate}%)`
    };
    return transformObj[props.direction];
});

const _location = computed(() => {
    const positionValue = {
        top: 'bottom:0px;width:100%;',
        bottom: 'top:0px;width:100%;',
        left: 'right:0px;top:0px;height:100%;',
        right: 'left:0px;top:0px;height:100%;'
    };

    let style = `transition-duration:${props.transitionDuration};`;
    if (timingFuns[props.timingFun]) {
        style += timingFuns[props.timingFun];
    }
    return style + positionValue[props.direction] + _translate.value;
});

const open = function() {
    if (controller.open) {
        return;
    }

    controller.open = true;

    let openTimer = setTimeout(() => {
        clearTimeout(openTimer);
        controller.lock = true;
        controller.bg = true;
        controller.bgOpacity = props.bgOpacity;
        controller.translate = -0.1;
        openTimer = null;
    }, 100);

    // 防止误触
    let lockTimer = setTimeout(() => {
        clearTimeout(lockTimer);
        controller.lock = false;
        lockTimer = null;
    }, 500);
};

const close = function() {
    if (controller.timer || controller.lock) {
        return;
    }

    controller.bgOpacity = 0;
    controller.bg = false;
    controller.translate = -100;

    controller.timer = setTimeout(() => {
        clearTimeout(controller.timer);
        controller.open = false;
        controller.timer = null;
        emits('onClose');
    }, 500);
};

defineExpose({
    open,
    close
});
</script>

<style lang="scss" scoped>
.popup-layer {
    width: 100vw;
    height: 100vh;
    overflow: hidden;
    top: 0px;
    left: 0px;
    position: fixed;
    z-index: 9999;

    .popup-content {
        position: absolute;
        z-index: 1000000;
        transition: transform;
    }

    .content-bg-top {
        position: absolute;
        width: 100%;
        height: 108rpx;
        left: 0;
        bottom: -100rpx;
    }

    .content-bg-bottom {
        position: absolute;
        width: 100%;
        height: 108rpx;
        left: 0;
        top: -100rpx;
    }

    .content-bg-left {
        position: absolute;
        width: 108rpx;
        height: 100%;
        top: 0;
        right: -100rpx;
    }

    .content-bg-right {
        position: absolute;
        width: 108rpx;
        height: 100%;
        top: 0;
        left: -100rpx;
    }
}

// 底部出来
.top-bg-color {
    width: 120vw;
    height: 120vh;
    background: #000;
    position: relative;
    left: -10vw;
    top: 121vh;
    opacity: 0;
    transition: 0.24s opacity, 0.24s top;
}

.top-bg-show {
    top: 0;
}

.top-bg-hide {
    top: 121vh;
}

.top-bg-color:after {
    content: '';
    width: 120vw;
    height: 120vw;
    border-radius: 50%;
    background-color: #000;
    position: absolute;
    left: 0;
    top: -30vw;
}

// 顶部出来
.bottom-bg-color {
    width: 120vw;
    height: 120vh;
    background: #000;
    position: relative;
    left: -10vw;
    bottom: 121vh;
    opacity: 0;
    transition: 0.24s opacity, 0.24s bottom;
}

.bottom-bg-show {
    bottom: 0;
}

.bottom-bg-hide {
    bottom: 121vh;
}

.bottom-bg-color:after {
    content: '';
    width: 120vw;
    height: 120vw;
    border-radius: 50%;
    background-color: #000;
    position: absolute;
    left: 0;
    bottom: -30vw;
}

// 右边出来
.left-bg-color {
    width: 120vw;
    height: 120vh;
    background: #000;
    position: relative;
    top: -10vh;
    left: 121vw;
    opacity: 0;
    transition: 0.24s opacity, 0.24s left;
}

.left-bg-show {
    left: 0;
}

.left-bg-hide {
    left: 121vw;
}

.left-bg-color:after {
    content: '';
    width: 120vh;
    height: 120vh;
    border-radius: 50%;
    background-color: #000;
    position: absolute;
    top: 0;
    left: -30vh;
}

// 左边边出来
.right-bg-color {
    width: 120vw;
    height: 120vh;
    background: #000;
    position: relative;
    top: -10vh;
    right: 121vw;
    opacity: 0;
    transition: 0.24s opacity, 0.24s left;
}

.right-bg-show {
    right: 0;
}

.right-bg-hide {
    right: 121vw;
}

.right-bg-color:after {
    content: '';
    width: 120vh;
    height: 120vh;
    border-radius: 50%;
    background-color: #000;
    position: absolute;
    top: 0;
    right: -30vh;
}
</style>

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注