wyb-popup.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. <template>
  2. <view v-if="isShow">
  3. <view
  4. @tap.stop.prevent
  5. @touchmove.stop.prevent
  6. class="wyb-popup-box"
  7. :style="{
  8. transitionDuration: duration + 'ms',
  9. opacity: contentOpacity || (type === 'center' ? 0 : 1),
  10. transform: contentTransform || autoTransform,
  11. zIndex: zIndex,
  12. borderTopRightRadius: type === 'center' || type === 'bottom' || type === 'left' ? radius + 'px' : 0,
  13. borderTopLeftRadius: type === 'center' || type === 'bottom' || type === 'right' ? radius + 'px' : 0,
  14. borderBottomRightRadius: type === 'center' || type === 'top' || type === 'left' ? radius + 'px' : 0,
  15. borderBottomLeftRadius: type === 'center' || type === 'top' || type === 'right' ? radius + 'px' : 0,
  16. width: autoWidth,
  17. height: autoHeight,
  18. minWidth: width + 'rpx',
  19. minHeight: height + 'rpx',
  20. top: sizeChange && type === 'center' ? winReTop : autoTop,
  21. bottom: autoBottom,
  22. left: autoLeft,
  23. right: autoRight,
  24. backgroundColor: bgColor}">
  25. <view
  26. class="wyb-popup-close"
  27. v-if="showCloseIcon"
  28. :style="{
  29. width: closeIcon ? closeIconSize + 'rpx' : 'auto',
  30. height: closeIcon ? closeIconSize + 'rpx' : 'auto',
  31. top: closeIconPos === 'top-right' || closeIconPos === 'top-left' ? vertOffset + 'rpx' : 'auto',
  32. bottom: closeIconPos === 'bottom-right' || closeIconPos === 'bottom-left' ? vertOffset + 'rpx' : 'auto',
  33. left: closeIconPos === 'bottom-left' || closeIconPos === 'top-left' ? horiOffset + 'rpx' : 'auto',
  34. right: closeIconPos === 'bottom-right' || closeIconPos === 'top-right' ? horiOffset + 'rpx' : 'auto'}">
  35. <image class="wyb-popup-custom-close" v-if="showCloseIcon&&closeIcon" :src="closeIcon" @tap="hide" :style="{
  36. width: closeIconSize + 'rpx',
  37. height: closeIconSize + 'rpx'}" />
  38. <view v-if="showCloseIcon&&!closeIcon" class="iconfont icon-close" @tap="hide" />
  39. </view>
  40. <scroll-view
  41. class="wyb-popup-container"
  42. :style="{
  43. width: autoWidth,
  44. height: autoHeight}"
  45. :enable-flex="true"
  46. :scroll-y="scrollY"
  47. :scroll-x="scrollX">
  48. <view class="wyb-popup-slot"><slot></slot></view>
  49. </scroll-view>
  50. </view>
  51. <view
  52. class="wyb-popup-mask"
  53. @tap.stop="close"
  54. @touchmove.stop.prevent
  55. :style="{
  56. opacity: maskOpacity,
  57. transitionDuration: duration + 'ms',
  58. backgroundColor: 'rgba(0, 0, 0, ' + maskAlpha + ')',
  59. zIndex: zIndex - 1}" />
  60. </view>
  61. </template>
  62. <script>
  63. export default {
  64. data() {
  65. return {
  66. w: uni.getSystemInfoSync().screenWidth,
  67. h: uni.getSystemInfoSync().screenHeight,
  68. isShow: false,
  69. winReBottom: '',
  70. winReTop: '',
  71. sizeChange: false,
  72. contentOpacity: null,
  73. contentTransform: null,
  74. maskOpacity: 0
  75. }
  76. },
  77. computed: {
  78. autoCenterTop() {
  79. let statusBarHeight = uni.getSystemInfoSync().statusBarHeight
  80. let windowHeight = uni.getSystemInfoSync().windowHeight
  81. let popupHeight = this.rpxToPx(this.height)
  82. let navHeight = 44
  83. let result = `${(windowHeight - popupHeight) / 2 - this.negativeTop}px`
  84. return result
  85. },
  86. autoTransform() {
  87. let result = ''
  88. switch(this.type) {
  89. case 'center':
  90. if (this.centerAnim === 'zoom-lessen') {
  91. result = `scale(${this.zoomLessenMulti})`
  92. } else if (this.centerAnim === 'slide-up') {
  93. result = `translateY(${100 * this.slideMulti}%)`
  94. } else if (this.centerAnim === 'slide-down') {
  95. result = `translateY(${-100 * this.slideMulti}%)`
  96. } else if (this.centerAnim === 'fade') {
  97. result = 'auto'
  98. }
  99. break
  100. case 'bottom':
  101. result = 'translateY(100%)'
  102. break
  103. case 'top':
  104. result = 'translateY(-100%)'
  105. break
  106. case 'left':
  107. result = 'translateX(-100%)'
  108. break
  109. case 'right':
  110. result = 'translateX(100%)'
  111. break
  112. }
  113. return result
  114. },
  115. autoWidth() {
  116. if (this.type === 'center') {
  117. return `${this.width}rpx`
  118. } else {
  119. if (this.mode === 'size-fixed') {
  120. if (this.type === 'top' || this.type === 'bottom') {
  121. return '100%'
  122. } else {
  123. return `${this.width}rpx`
  124. }
  125. } else {
  126. if (this.type === 'top' || this.type === 'bottom') {
  127. return '100%'
  128. } else {
  129. return 'auto'
  130. }
  131. }
  132. }
  133. },
  134. autoHeight() {
  135. if (this.type === 'center') {
  136. return `${this.height}rpx`
  137. } else {
  138. if (this.mode === 'size-fixed') {
  139. if (this.type === 'left' || this.type === 'right') {
  140. return '100%'
  141. } else {
  142. return `${this.height}rpx`
  143. }
  144. } else {
  145. if (this.type === 'left' || this.type === 'right') {
  146. return '100%'
  147. } else {
  148. return 'auto'
  149. }
  150. }
  151. }
  152. },
  153. autoTop() {
  154. if (this.type === 'center') {
  155. return this.autoCenterTop
  156. } else if (this.type === 'bottom') {
  157. return 'auto'
  158. } else {
  159. return 0
  160. }
  161. },
  162. autoBottom() {
  163. if (this.type === 'center' || this.type === 'top') {
  164. return 'auto'
  165. } else {
  166. return 0
  167. }
  168. },
  169. autoLeft() {
  170. if (this.type === 'center') {
  171. return `${(this.w - this.rpxToPx(this.width)) / 2}px`
  172. } else if (this.type === 'right') {
  173. return 'auto'
  174. } else {
  175. return 0
  176. }
  177. },
  178. autoRight() {
  179. if (this.type === 'center' || this.type === 'left') {
  180. return 'auto'
  181. } else {
  182. return 0
  183. }
  184. }
  185. },
  186. props: {
  187. type: {
  188. type: String,
  189. default: 'bottom'
  190. },
  191. mode: {
  192. type: String,
  193. default: 'size-auto'
  194. },
  195. height: {
  196. type: [String, Number],
  197. default: 400
  198. },
  199. width: {
  200. type: [String, Number],
  201. default: 500
  202. },
  203. radius: {
  204. type: [String, Number],
  205. default: 0
  206. },
  207. zIndex: {
  208. type: [String, Number],
  209. default: 10076
  210. },
  211. maskClickClose: {
  212. type: Boolean,
  213. default: true
  214. },
  215. maskAlpha: {
  216. type: Number,
  217. default: 0.5
  218. },
  219. duration: {
  220. type: Number,
  221. default: 400
  222. },
  223. showCloseIcon: {
  224. type: Boolean,
  225. default: false
  226. },
  227. scrollY: {
  228. type: Boolean,
  229. default: false
  230. },
  231. scrollX: {
  232. type: Boolean,
  233. default: false
  234. },
  235. closeIconPos: {
  236. type: String,
  237. default: 'top-right'
  238. },
  239. closeIcon: {
  240. type: String,
  241. default: ''
  242. },
  243. closeIconSize: {
  244. type: [String, Number],
  245. default: '20'
  246. },
  247. vertOffset: {
  248. type: [String, Number],
  249. default: '22'
  250. },
  251. horiOffset: {
  252. type: [String, Number],
  253. default: '22'
  254. },
  255. centerAnim: {
  256. type: String,
  257. default: 'zoom-lessen'
  258. },
  259. bgColor: {
  260. type: String,
  261. default: '#ffffff'
  262. },
  263. zoomLessenMulti: {
  264. type: Number,
  265. default: 1.15
  266. },
  267. slideMulti: {
  268. type: Number,
  269. default: 1
  270. },
  271. negativeTop: {
  272. type: Number,
  273. default: 0
  274. }
  275. },
  276. mounted() {
  277. // #ifdef H5
  278. let winHeight = uni.getSystemInfoSync().windowHeight
  279. uni.onWindowResize(res => {
  280. this.sizeChange = true
  281. if (this.type === 'bottom') {
  282. this.winReBottom = winHeight - res.size.windowHeight + 'px'
  283. } else if (this.type === 'center') {
  284. this.winReTop = ((res.size.windowHeight - this.rpxToPx(this.height)) / 2 - this.negativeTop) + 'px'
  285. }
  286. })
  287. // #endif
  288. },
  289. methods: {
  290. close() {
  291. this.maskClickClose && this.hide()
  292. },
  293. show() {
  294. this.isShow = true
  295. // #ifndef H5
  296. this.$nextTick(() => {
  297. this.maskIn()
  298. this.contentIn()
  299. this.wait(this.duration + 1).then(() => {
  300. this.$emit('show', {
  301. pageScroll: false,
  302. overflow: 'hidden'
  303. })
  304. })
  305. })
  306. // #endif
  307. // #ifdef H5
  308. this.wait(10).then(() => {
  309. this.maskIn()
  310. this.contentIn()
  311. this.wait(this.duration + 1).then(() => {
  312. this.$emit('show', {
  313. pageScroll: false,
  314. overflow: 'hidden'
  315. })
  316. })
  317. })
  318. // #endif
  319. },
  320. hide() {
  321. this.contentOut()
  322. this.maskOut()
  323. this.wait(this.duration + 1).then(() => {
  324. this.isShow = false
  325. this.$emit('hide', {
  326. pageScroll: true,
  327. overflow: 'scroll'
  328. })
  329. })
  330. },
  331. contentIn() {
  332. switch (this.type) {
  333. case 'center':
  334. if (this.centerAnim === 'zoom-lessen') {
  335. this.contentOpacity = 1
  336. this.contentTransform = 'scale(1)'
  337. } else if (this.centerAnim === 'slide-up' || this.centerAnim === 'slide-down') {
  338. this.contentOpacity = 1
  339. this.contentTransform = 'translateY(0)'
  340. } else if (this.centerAnim === 'fade') {
  341. this.contentOpacity = 1
  342. }
  343. break
  344. case 'bottom':
  345. case 'top':
  346. this.contentTransform = 'translateY(0)'
  347. break
  348. case 'left':
  349. case 'right':
  350. this.contentTransform = 'translateX(0)'
  351. break
  352. }
  353. },
  354. contentOut() {
  355. this.contentOpacity = null
  356. this.contentTransform = null
  357. },
  358. maskIn() {
  359. this.maskOpacity = 1
  360. },
  361. maskOut() {
  362. this.maskOpacity = 0
  363. },
  364. rpxToPx(rpx) {
  365. return rpx / 750 * this.w
  366. },
  367. wait(time) {
  368. return new Promise(resolve => {
  369. setTimeout(() => {
  370. resolve()
  371. }, time)
  372. })
  373. }
  374. }
  375. }
  376. </script>
  377. <style>
  378. @import "./iconfont.css";
  379. .wyb-popup-box{
  380. position: fixed;
  381. transition-timing-function: ease-out;
  382. transition-property: opacity, transform;
  383. }
  384. .wyb-popup-container {
  385. position: relative;
  386. box-sizing: border-box;
  387. }
  388. .wyb-popup-slot {
  389. width: 100%;
  390. height: 100%;
  391. }
  392. .wyb-popup-mask {
  393. position: fixed;
  394. top: 0;
  395. left: 0;
  396. bottom: 0;
  397. right: 0;
  398. transition-timing-function: ease;
  399. transition-property: opacity, transform;
  400. }
  401. .wyb-popup-close {
  402. position: absolute;
  403. fontSize: 40rpx;
  404. color: #808080;
  405. z-index: 20000;
  406. }
  407. .wyb-popup-custom-close {
  408. left: 0;
  409. top: 0;
  410. position: absolute;
  411. }
  412. </style>