前言:
继续记录,在上篇文章中,弹出框遮罩层在ios上没有正确的铺盖全屏,是因为机型的原因,也和我们的代码结构有相关的问题。今天再来展示另外一个奇葩的问题。
这次我使用了在本篇博客中的弹出框组件CustomDialog.vue。记录uni-app横屏项目:自定义弹出框-CSDN博客
以及在这篇博客中的CustomItem.vue组件。
uni-app:踩坑路---scroll-view内使用fixed定位,无效的问题-CSDN博客
CustomDialog.vue
<template>
<view class="dialog-overlay" v-if="visible" :style="{ zIndex: zIndex }" @tap="closeMask">
<view class="dialog" v-if="dialogVisible" :style="[getStyle]" :class="[showAnimate ? 'bounce-enter-active' : 'bounce-leave-active']" @tap.stop>
<view class="close" v-if="showClose" @tap="close">
<view class="iconfont icon-guanbi"></view>
</view>
<slot></slot>
</view>
</view>
</template>
<script>
export default {
name: 'CustomDialog',
props: {
visible: {
type: Boolean,
default: false
},
width: {
type: String,
default: 'auto'
},
height: {
type: String,
default: 'auto'
},
radius: {
type: String,
default: '16rpx'
},
bgColor: {
type: String,
default: '#fff'
},
customStyle: {
type: Object,
default: () => ({})
},
/* 是否展示右上角关闭按钮 */
showClose: {
type: Boolean,
default: true
},
/* 是否点击遮罩层可以关闭弹出框 */
maskCloseAble: {
type: Boolean,
default: true
},
/* 弹出框层级 */
zIndex: {
type: Number,
default: 999
}
},
data() {
return {
dialogVisible: this.visible,
showAnimate: this.visible,
timer: null
};
},
beforeDestroy() {
this.clearTimeout();
},
watch: {
visible: {
handler(val) {
setTimeout(() => {
this.dialogVisible = val;
this.showAnimate = val;
}, 50);
},
immediate: true
}
},
computed: {
getStyle() {
return {
width: this.width,
height: this.height,
background: this.bgColor,
borderRadius: this.radius,
...this.customStyle
};
}
},
methods: {
clearTimeout() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
},
closeMask() {
if (!this.maskCloseAble) return;
this.close();
},
close() {
this.closeAnimate();
this.timer = setTimeout(() => {
this.$emit('close');
this.$emit('update:visible', false);
}, 500);
},
closeAnimate() {
this.showAnimate = false;
this.clearTimeout();
}
}
};
</script>
<style lang="scss" scoped>
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(#000, 0.3);
}
.dialog {
position: relative;
border-radius: 16px;
padding: 20rpx;
padding-bottom: 14rpx;
margin-left: -50rpx;
opacity: 0;
.close {
position: absolute;
width: 28rpx;
height: 28rpx;
border-radius: 50%;
background-color: rgba(#000, 0.6);
top: -10rpx;
right: -10rpx;
.icon {
width: 10rpx;
height: 10rpx;
}
}
}
/* 打开与关闭的类名 */
.bounce-enter-active {
animation: bounceIn 0.5s both;
}
.bounce-leave-active {
animation: bounceOut 0.5s both;
}
/* 定义bounceIn动画 */
@keyframes bounceIn {
0% {
opacity: 0;
transform: scale(0);
}
50% {
opacity: 1;
transform: scale(1.2);
}
70% {
opacity: 1;
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* 定义 bounceOut 动画 */
@keyframes bounceOut {
0% {
opacity: 1;
transform: scale(1);
}
25% {
opacity: 1;
transform: scale(0.95);
}
50% {
opacity: 0;
transform: scale(1.1);
}
100% {
opacity: 0;
transform: scale(0);
}
}
.icon-guanbi {
color: #94ffd8;
font-size: 16rpx;
}
</style>
CustomItem.vue
<template>
<view class="">
<view class="item" @click="visible = true"></view>
<view class="mask" v-if="visible" @click="visible = false"></view>
</view>
</template>
<script>
export default {
name: 'CustomItem',
data() {
return {
visible: false
};
}
};
</script>
<style lang="scss" scoped>
.item {
width: 100rpx;
height: 100rpx;
background-color: #00aaff;
}
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(#000, 0.5);
}
</style>
页面使用:
<template>
<view class="index">
<button @click="visible = true">click</button>
<custom-dialog :visible.sync="visible" width="500rpx" height="200rpx">
<view class="list">
<custom-item class="item" v-for="i in 3" :key="i"></custom-item>
</view>
</custom-dialog>
</view>
</template>
<script>
import CustomDialog from '@/components/CustomDialog/index.vue';
import CustomItem from '@/components/CustomItem/index.vue';
export default {
components: {
CustomDialog,
CustomItem
},
data() {
return {
visible: false
};
}
};
</script>
<style lang="scss" scoped>
.index {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.list {
padding: 20rpx;
display: flex;
align-items: center;
.item {
margin-right: 20rpx;
}
}
</style>
和上篇文章中不同的是,这次可并没有使用scroll-view组件,只是在弹出框组件内使用了另一个Item组件而已,但是效果却大大出乎了意料 !
弹出框显示:一切ok
点击蓝色方块,展示一个遮罩层:没想到这个遮罩层他又又又被截断了,没有去正确的铺满全屏。。。这次又是为什么?
经过我一层层的排出,最后得出了一个惊人的结论,居然和我的弹出框组件有关,还是最让人意想不到的css的transform属性。transform对普通元素的N多渲染影响,这篇文章就说明的相当详细。
下面我把所有关于transform都给注释掉了,然后再去尝试点击蓝色方块。
/* 定义bounceIn动画 */
@keyframes bounceIn {
0% {
opacity: 0;
// transform: scale(0);
}
50% {
opacity: 1;
// transform: scale(1.2);
}
70% {
opacity: 1;
// transform: scale(0.9);
}
100% {
opacity: 1;
// transform: scale(1);
}
}
/* 定义 bounceOut 动画 */
@keyframes bounceOut {
0% {
opacity: 1;
// transform: scale(1);
}
25% {
opacity: 1;
// transform: scale(0.95);
}
50% {
opacity: 0;
// transform: scale(1.1);
}
100% {
opacity: 0;
// transform: scale(0);
}
}
ok,彻底告别地球了。。。
既然问题已经找到,那就看怎么解决了。。。
1.不再使用transform属性,改用其他的属性编写动画,当然结论就是会发现动画没有使用transform丝滑就是了。
2.老办法,保留transform属性,但是需要避免在其子元素上使用fixed定位,如果非要使用,那就需要考虑将fixed的元素拿出去,这点其实在VUE3中解决起来很方便。。。毕竟一个teleport就可以直接将元素指定到对应的元素上。