uni-popup.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. <template>
  2. <view v-if="showPopup" class="uni-popup" @touchmove.stop.prevent="clear">
  3. <uni-transition :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
  4. <uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap" style="height: 100%;">
  5. <view class="uni-popup__wrapper-box" @click.stop="clear">
  6. <slot />
  7. </view>
  8. </uni-transition>
  9. </view>
  10. </template>
  11. <script>
  12. /**
  13. * PopUp 弹出层
  14. * @description 弹出层组件,为了解决遮罩弹层的问题
  15. * @tutorial https://ext.dcloud.net.cn/plugin?id=329
  16. * @property {String} type = [top|center|bottom] 弹出方式
  17. * @value top 顶部弹出
  18. * @value center 中间弹出
  19. * @value bottom 底部弹出
  20. * @property {Boolean} animation = [ture|false] 是否开启动画
  21. * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
  22. * @event {Function} change 打开关闭弹窗触发,e={show: false}
  23. */
  24. export default {
  25. name: 'UniPopup',
  26. props: {
  27. // 开启动画
  28. animation: {
  29. type: Boolean,
  30. default: true
  31. },
  32. // 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
  33. type: {
  34. type: String,
  35. default: 'center'
  36. },
  37. // maskClick
  38. maskClick: {
  39. type: Boolean,
  40. default: true
  41. }
  42. },
  43. data() {
  44. return {
  45. duration: 300,
  46. ani: [],
  47. showPopup: false,
  48. showTrans: false,
  49. maskClass: {
  50. 'position': 'fixed',
  51. 'bottom': 0,
  52. 'top': 0,
  53. 'left': 0,
  54. 'right': 0,
  55. 'backgroundColor': 'rgba(0, 0, 0, 0.4)'
  56. },
  57. transClass: {
  58. 'position': 'fixed',
  59. 'left': 0,
  60. 'right': 0,
  61. }
  62. }
  63. },
  64. watch: {
  65. type: {
  66. handler: function(newVal) {
  67. switch (this.type) {
  68. case 'top':
  69. this.ani = ['slide-top']
  70. this.transClass = {
  71. 'position': 'fixed',
  72. 'left': 0,
  73. 'right': 0,
  74. }
  75. break
  76. case 'bottom':
  77. this.ani = ['slide-bottom']
  78. this.transClass = {
  79. 'position': 'fixed',
  80. 'left': 0,
  81. 'right': 0,
  82. 'bottom': 0
  83. }
  84. break
  85. case 'center':
  86. this.ani = ['zoom-out', 'fade']
  87. this.transClass = {
  88. 'position': 'fixed',
  89. /* #ifndef APP-NVUE */
  90. 'display': 'flex',
  91. 'flexDirection': 'column',
  92. /* #endif */
  93. 'bottom': 0,
  94. 'left': 0,
  95. 'right': 0,
  96. 'top': 0,
  97. 'justifyContent': 'center',
  98. 'alignItems': 'center'
  99. }
  100. break
  101. }
  102. },
  103. immediate: true
  104. }
  105. },
  106. created() {
  107. if (this.animation) {
  108. this.duration = 300
  109. } else {
  110. this.duration = 0
  111. }
  112. },
  113. methods: {
  114. clear(e) {
  115. // TODO nvue 取消冒泡
  116. e.stopPropagation()
  117. },
  118. open() {
  119. this.showPopup = true
  120. this.$nextTick(() => {
  121. clearTimeout(this.timer)
  122. this.timer = setTimeout(() => {
  123. this.showTrans = true
  124. }, 50);
  125. })
  126. this.$emit('change', {
  127. show: true
  128. })
  129. },
  130. close(type) {
  131. this.showTrans = false
  132. this.$nextTick(() => {
  133. clearTimeout(this.timer)
  134. this.timer = setTimeout(() => {
  135. this.$emit('change', {
  136. show: false
  137. })
  138. this.showPopup = false
  139. }, 300)
  140. })
  141. },
  142. onTap() {
  143. if (!this.maskClick) return
  144. this.close()
  145. }
  146. }
  147. }
  148. </script>
  149. <style scoped>
  150. .uni-popup {
  151. position: fixed;
  152. /* #ifdef H5 */
  153. top: var(--window-top);
  154. /* #endif */
  155. /* #ifndef H5 */
  156. top: 0;
  157. /* #endif */
  158. bottom: 0;
  159. left: 0;
  160. right: 0;
  161. /* #ifndef APP-NVUE */
  162. z-index: 99;
  163. /* #endif */
  164. }
  165. .uni-popup__mask {
  166. position: absolute;
  167. top: 0;
  168. bottom: 0;
  169. left: 0;
  170. right: 0;
  171. background-color: rgba(0, 0, 0, 0.4);
  172. opacity: 0;
  173. }
  174. .mask-ani {
  175. transition-property: opacity;
  176. transition-duration: 0.2s;
  177. }
  178. .uni-top-mask {
  179. opacity: 1;
  180. }
  181. .uni-bottom-mask {
  182. opacity: 1;
  183. }
  184. .uni-center-mask {
  185. opacity: 1;
  186. }
  187. .uni-popup__wrapper {
  188. /* #ifndef APP-NVUE */
  189. display: block;
  190. /* #endif */
  191. position: absolute;
  192. }
  193. .top {
  194. top: 0;
  195. left: 0;
  196. right: 0;
  197. transform: translateY(-500px);
  198. }
  199. .bottom {
  200. bottom: 0;
  201. left: 0;
  202. right: 0;
  203. transform: translateY(500px);
  204. }
  205. .center {
  206. /* #ifndef APP-NVUE */
  207. display: flex;
  208. flex-direction: column;
  209. /* #endif */
  210. bottom: 0;
  211. left: 0;
  212. right: 0;
  213. top: 0;
  214. justify-content: center;
  215. align-items: center;
  216. transform: scale(1.2);
  217. opacity: 0;
  218. }
  219. .uni-popup__wrapper-box {
  220. /* #ifndef APP-NVUE */
  221. display: block;
  222. /* #endif */
  223. position: relative;
  224. height: 100%;
  225. }
  226. .content-ani {
  227. /* transition: transform 0.3s;
  228. */
  229. transition-property: transform, opacity;
  230. transition-duration: 0.2s;
  231. }
  232. .uni-top-content {
  233. transform: translateY(0);
  234. }
  235. .uni-bottom-content {
  236. transform: translateY(0);
  237. }
  238. .uni-center-content {
  239. transform: scale(1);
  240. opacity: 1;
  241. }
  242. </style>