base.mixin.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import {
  2. getStyleStr
  3. } from '../../../utils/style.js';
  4. // #ifdef APP-NVUE
  5. const animation = uni.requireNativePlugin('animation')
  6. // #endif
  7. export default {
  8. data() {
  9. return {
  10. timingFunction: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
  11. isAnimated: false,
  12. isScrolling: false,
  13. customDuration: 0,
  14. left: 0,
  15. mCurrent: this.current
  16. };
  17. },
  18. created() {},
  19. mounted() {
  20. // #ifdef MP
  21. this.swiperViews = this.$children
  22. // #endif
  23. // #ifdef APP-PLUS || H5
  24. this.swiperViews = this.$slots.default.map(it => it.child)
  25. // #endif
  26. this._setLeft();
  27. this.mCurrent = this.current
  28. this._notifyCurrentForItems(this.current, this.position)
  29. },
  30. watch: {
  31. mCurrent() {
  32. let current = this.mCurrent
  33. if (this.circular) {
  34. if (this.position == 1) {
  35. current = this.actualSize - (this.plus - 1)
  36. // console.log('最前了', current)
  37. } else if (this.position == this._size - 2) {
  38. current = this.plus - 2;
  39. // console.log('最后了', current)
  40. }
  41. if (current < 0) {
  42. current = this.position + 1
  43. }
  44. current %= this.actualSize
  45. }
  46. // console.log('position', this.position, current)
  47. this.$emit('update:current', current)
  48. this.$emit('change', current)
  49. this._notifyCurrentForItems(current, this.position)
  50. }
  51. },
  52. computed: {
  53. is3D() {
  54. return this.mode == '3d'
  55. },
  56. position() {
  57. return this.circular ? (this.mCurrent + this.plus) : this.mCurrent
  58. },
  59. manualDuration() {
  60. if (this.customDuration > 0)
  61. return this.customDuration
  62. return this.isAnimated ? this.duration : 0
  63. },
  64. boxStyle() {
  65. return getStyleStr({
  66. width: this.width + 'rpx',
  67. height: this.height + 'rpx'
  68. });
  69. },
  70. containerStyle() {
  71. const style = {
  72. height: this.height + 'rpx'
  73. };
  74. // #ifdef APP-NVUE
  75. // FIXME: 理论isAnimated=false应该不设置transform,但是ios有个奇怪的问题,top不为0导致布局错位
  76. const isIOS = uni.getSystemInfoSync().platform == 'ios'
  77. if (isIOS) {
  78. style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)'
  79. } else {
  80. if (this.isAnimated == false) {
  81. style.transform = 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)'
  82. }
  83. }
  84. // #endif
  85. // #ifndef APP-NVUE
  86. style.left = this.left + 'rpx'
  87. style.transition = 'left ' + this.manualDuration + 'ms ' + this.timingFunction
  88. // #endif
  89. return getStyleStr(style);
  90. },
  91. _size() {
  92. return (this.$slots && this.$slots.default && this.$slots.default.length) || this.size;
  93. },
  94. // plus * 2
  95. plusSize() {
  96. return this.circular ? this.plus * 2 : 0;
  97. },
  98. // 真实长度
  99. actualSize() {
  100. return this._size - this.plusSize;
  101. }
  102. },
  103. methods: {
  104. prev() {
  105. if (this.isAnimated) return;
  106. if (this.isScrolling) return;
  107. if (this.mCurrent == 0 && this.circular == false) return
  108. this.mCurrent--;
  109. this._run()
  110. },
  111. next() {
  112. if (this.isAnimated) return;
  113. if (this.isScrolling) return;
  114. if (this.circular == true) {
  115. this.mCurrent++;
  116. if (this.mCurrent == this._size) {
  117. this.mCurrent = 0;
  118. }
  119. } else {
  120. if (this.mCurrent == this._size - 1) return
  121. this.mCurrent++;
  122. }
  123. this._run()
  124. },
  125. moveTo(e) {
  126. if (this.isAnimated) return
  127. const {
  128. deltaX,
  129. left
  130. } = e
  131. this.isScrolling = true
  132. if (!this.circular) {
  133. if (
  134. // 第一项,不能向右滑(上一项)
  135. (deltaX > 0 && this.mCurrent == 0) ||
  136. // 最后一项,不能向左滑(下一项)
  137. (deltaX < 0 && this.mCurrent == this._size - 1)
  138. ) {
  139. if (!this.bounce) return
  140. // 添加阻尼滑动
  141. const _left = this._left || this.wxsData.left
  142. this.left = _left + (deltaX * (1 - Math.abs(deltaX) * 3 / (this.width * 5)))
  143. this._set3DScale(deltaX)
  144. return
  145. }
  146. }
  147. this.left = left
  148. this._set3DScale(deltaX)
  149. },
  150. moveEnd(e) {
  151. const {
  152. velocity,
  153. deltaX,
  154. deltaY
  155. } = e
  156. this.isScrolling = false
  157. if (!this.circular) {
  158. // 第一项,不能向右滑(上一项)
  159. if (deltaX > 0 && this.mCurrent == 0) {
  160. this._restoreStartTouch()
  161. return
  162. }
  163. // 最后一项,不能向左滑(下一项)
  164. if (deltaX < 0 && this.mCurrent == this._size - 1) {
  165. this._restoreStartTouch()
  166. return
  167. }
  168. }
  169. const isTurnPage = Math.abs(deltaX) > this.itemWidth / 2
  170. if (isTurnPage || velocity > 0.2) {
  171. if (deltaX < 0) {
  172. this.customDuration = 350
  173. this.next()
  174. } else if (deltaX > 0) {
  175. this.customDuration = 350
  176. this.prev()
  177. }
  178. } else {
  179. this._restoreStartTouch()
  180. }
  181. },
  182. _set3DScale(deltaX) {
  183. if (this.is3D) {
  184. const min = Math.min
  185. const maxScale = Math.abs(this.scale - 1)
  186. const mScale = deltaX * maxScale / this.width
  187. const mRealScale = min(this.scale, this.scale - Math.abs(mScale))
  188. this.swiperViews[this.position].mScale = mRealScale < 1 ? 1 : mRealScale
  189. if (this.position - 1 > -1) {
  190. this.swiperViews[this.position - 1].mScale = mScale > 0 ? min(this.scale, 1 + mScale) : min(1, 1 + mScale)
  191. }
  192. if (this.position + 1 < this._size) {
  193. this.swiperViews[this.position + 1].mScale = mScale > 0 ? min(1, 1 - mScale) : min(this.scale, 1 - mScale)
  194. }
  195. }
  196. },
  197. _restoreStartTouch() {
  198. const self = this
  199. this.customDuration = 300
  200. // #ifdef APP-VUE || MP-WEIXIN || H5
  201. this.left = this.wxsData.left
  202. // #endif
  203. // #ifndef APP-PLUS || MP-WEIXIN || H5
  204. this.left = this._left
  205. // #endif
  206. this._run(false)
  207. if (this.is3D) {
  208. this.swiperViews[this.position].restoreScale(this.manualDuration)
  209. if (this.position - 1 > -1) {
  210. this.swiperViews[this.position - 1].restoreScale(this.manualDuration)
  211. }
  212. if (this.position + 1 < this._size) {
  213. this.swiperViews[this.position + 1].restoreScale(this.manualDuration)
  214. }
  215. }
  216. },
  217. _notifyCurrentForItems(current, position) {
  218. this.swiperViews && this.swiperViews.forEach(it => {
  219. it.current = current
  220. it.position = position
  221. })
  222. },
  223. _run(isSet = true) {
  224. this.isAnimated = true;
  225. if (isSet)
  226. this._setLeft();
  227. const self = this;
  228. if (this.is3D) {
  229. this.swiperViews[this.position].restoreToScale(this.scale, this.manualDuration)
  230. if (this.position - 1 > -1) {
  231. this.swiperViews[this.position - 1].restoreToScale(1, this.manualDuration)
  232. }
  233. if (this.position + 1 < this._size) {
  234. this.swiperViews[this.position + 1].restoreToScale(1, this.manualDuration)
  235. }
  236. }
  237. // #ifdef APP-NVUE
  238. animation.transition(this.$refs.container, {
  239. styles: {
  240. transform: 'translate(' + uni.upx2px(this.left) + 'px' + ',0px)',
  241. },
  242. duration: this.manualDuration, //ms
  243. timingFunction: this.timingFunction,
  244. needLayout: false,
  245. delay: 0 //ms
  246. }, function() {
  247. self._reset();
  248. })
  249. // #endif
  250. // #ifndef APP-NVUE
  251. setTimeout(() => {
  252. this._reset();
  253. }, this.manualDuration);
  254. // #endif
  255. },
  256. _setLeft() {
  257. if (this.circular == true) {
  258. const s1 = (this.width - this.itemWidth - this.space * 2) / 2;
  259. let left = (this.plus + this.mCurrent) * (this.space + this.itemWidth) - s1;
  260. this.left = -left;
  261. } else {
  262. this.left = -(this.itemWidth + this.space) * this.mCurrent
  263. }
  264. // #ifdef APP-VUE || MP-WEIXIN || H5
  265. this.wxsData = {
  266. left: this.left,
  267. bounce: this.bounce
  268. }
  269. // #endif
  270. },
  271. _reset() {
  272. this.isScrolling = false
  273. this.isAnimated = false
  274. this.customDuration = 0
  275. if (this.circular == true) {
  276. if (this.position == 1) {
  277. this.mCurrent = this.actualSize - (this.plus - 1);
  278. this._setLeft();
  279. this._restoreScale()
  280. }
  281. // -2:原数组索引-1 + plus数组索引-1
  282. if (this.position == this._size - 2) {
  283. this.mCurrent = this.plus - 2;
  284. this._setLeft();
  285. this._restoreScale()
  286. }
  287. }
  288. },
  289. _restoreScale() {
  290. if (this.is3D) {
  291. this.swiperViews[this.position].restoreToScale(this.scale, 0)
  292. if (this.position - 1 > -1) {
  293. this.swiperViews[this.position - 1].restoreToScale(1, 0)
  294. }
  295. if (this.position + 1 < this._size) {
  296. this.swiperViews[this.position + 1].restoreToScale(1, 0)
  297. }
  298. }
  299. }
  300. }
  301. }