# 弹框组件的实现

# 需求分析

  • 需要一个 参数 去控制弹框的显示
  • 显示出的画面是多层文本对象堆叠出的结果,它需要有 父级元素遮布中心图块 ,三个文本对象合成展示。
  • 需要一个按钮去 控制关闭
  • 同时兼容PC端,移动端

# 基本实现

# 模板代码

// 父级元素
<template>
    <div class="popup" v-show="innerShow">
        <div class="mark"></div>
        <div class="popup-main" v-show="innerShow">
            <!-- ...自定义添加内容 -->
            <!-- 关闭按钮,script引入图片CloseImg -->
            <div class="close common" :style="{ 'background': `url(${CloseImg})` }" @click.stop="handleClose"
                @touchstart.stop="handleClose"></div>
        </div>
    </div>
</template>

# 添加Transition层

<!-- 给父级元素和主体元素添加Transition -->
<template>
    <!-- 父级transition -->
    <Transition name="container" :duration="500">
        <div class="popup" v-show="innerIsShow">
            <div class="popup-mark"></div>
            <!-- 主题内容transition -->
            <Transition name="main">
                <div class="popup-main" v-show="innerIsShow">

                    <!-- 关闭按钮 -->
                    <div class="close common" :style="{ 'background': `url(${CloseImg})` }" @click.stop="handleClose"
                        @touchstart.stop="handleClose"></div>
                </div>
            </Transition>

        </div>
    </Transition>
</template>

# css代码

<style lang="scss" scoped>
.main-enter-from {
    opacity: 0;
    transform: scale(0);
}


.main-enter-to {
    opacity: 1;
    transition: all 0.5s ease;
}

.main-leave-from {
    opacity: 1;
}



.main-leave-to {
    opacity: 0;
    transform: scale(0);
    transition: all 1s ease;
}


.popup {
    position: fixed;
    top: 0;
    right: 0;
    width: 100%;
    height: 100%;

    // 内容层 z-index要比遮罩大,否则会被遮盖
    .popup-mark {
        position: absolute;
        top: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, .6);
    }

    .popup-main {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100%;
        flex-direction: column;

        .common {
            background-repeat: no-repeat !important;
            background-size: cover !important;
        }

        .close {
            z-index: 999;
            width: 35px;
            height: 35px;
            margin-top: 30px;
        }
    }
}

</style>

# script代码

<script>
/**
 * 弹窗组件
 * @emit {boolean} setClose参数值 用于设置props.isShow关闭
 * @param {boolean} isShow 是否显示勋章
*/
import CloseImg from "./img/close.png";
export default {
    data() {
        return {
            CloseImg, // 关闭图片
            innerIsShow: this.isShow, // 接收父级的isShow
        }
    },
    methods: {
        handleClose() {
            // 关闭父组件传进来的isShow
            this.$emit("setClose", false);
            this.innerIsShow = false;
        },

    },
    props: {
        isShow: {
            type: Boolean,
            required: true,
            default: false,
        },
    },
    watch: {
        isShow(n) {
            n == true && (this.innerIsShow = n);
        },
    }
}
</script>

# 添加动画

.main-enter-active {
    animation: bounce-in 0.5s;
}
.main-leave-active {
    animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
    0% {
        transform: scale(0);
    }

    50% {
        transform: scale(1.25);
    }

    100% {
        transform: scale(1);
    }
}

# 思路小结

  1. 模板结构分为三部分: 父元素、 遮布、 中间图块
  2. 图层优先级(z-index: 中间图块 > 遮布 > 父元素
  3. 图块居中方案选择flex(justify-content:center;align-items:center)布局。最开始借鉴他人的 父相子绝top:50%; left:50%; transform: translate(-50%,-50%)),但考虑中间图块还需要添加 自定义内容,因此后者方案不考虑。
  4. 简单的弹出bounce特效,是通过transform: scale(x)和 动画的enterleave实现的。
  5. props接收到的数据并不是响应式的,需要本地data自己创建一个字段。

# 参考文章

  1. Transition | Vue.js (vuejs.org) (opens new window)
  2. 精细控制transition细节 - 实现一个活泼的弹框 - 掘金 (juejin.cn) (opens new window)