You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
6.1 KiB

3 years ago
  1. <template>
  2. <view
  3. v-if="isShow"
  4. ref="ani"
  5. class="uni-transition"
  6. :class="[ani.in]"
  7. :style="'transform:' +transform+';'+stylesObject"
  8. @click="change"
  9. >
  10. <slot></slot>
  11. </view>
  12. </template>
  13. <script>
  14. // #ifdef APP-NVUE
  15. const animation = uni.requireNativePlugin('animation');
  16. // #endif
  17. /**
  18. * Transition 过渡动画
  19. * @description 简单过渡动画组件
  20. * @tutorial https://ext.dcloud.net.cn/plugin?id=985
  21. * @property {Boolean} show = [false|true] 控制组件显示或隐藏
  22. * @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
  23. * @value fade 渐隐渐出过渡
  24. * @value slide-top 由上至下过渡
  25. * @value slide-right 由右至左过渡
  26. * @value slide-bottom 由下至上过渡
  27. * @value slide-left 由左至右过渡
  28. * @value zoom-in 由小到大过渡
  29. * @value zoom-out 由大到小过渡
  30. * @property {Number} duration 过渡动画持续时间
  31. * @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
  32. */
  33. export default {
  34. name: 'uniTransition',
  35. props: {
  36. show: {
  37. type: Boolean,
  38. default: false
  39. },
  40. modeClass: {
  41. type: Array,
  42. default () {
  43. return []
  44. }
  45. },
  46. duration: {
  47. type: Number,
  48. default: 300
  49. },
  50. styles: {
  51. type: Object,
  52. default () {
  53. return {}
  54. }
  55. },
  56. maskBackgroundColor: {
  57. type: String,
  58. default: 'rgba(0, 0, 0, 0.4)'
  59. }
  60. },
  61. data() {
  62. return {
  63. isShow: false,
  64. transform: '',
  65. ani: { in: '',
  66. active: ''
  67. }
  68. };
  69. },
  70. watch: {
  71. show: {
  72. handler(newVal) {
  73. if (newVal) {
  74. this.open()
  75. } else {
  76. this.close()
  77. }
  78. },
  79. immediate: true
  80. }
  81. },
  82. computed: {
  83. stylesObject() {
  84. let styles = {
  85. ...this.styles,
  86. backgroundColor: this.maskBackgroundColor,
  87. 'transition-duration': this.duration / 1000 + 's'
  88. }
  89. let transfrom = ''
  90. for (let i in styles) {
  91. let line = this.toLine(i)
  92. transfrom += line + ':' + styles[i] + ';'
  93. }
  94. return transfrom
  95. }
  96. },
  97. created() {
  98. // this.timer = null
  99. // this.nextTick = (time = 50) => new Promise(resolve => {
  100. // clearTimeout(this.timer)
  101. // this.timer = setTimeout(resolve, time)
  102. // return this.timer
  103. // });
  104. },
  105. methods: {
  106. change() {
  107. this.$emit('click', {
  108. detail: this.isShow
  109. })
  110. },
  111. open() {
  112. clearTimeout(this.timer)
  113. this.isShow = true
  114. this.transform = ''
  115. this.ani.in = ''
  116. for (let i in this.getTranfrom(false)) {
  117. if (i === 'opacity') {
  118. this.ani.in = 'fade-in'
  119. } else {
  120. this.transform += `${this.getTranfrom(false)[i]} `
  121. }
  122. }
  123. this.$nextTick(() => {
  124. setTimeout(() => {
  125. this._animation(true)
  126. }, 50)
  127. })
  128. },
  129. close(type) {
  130. clearTimeout(this.timer)
  131. this._animation(false)
  132. },
  133. _animation(type) {
  134. let styles = this.getTranfrom(type)
  135. // #ifdef APP-NVUE
  136. if(!this.$refs['ani']) return
  137. animation.transition(this.$refs['ani'].ref, {
  138. styles,
  139. duration: this.duration, //ms
  140. timingFunction: 'ease',
  141. needLayout: false,
  142. delay: 0 //ms
  143. }, () => {
  144. if (!type) {
  145. this.isShow = false
  146. }
  147. this.$emit('change', {
  148. detail: this.isShow
  149. })
  150. })
  151. // #endif
  152. // #ifndef APP-NVUE
  153. this.transform = ''
  154. for (let i in styles) {
  155. if (i === 'opacity') {
  156. this.ani.in = `fade-${type?'out':'in'}`
  157. } else {
  158. this.transform += `${styles[i]} `
  159. }
  160. }
  161. this.timer = setTimeout(() => {
  162. if (!type) {
  163. this.isShow = false
  164. }
  165. this.$emit('change', {
  166. detail: this.isShow
  167. })
  168. }, this.duration)
  169. // #endif
  170. },
  171. getTranfrom(type) {
  172. let styles = {
  173. transform: ''
  174. }
  175. this.modeClass.forEach((mode) => {
  176. switch (mode) {
  177. case 'fade':
  178. styles.opacity = type ? 1 : 0
  179. break;
  180. case 'slide-top':
  181. styles.transform += `translateY(${type?'0':'-100%'}) `
  182. break;
  183. case 'slide-right':
  184. styles.transform += `translateX(${type?'0':'100%'}) `
  185. break;
  186. case 'slide-bottom':
  187. styles.transform += `translateY(${type?'0':'100%'}) `
  188. break;
  189. case 'slide-left':
  190. styles.transform += `translateX(${type?'0':'-100%'}) `
  191. break;
  192. case 'zoom-in':
  193. styles.transform += `scale(${type?1:0.8}) `
  194. break;
  195. case 'zoom-out':
  196. styles.transform += `scale(${type?1:1.2}) `
  197. break;
  198. }
  199. })
  200. return styles
  201. },
  202. _modeClassArr(type) {
  203. let mode = this.modeClass
  204. if (typeof(mode) !== "string") {
  205. let modestr = ''
  206. mode.forEach((item) => {
  207. modestr += (item + '-' + type + ',')
  208. })
  209. return modestr.substr(0, modestr.length - 1)
  210. } else {
  211. return mode + '-' + type
  212. }
  213. },
  214. // getEl(el) {
  215. // console.log(el || el.ref || null);
  216. // return el || el.ref || null
  217. // },
  218. toLine(name) {
  219. return name.replace(/([A-Z])/g, "-$1").toLowerCase();
  220. }
  221. }
  222. }
  223. </script>
  224. <style>
  225. .uni-transition {
  226. transition-timing-function: ease;
  227. transition-duration: 0.3s;
  228. transition-property: transform, opacity;
  229. }
  230. .fade-in {
  231. opacity: 0;
  232. }
  233. .fade-active {
  234. opacity: 1;
  235. }
  236. .slide-top-in {
  237. /* transition-property: transform, opacity; */
  238. transform: translateY(-100%);
  239. }
  240. .slide-top-active {
  241. transform: translateY(0);
  242. /* opacity: 1; */
  243. }
  244. .slide-right-in {
  245. transform: translateX(100%);
  246. }
  247. .slide-right-active {
  248. transform: translateX(0);
  249. }
  250. .slide-bottom-in {
  251. transform: translateY(100%);
  252. }
  253. .slide-bottom-active {
  254. transform: translateY(0);
  255. }
  256. .slide-left-in {
  257. transform: translateX(-100%);
  258. }
  259. .slide-left-active {
  260. transform: translateX(0);
  261. opacity: 1;
  262. }
  263. .zoom-in-in {
  264. transform: scale(0.8);
  265. }
  266. .zoom-out-active {
  267. transform: scale(1);
  268. }
  269. .zoom-out-in {
  270. transform: scale(1.2);
  271. }
  272. </style>