tn-stack-swiper.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <template>
  2. <view
  3. class="tn-stack-swiper-class tn-stack-swiper"
  4. :style="{
  5. width: $t.string.getLengthUnitValue(width),
  6. height: $t.string.getLengthUnitValue(height)
  7. }"
  8. :list="swiperList"
  9. :itemData="itemData"
  10. :currentIndex="swiperIndex"
  11. :autoplayFlag="autoplayFlag"
  12. :change:list="wxs.listObserver"
  13. :change:itemData="wxs.itemDataObserver"
  14. :change:currentIndex="wxs.swiperIndexChange"
  15. :change:autoplayFlag="wxs.autoplayFlagChange"
  16. >
  17. <block v-for="(item, index) in list" :key="index">
  18. <!-- #ifdef MP-WEIXIN -->
  19. <view
  20. class="tn-stack-swiper__item tn-stack-swiper__item__transition"
  21. :class="[`tn-stack-swiper__item--${direction}`]"
  22. :data-index="index"
  23. :data-switchRate="switchRate"
  24. @touchstart="wxs.touchStart"
  25. :catch:touchmove="touching?wxs.touchMove:''"
  26. :catch:touchend="touching?wxs.touchEnd:''"
  27. >
  28. <image class="tn-stack-swiper__image" :src="item.image"></image>
  29. </view>
  30. <!-- #endif -->
  31. <!-- #ifndef MP-WEIXIN -->
  32. <view
  33. class="tn-stack-swiper__item"
  34. :class="[`tn-stack-swiper__item--${direction}`]"
  35. :data-index="index"
  36. :data-switchRate="switchRate"
  37. @touchstart="wxs.touchStart"
  38. @touchmove="wxs.touchMove"
  39. @touchend="wxs.touchEnd"
  40. >
  41. <image class="tn-stack-swiper__image" :src="item.image"></image>
  42. </view>
  43. <!-- #endif -->
  44. </block>
  45. </view>
  46. </template>
  47. <!-- #ifdef MP-WEIXIN -->
  48. <script src="./index.wxs" lang="wxs" module="wxs"></script>
  49. <!-- #endif -->
  50. <!-- #ifndef MP-WEIXIN -->
  51. <script src="./index-h5.wxs" lang="wxs" module="wxs"></script>
  52. <!-- #endif -->
  53. <script>
  54. export default {
  55. name: 'tn-stack-swiper',
  56. props: {
  57. // 显示图片的列表数据
  58. // {
  59. // // 图片地址
  60. // image: 'xxx'
  61. // }
  62. list: {
  63. type: Array,
  64. default() {
  65. return []
  66. }
  67. },
  68. // 轮播容器的宽度 rpx
  69. width: {
  70. type: [String, Number],
  71. default: '100%'
  72. },
  73. // 轮播容器的高度 rpx
  74. height: {
  75. type: [String, Number],
  76. default: 500
  77. },
  78. // 自动切换
  79. autoplay: {
  80. type: Boolean,
  81. default: false
  82. },
  83. // 自动切换时长 ms
  84. interval: {
  85. type: Number,
  86. default: 5000
  87. },
  88. // 滑动切换移动比例, [0 - 100]
  89. // 比例相对于item的宽度
  90. switchRate: {
  91. type: Number,
  92. default: 30
  93. },
  94. // 缩放比例 [0-1]
  95. scaleRate: {
  96. type: Number,
  97. default: 0.1
  98. },
  99. // 下一轮播偏移比例
  100. translateRate: {
  101. type: Number,
  102. default: 16
  103. },
  104. // 下一轮播透明比例
  105. opacityRate: {
  106. type: Number,
  107. default:10
  108. },
  109. // 滑动方向
  110. // horizontal -> 水平 vertical -> 垂直
  111. direction: {
  112. type: String,
  113. default: 'horizontal'
  114. }
  115. },
  116. data() {
  117. return {
  118. autoplayTimer: null,
  119. // window窗口的宽度
  120. windowWidth: 0,
  121. // 轮播item的宽度
  122. swiperItemWidth: 0,
  123. // 轮播item的高度
  124. swiperItemHeight: 0,
  125. // 当前选中的轮播item
  126. swiperIndex: -1,
  127. // 标记是否开始触摸
  128. touching: true,
  129. // 轮播列表信息
  130. swiperList: [],
  131. // 标记当前是否为自动播放
  132. autoplayFlag: false
  133. }
  134. },
  135. computed: {
  136. itemData() {
  137. return {
  138. windowWidth: this.windowWidth,
  139. itemWidth: this.swiperItemWidth,
  140. itemHeight: this.swiperItemHeight,
  141. direction: this.direction,
  142. autoplaying: this.autoplayFlag
  143. }
  144. }
  145. },
  146. watch: {
  147. list(val) {
  148. this.swiperList = []
  149. this.$nextTick(() => {
  150. this.initSwiperRectInfo()
  151. })
  152. },
  153. autoplay(val) {
  154. if (!val) {
  155. this.clearAutoPlayTimer()
  156. } else {
  157. this.setAutoPlay()
  158. }
  159. }
  160. },
  161. created() {
  162. this.autoplayFlag = this.autoplay
  163. },
  164. mounted() {
  165. this.$nextTick(() => {
  166. this.initSwiperRectInfo()
  167. })
  168. },
  169. destroyed() {
  170. this.clearAutoPlayTimer()
  171. },
  172. methods: {
  173. // 初始化轮播容器信息
  174. async initSwiperRectInfo() {
  175. // 用于一开始绑定事件
  176. // this.touching = true
  177. // 获取轮播item的宽度
  178. const swiperItemRect = await this._tGetRect('.tn-stack-swiper__item')
  179. if (!swiperItemRect || !swiperItemRect.width || !swiperItemRect.height) {
  180. setTimeout(() => {
  181. this.initSwiperRectInfo()
  182. }, 50)
  183. return
  184. }
  185. this.swiperItemWidth = swiperItemRect.width
  186. this.swiperItemHeight = swiperItemRect.height
  187. // this.touching = false
  188. // 获取系统的窗口宽度信息
  189. const systemInfo = uni.getSystemInfoSync()
  190. this.windowWidth = systemInfo.windowWidth
  191. this.swiperIndex = 0
  192. // 设置对应swiper元素的位置和层级信息
  193. this.swiperList = this.list.map((item, index) => {
  194. const scale = 1 - (this.scaleRate * index)
  195. if (this.direction === 'horizontal') {
  196. item.translateX = ((index * this.translateRate) * 0.01 * this.swiperItemWidth)
  197. } else if (this.direction === 'vertical') {
  198. item.translateY = ((index * this.translateRate) * 0.01 * this.swiperItemHeight)
  199. }
  200. item.opacity = (1 - ((index * this.opacityRate) * 0.01))
  201. item.zIndex = this.list.length - index
  202. item.scale = scale <= 0 ? 0 : scale
  203. return item
  204. })
  205. this.setAutoPlay()
  206. },
  207. // 设置自动切换轮播
  208. setAutoPlay() {
  209. if (this.autoplay) {
  210. this.clearAutoPlayTimer()
  211. this.autoplayFlag = true
  212. this.autoplayTimer = setInterval(() => {
  213. this.swiperIndex = this.swiperIndex + 1 > this.swiperList.length - 1 ? 0 : this.swiperIndex + 1
  214. }, this.interval)
  215. }
  216. },
  217. // 清除自动切换定时器
  218. clearAutoPlayTimer() {
  219. if (this.autoplayTimer != null) {
  220. this.autoplayFlag = false
  221. clearInterval(this.autoplayTimer)
  222. }
  223. },
  224. // 修改轮播选中index
  225. changeSwiperIndex(e) {
  226. // console.log(e.index);
  227. this.swiperIndex = e.index
  228. this.$emit('change', { index: e.index })
  229. },
  230. // 修改触摸状态
  231. changeTouchState(e) {
  232. this.touching = e.touching
  233. },
  234. // 打印日志
  235. printLog(data) {
  236. console.log("log", data);
  237. }
  238. }
  239. }
  240. </script>
  241. <style lang="scss" scoped>
  242. .tn-stack-swiper {
  243. position: relative;
  244. &__item {
  245. position: absolute;
  246. border-radius: 20rpx;
  247. overflow: hidden;
  248. &--horizontal {
  249. width: 88%;
  250. height: 100%;
  251. transform-origin: left center;
  252. }
  253. &--vertical {
  254. width: 100%;
  255. height: 88%;
  256. transform-origin: top center;
  257. }
  258. &__transition {
  259. transition-property: transform,opacity;
  260. transition-duration: 0.25s;
  261. transition-timing-function: ease-out;
  262. // transition: transform, opacity 0.25s ease-in-out !important;
  263. }
  264. }
  265. &__image {
  266. width: 100%;
  267. height: 100%;
  268. }
  269. }
  270. </style>