ax-popup.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <template>
  2. <view v-if="visible" :class="CssSheet" :style="[StyleSheet]" class="ax ax-popup">
  3. <view @animationend="animationend()" class="__body"><slot></slot></view>
  4. <view v-if="maskEnable || maskClose" @click="mask()" class="__mask"></view>
  5. </view>
  6. </template>
  7. <script>
  8. export default {
  9. name:"ax-popup",
  10. emits: [
  11. // 打开弹窗
  12. 'open',
  13. // 已打开弹窗
  14. 'opened',
  15. // 关闭窗口
  16. 'close',
  17. // 已关闭窗口
  18. 'closed',
  19. // 幕布被点击
  20. 'mask'
  21. ],
  22. props:{
  23. // 内容方位 [默认居中 | top 上方 | left 左边 | right 右边 | bottom 下方]
  24. position:{type:String,default:''},
  25. // 启用幕布
  26. maskEnable:{type:Boolean,default:false},
  27. // 幕布类型 [默认透明 | black 黑色 | white 白色]
  28. maskType:{type:String,default:''},
  29. // 幕布模糊
  30. maskBlur:{type:[Number,String],default:0},
  31. // 幕布关闭
  32. maskClose:{type:Boolean,default:false},
  33. },
  34. data() {
  35. return {
  36. // 组件可视性
  37. visible: false,
  38. // 关闭动画
  39. closing: false
  40. };
  41. },
  42. computed:{
  43. CssSheet(){
  44. return [
  45. this.position,
  46. this.maskType,
  47. this.closing?'close':'',
  48. ]
  49. },
  50. StyleSheet(){
  51. return {
  52. '--mask-blur': `blur(${this.maskBlur}px)`,
  53. }
  54. }
  55. },
  56. methods:{
  57. animationend(e){
  58. if(this.visible==true && this.closing==false){
  59. this.$emit('opened');
  60. }else{
  61. this.visible = false;
  62. this.closing = false;
  63. this.$emit('closed');
  64. }
  65. },
  66. mask(){
  67. if(this.maskClose) this.close();
  68. this.$emit('mask');
  69. },
  70. close(){
  71. this.closing = true;
  72. this.$emit('close');
  73. },
  74. open(){
  75. this.visible = true;
  76. this.closing = false;
  77. this.$emit('open');
  78. }
  79. }
  80. }
  81. </script>
  82. <style scoped>
  83. .ax-popup{
  84. --duration: 150ms;
  85. --mask-background: transparent;
  86. display: flex;
  87. align-items: center;
  88. justify-content: center;
  89. flex-direction: column;
  90. transform-style: preserve-3d;
  91. position: fixed;
  92. top: 0;
  93. left: 0;
  94. right: 0;
  95. bottom: 0;
  96. z-index: 99999;
  97. }
  98. .ax-popup .__mask{
  99. position: absolute;
  100. top: 0;
  101. left: 0;
  102. right: 0;
  103. bottom: 0;
  104. backdrop-filter: var(--mask-blur);
  105. background-color: var(--mask-background);
  106. z-index: 0;
  107. animation: showMask var(--duration) ease-out 1;
  108. }
  109. @keyframes showMask{
  110. 0%{opacity: 0;}
  111. 100%{opacity: 1;}
  112. }
  113. .ax-popup .__body{
  114. position: relative;
  115. z-index: 1;
  116. animation: showBody var(--duration) ease-out 1;
  117. }
  118. @keyframes showBody{
  119. 0%{transform: translateY(75%);opacity: 0;}
  120. 100%{transform: translateY(0%);opacity: 1;}
  121. }
  122. .ax-popup.black .__mask{
  123. --mask-background: rgba(0, 0, 0, 0.35);
  124. }
  125. .ax-popup.white .__mask{
  126. --mask-background: rgba(255, 255, 255, 0.85);
  127. }
  128. .ax-popup.close .__mask{
  129. animation-name: closeMask;
  130. animation-fill-mode: forwards;
  131. }
  132. @keyframes closeMask{
  133. 0%{opacity: 1;}
  134. 100%{opacity: 0;}
  135. }
  136. .ax-popup.close .__body{
  137. animation-name: closeBody;
  138. animation-fill-mode: forwards;
  139. }
  140. @keyframes closeBody{
  141. 0%{transform: translateY(0%);opacity: 1;}
  142. 100%{transform: translateY(-75%);opacity: 0;}
  143. }
  144. .ax-popup.top{
  145. justify-content: flex-start;
  146. }
  147. .ax-popup.top .__body{
  148. animation-name: showTopBody;
  149. }
  150. .ax-popup.top.close .__body{
  151. animation-name: closeTopBody;
  152. }
  153. @keyframes showTopBody{
  154. 0%{transform: translateY(-100%);opacity: 0;}
  155. 100%{transform: translateY(0%);opacity: 1;}
  156. }
  157. @keyframes closeTopBody{
  158. 0%{transform: translateY(0%);opacity: 1;}
  159. 100%{transform: translateY(-100%);opacity: 0;}
  160. }
  161. .ax-popup.bottom{
  162. justify-content: flex-end;
  163. }
  164. .ax-popup.bottom .__body{
  165. animation-name: showBottomBody;
  166. }
  167. .ax-popup.bottom.close .__body{
  168. animation-name: closeBottomBody;
  169. }
  170. @keyframes showBottomBody{
  171. 0%{transform: translateY(100%);opacity: 0;}
  172. 100%{transform: translateY(0%);opacity: 1;}
  173. }
  174. @keyframes closeBottomBody{
  175. 0%{transform: translateY(0%);opacity: 1;}
  176. 100%{transform: translateY(100%);opacity: 0;}
  177. }
  178. .ax-popup.left{
  179. align-items: flex-start;
  180. }
  181. .ax-popup.left .__body{
  182. animation-name: showLeftBody;
  183. }
  184. .ax-popup.left.close .__body{
  185. animation-name: closeLeftBody;
  186. }
  187. @keyframes showLeftBody{
  188. 0%{transform: translateX(-100%);opacity: 0;}
  189. 100%{transform: translateX(0%);opacity: 1;}
  190. }
  191. @keyframes closeLeftBody{
  192. 0%{transform: translateX(0%);opacity: 1;}
  193. 100%{transform: translateX(-100%);opacity: 0;}
  194. }
  195. .ax-popup.right{
  196. align-items: flex-end;
  197. }
  198. .ax-popup.right .__body{
  199. animation-name: showRightBody;
  200. }
  201. .ax-popup.right.close .__body{
  202. animation-name: closeRightBody;
  203. }
  204. @keyframes showRightBody{
  205. 0%{transform: translateX(100%);opacity: 0;}
  206. 100%{transform: translateX(0%);opacity: 1;}
  207. }
  208. @keyframes closeRightBody{
  209. 0%{transform: translateX(0%);opacity: 1;}
  210. 100%{transform: translateX(100%);opacity: 0;}
  211. }
  212. </style>