tn-custom-swiper-item.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <template>
  2. <!-- #ifdef MP-WEIXIN -->
  3. <view
  4. class="tn-c-swiper-item"
  5. :style="[swiperStyle]"
  6. :itemData="itemData"
  7. :currentIndex="currentIndex"
  8. :containerData="containerData"
  9. :change:itemData="wxs.itemDataObserver"
  10. :change:currentIndex="wxs.currentIndexObserver"
  11. :change:containerData="wxs.containerDataObserver"
  12. @touchstart="wxs.touchStart"
  13. :catch:touchmove="touching?wxs.touchMove:''"
  14. :catch:touchend="touching?wxs.touchEnd:''"
  15. >
  16. <view class="item__container tn-c-swiper-item__container" :style="[containerStyle]">
  17. <slot></slot>
  18. </view>
  19. </view>
  20. <!-- #endif -->
  21. <!-- #ifndef MP-WEIXIN -->
  22. <view
  23. class="tn-c-swiper-item"
  24. :style="[swiperStyle]"
  25. :itemData="itemData"
  26. :currentIndex="currentIndex"
  27. :containerData="containerData"
  28. :change:itemData="wxs.itemDataObserver"
  29. :change:currentIndex="wxs.currentIndexObserver"
  30. :change:containerData="wxs.containerDataObserver"
  31. @touchstart="wxs.touchStart"
  32. @touchmove="wxs.touchMove"
  33. @touchend="wxs.touchEnd"
  34. >
  35. <view class="item__container tn-c-swiper-item__container" :style="[containerStyle]">
  36. <slot></slot>
  37. </view>
  38. </view>
  39. <!-- #endif -->
  40. </template>
  41. <script src="./index.wxs" lang="wxs" module="wxs"></script>
  42. <script>
  43. export default {
  44. name: 'tn-custom-swiper-item',
  45. props: {
  46. },
  47. computed: {
  48. // swiperItem公共数据
  49. itemData() {
  50. return {
  51. index: this.index,
  52. itemWidth: this.itemWidth,
  53. itemHeight: this.itemHeight,
  54. itemTop: this.itemTop,
  55. itemLeft: this.itemLeft
  56. }
  57. },
  58. currentIndex() {
  59. return this.parentData.currentIndex
  60. },
  61. containerData() {
  62. return {
  63. duration: this.parentData.duration,
  64. animationFinish: this.parentData.swiperContainerAnimationFinish,
  65. circular: this.parentData.circular,
  66. swiperItemLength: this.swiperItemLength,
  67. vertical: this.parentData.vertical
  68. }
  69. },
  70. swiperStyle() {
  71. let style = {}
  72. style.transform = `translate3d(${this.translateX}%, ${this.translateY}%, 0px)`
  73. return style
  74. },
  75. containerStyle() {
  76. let style = {}
  77. if (this.parentData.customSwiperStyle && Object.keys(this.parentData.customSwiperStyle).length > 0) {
  78. style = this.parentData.customSwiperStyle
  79. }
  80. if ((this.currentIndex === 0 && this.index === this.swiperItemLength - 1) || (this.index === this.currentIndex - 1) &&
  81. (this.parentData.prevSwiperStyle && Object.keys(this.parentData.prevSwiperStyle).length > 0)
  82. ) {
  83. // 前一个swiperItem
  84. const copyStyle = JSON.parse(JSON.stringify(style))
  85. style = Object.assign(copyStyle, this.parentData.prevSwiperStyle)
  86. }
  87. if ((this.currentIndex === this.swiperItemLength - 1 && this.index === 0) || (this.index === this.currentIndex + 1) &&
  88. (this.parentData.nextSwiperStyle && Object.keys(this.parentData.nextSwiperStyle).length > 0)
  89. ) {
  90. // 后一个swiperItem
  91. const copyStyle = JSON.parse(JSON.stringify(style))
  92. style = Object.assign(copyStyle, this.parentData.nextSwiperStyle)
  93. }
  94. return style
  95. }
  96. },
  97. data() {
  98. return {
  99. // 父组件参数
  100. parentData: {
  101. duration: 500,
  102. currentIndex: 0,
  103. swiperContainerAnimationFinish: false,
  104. circular: false,
  105. vertical: false,
  106. prevSwiperStyle: {},
  107. customSwiperStyle: {},
  108. nextSwiperStyle: {}
  109. },
  110. // 标记当前是否正在触摸
  111. touching: true,
  112. // 当前swiperItem的偏移位置
  113. translateX: 0,
  114. translateY: 0,
  115. // 当前swiperItem的宽高
  116. itemWidth: 0,
  117. itemHeight: 0,
  118. // 当前swiperItem的位置信息
  119. itemTop: 0,
  120. itemLeft: 0,
  121. // 当前swiperItem的状态 prev current next
  122. status: 'current',
  123. // 当前swiperItem的index序号
  124. index: 0,
  125. // swiperItem的的数量
  126. swiperItemLength: 0
  127. }
  128. },
  129. created() {
  130. this.parent = false
  131. this.updateParentData()
  132. // 获取当前父组件children的数量作为当前swiperItem的序号
  133. this.index = this.parent.children.length
  134. this.parent && this.parent.children.push(this)
  135. },
  136. mounted() {
  137. this.$nextTick(() => {
  138. this.initSwiperItem()
  139. })
  140. },
  141. methods: {
  142. // 初始化swiperItem
  143. initSwiperItem() {
  144. this.getSwiperItemRect(() => {
  145. this.parent.updateAllSwiperItemStyle()
  146. this.parentData.swiperContainerAnimationFinish = true
  147. })
  148. },
  149. // 获取swiperItem的信息
  150. async getSwiperItemRect(callback) {
  151. const swiperItemRes = await this._tGetRect('.tn-c-swiper-item')
  152. if (!swiperItemRes.height || !swiperItemRes.width) {
  153. setTimeout(() => {
  154. this.getSwiperItemRect()
  155. }, 30)
  156. return
  157. }
  158. this.itemWidth = swiperItemRes.width
  159. this.itemHeight = swiperItemRes.height
  160. this.itemTop = swiperItemRes.top
  161. this.itemLeft = swiperItemRes.left
  162. callback && callback()
  163. },
  164. // 更新swiperItem样式
  165. updateSwiperItemStyle(swiperItemLength, currentIndex = undefined) {
  166. currentIndex = currentIndex != undefined ? currentIndex : this.parentData.currentIndex
  167. this.swiperItemLength = swiperItemLength
  168. // 根据当前swiperItem的序号设置偏移位置
  169. // 判断当前swiperItem是否为第一个,如果是则将最后的swiperItem移动到当前的前一个位置(即最前面)
  170. if (currentIndex === 0 && this.index === swiperItemLength - 1) {
  171. if (this.parentData.vertical) {
  172. this.translateX = 0
  173. this.translateY = -100
  174. } else {
  175. this.translateX = -100
  176. this.translateY = 0
  177. }
  178. }
  179. // 判断当前swiperItem是否为最后一个,如果是则将最前的swiperItem移动到当前的后一个位置(即最后面)
  180. else if (currentIndex === swiperItemLength - 1 && this.index === 0) {
  181. if (this.parentData.vertical) {
  182. this.translateX = 0
  183. this.translateY = swiperItemLength * 100
  184. } else {
  185. this.translateX = swiperItemLength * 100
  186. this.translateY = 0
  187. }
  188. }
  189. // 正常情况
  190. else {
  191. if (this.parentData.vertical) {
  192. this.translateX = 0
  193. this.translateY = this.index * 100
  194. } else {
  195. this.translateX = this.index * 100
  196. this.translateY = 0
  197. }
  198. }
  199. },
  200. // 更新父组件的偏移位置信息
  201. updateParentSwiperContainerStyle(e) {
  202. this.parent.updateSwiperContainerStyleWithValue(e.value)
  203. },
  204. // 根据方向更新父组件的偏移位置信息
  205. updateParentSwiperContainerStyleWithDirection(e) {
  206. this.parent.updateSwiperContainerStyleWithDirection(e.direction)
  207. },
  208. // 修改父组件的偏移位置的状态
  209. changeParentSwiperContainerStyleStatus(e) {
  210. // reset -> 重置 reload -> 重载
  211. this.parent.updateSwiperContainerStyleWithDirection(e.status)
  212. },
  213. // 更新父组件信息
  214. updateParentData() {
  215. this.getParentData('tn-custom-swiper')
  216. },
  217. // 更新触摸状态
  218. updateTouchingStatus(e) {
  219. this.touching = e.status
  220. if (e.status) {
  221. this.parent.stopAutoPlay()
  222. } else {
  223. this.parent.startAutoPlay()
  224. }
  225. },
  226. // 提取对应用户自定义样式
  227. extractCustomStyle(customStyle) {
  228. let data = {
  229. transform: {},
  230. style: {}
  231. }
  232. if (!customStyle) return data
  233. // 允许设置的transform参数
  234. const allowTransformProps = ['scale','scaleX','scaleY','scaleZ','rotate','rotateX','rotateY','rotateZ']
  235. for (let prop in customStyle) {
  236. if (prop.startsWith('transformProp')) {
  237. // transform里面的样式
  238. let transformProp = prop.substring('transformProp'.length)
  239. const index = allowTransformProps.findIndex((item) => {
  240. return item.toLowerCase() === transformProp.toLowerCase()
  241. })
  242. if (index !== -1) {
  243. transformProp = allowTransformProps[index]
  244. data.transform[transformProp] = customStyle[prop]
  245. }
  246. } else {
  247. // 普通样式
  248. data.style[prop] = customStyle[prop]
  249. }
  250. }
  251. return data
  252. }
  253. }
  254. }
  255. </script>
  256. <style lang="scss" scoped>
  257. .tn-c-swiper-item {
  258. width: 100%;
  259. height: 100%;
  260. position: absolute;
  261. display: block;
  262. will-change: transform;
  263. cursor: none;
  264. transform: translate3d(0px, 0px, 0px);
  265. .item__container {
  266. width: 100%;
  267. height: 100%;
  268. display: block;
  269. position: absolute;
  270. }
  271. }
  272. </style>