<template> <view v-if="isShow"> <view @tap.stop.prevent @touchmove.stop.prevent class="wyb-popup-box" :style="{ transitionDuration: duration + 'ms', opacity: contentOpacity || (type === 'center' ? 0 : 1), transform: contentTransform || autoTransform, zIndex: zIndex, borderTopRightRadius: type === 'center' || type === 'bottom' || type === 'left' ? radius + 'px' : 0, borderTopLeftRadius: type === 'center' || type === 'bottom' || type === 'right' ? radius + 'px' : 0, borderBottomRightRadius: type === 'center' || type === 'top' || type === 'left' ? radius + 'px' : 0, borderBottomLeftRadius: type === 'center' || type === 'top' || type === 'right' ? radius + 'px' : 0, width: autoWidth, height: autoHeight, minWidth: width + 'rpx', minHeight: height + 'rpx', top: sizeChange && type === 'center' ? winReTop : autoTop, bottom: autoBottom, left: autoLeft, right: autoRight, backgroundColor: bgColor}"> <view class="wyb-popup-close" v-if="showCloseIcon" :style="{ width: closeIcon ? closeIconSize + 'rpx' : 'auto', height: closeIcon ? closeIconSize + 'rpx' : 'auto', top: closeIconPos === 'top-right' || closeIconPos === 'top-left' ? vertOffset + 'rpx' : 'auto', bottom: closeIconPos === 'bottom-right' || closeIconPos === 'bottom-left' ? vertOffset + 'rpx' : 'auto', left: closeIconPos === 'bottom-left' || closeIconPos === 'top-left' ? horiOffset + 'rpx' : 'auto', right: closeIconPos === 'bottom-right' || closeIconPos === 'top-right' ? horiOffset + 'rpx' : 'auto'}"> <image class="wyb-popup-custom-close" v-if="showCloseIcon&&closeIcon" :src="closeIcon" @tap="hide" :style="{ width: closeIconSize + 'rpx', height: closeIconSize + 'rpx'}" /> <view v-if="showCloseIcon&&!closeIcon" class="iconfont icon-close" @tap="hide" /> </view> <scroll-view class="wyb-popup-container" :style="{ width: autoWidth, height: autoHeight}" :enable-flex="true" :scroll-y="scrollY" :scroll-x="scrollX"> <view class="wyb-popup-slot"><slot></slot></view> </scroll-view> </view> <view class="wyb-popup-mask" @tap.stop="close" @touchmove.stop.prevent :style="{ opacity: maskOpacity, transitionDuration: duration + 'ms', backgroundColor: 'rgba(0, 0, 0, ' + maskAlpha + ')', zIndex: zIndex - 1}" /> </view> </template> <script> export default { data() { return { w: uni.getSystemInfoSync().screenWidth, h: uni.getSystemInfoSync().screenHeight, isShow: false, winReBottom: '', winReTop: '', sizeChange: false, contentOpacity: null, contentTransform: null, maskOpacity: 0 } }, computed: { autoCenterTop() { let statusBarHeight = uni.getSystemInfoSync().statusBarHeight let windowHeight = uni.getSystemInfoSync().windowHeight let popupHeight = this.rpxToPx(this.height) let navHeight = 44 let result = `${(windowHeight - popupHeight) / 2 - this.negativeTop}px` return result }, autoTransform() { let result = '' switch(this.type) { case 'center': if (this.centerAnim === 'zoom-lessen') { result = `scale(${this.zoomLessenMulti})` } else if (this.centerAnim === 'slide-up') { result = `translateY(${100 * this.slideMulti}%)` } else if (this.centerAnim === 'slide-down') { result = `translateY(${-100 * this.slideMulti}%)` } else if (this.centerAnim === 'fade') { result = 'auto' } break case 'bottom': result = 'translateY(100%)' break case 'top': result = 'translateY(-100%)' break case 'left': result = 'translateX(-100%)' break case 'right': result = 'translateX(100%)' break } return result }, autoWidth() { if (this.type === 'center') { return `${this.width}rpx` } else { if (this.mode === 'size-fixed') { if (this.type === 'top' || this.type === 'bottom') { return '100%' } else { return `${this.width}rpx` } } else { if (this.type === 'top' || this.type === 'bottom') { return '100%' } else { return 'auto' } } } }, autoHeight() { if (this.type === 'center') { return `${this.height}rpx` } else { if (this.mode === 'size-fixed') { if (this.type === 'left' || this.type === 'right') { return '100%' } else { return `${this.height}rpx` } } else { if (this.type === 'left' || this.type === 'right') { return '100%' } else { return '400px' } } } }, autoTop() { if (this.type === 'center') { return this.autoCenterTop } else if (this.type === 'bottom') { return 'auto' } else { return 0 } }, autoBottom() { if (this.type === 'center' || this.type === 'top') { return 'auto' } else { return 0 } }, autoLeft() { if (this.type === 'center') { return `${(this.w - this.rpxToPx(this.width)) / 2}px` } else if (this.type === 'right') { return 'auto' } else { return 0 } }, autoRight() { if (this.type === 'center' || this.type === 'left') { return 'auto' } else { return 0 } } }, props: { type: { type: String, default: 'bottom' }, mode: { type: String, default: 'size-auto' }, height: { type: [String, Number], default: 400 }, width: { type: [String, Number], default: 500 }, radius: { type: [String, Number], default: 0 }, zIndex: { type: [String, Number], default: 10076 }, maskClickClose: { type: Boolean, default: true }, maskAlpha: { type: Number, default: 0.5 }, duration: { type: Number, default: 400 }, showCloseIcon: { type: Boolean, default: false }, scrollY: { type: Boolean, default: false }, scrollX: { type: Boolean, default: false }, closeIconPos: { type: String, default: 'top-right' }, closeIcon: { type: String, default: '' }, closeIconSize: { type: [String, Number], default: '20' }, vertOffset: { type: [String, Number], default: '22' }, horiOffset: { type: [String, Number], default: '22' }, centerAnim: { type: String, default: 'zoom-lessen' }, bgColor: { type: String, default: '#ffffff' }, zoomLessenMulti: { type: Number, default: 1.15 }, slideMulti: { type: Number, default: 1 }, negativeTop: { type: Number, default: 0 } }, mounted() { // #ifdef H5 let winHeight = uni.getSystemInfoSync().windowHeight uni.onWindowResize(res => { this.sizeChange = true if (this.type === 'bottom') { this.winReBottom = winHeight - res.size.windowHeight + 'px' } else if (this.type === 'center') { this.winReTop = ((res.size.windowHeight - this.rpxToPx(this.height)) / 2 - this.negativeTop) + 'px' } }) // #endif }, methods: { close() { this.maskClickClose && this.hide() }, show() { this.isShow = true // #ifndef H5 this.$nextTick(() => { this.maskIn() this.contentIn() this.wait(this.duration + 1).then(() => { this.$emit('show', { pageScroll: false, overflow: 'hidden' }) }) }) // #endif // #ifdef H5 this.wait(10).then(() => { this.maskIn() this.contentIn() this.wait(this.duration + 1).then(() => { this.$emit('show', { pageScroll: false, overflow: 'hidden' }) }) }) // #endif }, hide() { this.contentOut() this.maskOut() this.wait(this.duration + 1).then(() => { this.isShow = false this.$emit('hide', { pageScroll: true, overflow: 'scroll' }) }) }, contentIn() { switch (this.type) { case 'center': if (this.centerAnim === 'zoom-lessen') { this.contentOpacity = 1 this.contentTransform = 'scale(1)' } else if (this.centerAnim === 'slide-up' || this.centerAnim === 'slide-down') { this.contentOpacity = 1 this.contentTransform = 'translateY(0)' } else if (this.centerAnim === 'fade') { this.contentOpacity = 1 } break case 'bottom': case 'top': this.contentTransform = 'translateY(0)' break case 'left': case 'right': this.contentTransform = 'translateX(0)' break } }, contentOut() { this.contentOpacity = null this.contentTransform = null }, maskIn() { this.maskOpacity = 1 }, maskOut() { this.maskOpacity = 0 }, rpxToPx(rpx) { return rpx / 750 * this.w }, wait(time) { return new Promise(resolve => { setTimeout(() => { resolve() }, time) }) } } } </script> <style> @import "./iconfont.css"; .wyb-popup-box{ position: fixed; transition-timing-function: ease-out; transition-property: opacity, transform; } .wyb-popup-container { position: relative; box-sizing: border-box; } .wyb-popup-slot { width: 100%; height: 100%; } .wyb-popup-mask { position: fixed; top: 0; left: 0; bottom: 0; right: 0; transition-timing-function: ease; transition-property: opacity, transform; } .wyb-popup-close { position: absolute; fontSize: 40rpx; color: #808080; z-index: 20000; } .wyb-popup-custom-close { left: 0; top: 0; position: absolute; } </style>