swiper-card.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. <template>
  2. <view class="container" :style="{width:`${containerWidth}rpx`,height:`${height}rpx`}" @touchstart="touchstart"
  3. @touchend="touchend">
  4. <view :class="['container-track_image',`trackImage-${index-1}`]" v-for="index in count" :key="index"
  5. @click="change(index-1)"
  6. :style="{left:(containerWidth - width) / 2 + 'rpx',width:`${width}rpx`,height:`${height}rpx`,...animations[index-1] || {}}"
  7. :ref="`trackImage-${index-1}`">
  8. <slot :name="`card-${index-1}`"></slot>
  9. </view>
  10. </view>
  11. </template>
  12. <script>
  13. // #ifdef APP-NVUE
  14. const animationModule = weex.requireModule('animation')
  15. // #endif
  16. export default {
  17. data() {
  18. return {
  19. currentIndex: 1, // 当前显示的卡片索引
  20. startX: 0,
  21. endX: 0,
  22. isClick: false,
  23. animations: [],
  24. };
  25. },
  26. props: {
  27. containerWidth: {
  28. type: Number,
  29. default: 750
  30. },
  31. width: {
  32. type: Number,
  33. default: 128
  34. },
  35. height: {
  36. type: Number,
  37. default: 140
  38. },
  39. offsetStep: {
  40. type: Number,
  41. default: -15
  42. },
  43. scaleStep: {
  44. type: Number,
  45. default: 0.9
  46. },
  47. count: {
  48. type: Number,
  49. default: 3
  50. }
  51. },
  52. mounted() {
  53. this.$nextTick(() => {
  54. this.layout()
  55. })
  56. },
  57. methods: {
  58. change(index) {
  59. if (!this.isClick) return;
  60. if (this.currentIndex == index) return;
  61. this.currentIndex = index
  62. this.layout(500)
  63. },
  64. touchstart(e) {
  65. this.startX = e.touches[0].pageX;
  66. },
  67. touchend(e) {
  68. this.endX = e.changedTouches[0].pageX
  69. if (this.startX > this.endX) {
  70. this.currentIndex++
  71. } else if (this.startX < this.endX) {
  72. this.currentIndex--
  73. } else {
  74. this.isClick = true
  75. }
  76. if (this.currentIndex > this.count - 1) {
  77. this.currentIndex = this.count - 1;
  78. } else if (this.currentIndex < 0) {
  79. this.currentIndex = 0;
  80. }
  81. this.layout(500)
  82. },
  83. layout(duration) {
  84. for (let i = 0; i < this.count; i++) {
  85. // #ifdef APP-NVUE
  86. const dom = this.$refs[`trackImage-${i}`][0]
  87. // #endif
  88. // #ifndef APP-NVUE
  89. const dom = `trackImage-${i}`
  90. // #endif
  91. const dis = Math.abs(i - this.currentIndex);
  92. const xOffset = (i - this.currentIndex) * (this.offsetStep + this.width);
  93. const scale = this.scaleStep ** dis;
  94. const active = {
  95. dom,
  96. styles: {
  97. transform: `scale(${scale}) translateX(${xOffset/2}px)`
  98. },
  99. transformOrigin: 'center center',
  100. timingFunction: 'ease-out',
  101. duration,
  102. }
  103. this.animationActive(active, i)
  104. }
  105. },
  106. animationActive(active, i) {
  107. // #ifdef APP-NVUE
  108. return new Promise((resolve, reject) => {
  109. animationModule.transition(active.dom, {
  110. styles: active.styles || {},
  111. duration: active.duration,
  112. timingFunction: active.timingFunction,
  113. delay: active.delay || 0,
  114. transformOrigin: active.transformOrigin,
  115. }, () => {
  116. resolve()
  117. })
  118. })
  119. // #endif
  120. // #ifndef APP-NVUE
  121. this.$set(this.animations,i,active.styles)
  122. // #endif
  123. },
  124. },
  125. };
  126. </script>
  127. <style scoped>
  128. /* 外部容器,设置宽高 */
  129. .container {
  130. position: relative;
  131. }
  132. .container-track_image {
  133. position: absolute;
  134. transition: 0.5s ease;
  135. transform-origin: center center;
  136. }
  137. </style>