y-Tabs.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <template>
  2. <view class="tabBlock" v-if="list.length > 0">
  3. <scroll-view scroll-x="true" scroll-with-animation :scroll-left="tabsScrollLeft" @scroll="scroll">
  4. <view class="tab" id="tab_list">
  5. <view v-for="(item, index) in list" :key="index" :class="['tab__item', {'tab__item--active': currentIndex === index}]"
  6. :style="{color: (currentIndex === index ? `${itemColor}`: '')}" id="tab_item" @click="select(item, index)">
  7. <view :class="['tab__item-title', {'tab__item-title--active': currentIndex === index}]">
  8. <text class="tab_item_t">{{item.activityTime}}</text>
  9. <text class="tab_item_c">{{item.title}}</text>
  10. </view>
  11. </view>
  12. </view>
  13. <view class="tab__line" v-if="lineAnimated" :style="{background: lineColor, width: lineStyle.width, transform: lineStyle.transform,transitionDuration: lineStyle.transitionDuration}">
  14. </view>
  15. </scroll-view>
  16. </view>
  17. </template>
  18. <script>
  19. export default {
  20. props: {
  21. value: [Number, String],
  22. list: { // 传值
  23. type: Array,
  24. default: () => {
  25. return []
  26. }
  27. },
  28. itemColor: String, // tab主色调
  29. lineColor: String, // 下划线主色调
  30. lineAnimated: { // 是否展示下划线动画
  31. type: Boolean,
  32. default: true
  33. }
  34. },
  35. data() {
  36. return {
  37. currentIndex: 0,
  38. lineStyle: {},
  39. scrollLeft: 0,
  40. tabsScrollLeft: 0,
  41. duration: 0.3
  42. }
  43. },
  44. watch: {
  45. list() {
  46. this.setTabList()
  47. },
  48. value() {
  49. this.currentIndex = this.value
  50. this.setTabList()
  51. }
  52. },
  53. mounted() {
  54. this.currentIndex = this.value
  55. this.setTabList()
  56. if (!this.lineAnimated) {
  57. this.duration = 0
  58. }
  59. },
  60. methods: {
  61. select(item, index) {
  62. this.$emit('input', index)
  63. },
  64. setTabList() {
  65. this.$nextTick(() => {
  66. if (this.list.length > 0) {
  67. this.setLine()
  68. this.scrollIntoView()
  69. }
  70. })
  71. },
  72. setLine() {
  73. let lineWidth = 0,
  74. lineLeft = 0
  75. this.getElementData(`#tab_item`, (data) => {
  76. let el = data[this.currentIndex]
  77. lineWidth = el.width / 2
  78. // lineLeft = el.width * (this.currentIndex + 0.5) // 此种只能针对每个item长度一致的
  79. lineLeft = el.width / 2 + (-data[0].left) + el.left
  80. this.lineStyle = {
  81. width: `${lineWidth}px`,
  82. transform: `translateX(${lineLeft}px) translateX(-50%)`,
  83. transitionDuration: `${this.duration}s`
  84. };
  85. })
  86. },
  87. scrollIntoView() { // item滚动
  88. let lineLeft = 0;
  89. this.getElementData('#tab_list', (data) => {
  90. let list = data[0]
  91. this.getElementData(`#tab_item`, (data) => {
  92. let el = data[this.currentIndex]
  93. // lineLeft = el.width * (this.currentIndex + 0.5) - list.width / 2 - this.scrollLeft
  94. lineLeft = el.width / 2 + (-list.left) + el.left - list.width / 2 - this.scrollLeft
  95. this.tabsScrollLeft = this.scrollLeft + lineLeft
  96. })
  97. })
  98. },
  99. getElementData(el, callback) {
  100. uni.createSelectorQuery().in(this).selectAll(el).boundingClientRect().exec((data) => {
  101. callback(data[0]);
  102. });
  103. },
  104. scroll(e) {
  105. this.scrollLeft = e.detail.scrollLeft;
  106. }
  107. }
  108. }
  109. </script>
  110. <style lang="scss">
  111. .tabBlock {
  112. position: relative;
  113. background: #020824;
  114. .tab {
  115. position: relative;
  116. display: flex;
  117. font-size: 28rpx;
  118. padding-bottom: 15rpx;
  119. white-space: nowrap;
  120. &__item {
  121. flex: 1;
  122. text-align: center;
  123. line-height: 90rpx;
  124. color: $uni-text-color;
  125. margin-right: 14rpx;
  126. &--active {
  127. color: $uni-color-primary;
  128. }
  129. &-title {
  130. width: 100%;
  131. height: 100rpx;
  132. display: flex;
  133. flex-direction: column;
  134. justify-content: center;
  135. align-items: center;
  136. background: #131B46;
  137. border-radius: 6px;
  138. text {
  139. display: block;
  140. }
  141. .tab_item_t {
  142. font-family: AlibabaPuHuiTiH;
  143. font-size: 14px;
  144. color: #00E78D;
  145. letter-spacing: 0;
  146. text-align: center;
  147. line-height: 20px;
  148. font-weight: 600;
  149. }
  150. .tab_item_c {
  151. font-family: AlibabaPuHuiTiM;
  152. font-size: 12px;
  153. color: #00E78D;
  154. letter-spacing: 0;
  155. text-align: center;
  156. line-height: 21px;
  157. }
  158. }
  159. .tab__item-title--active {
  160. width: 100%;
  161. height: 100rpx;
  162. display: flex;
  163. flex-direction: column;
  164. justify-content: center;
  165. align-items: center;
  166. background: #00E78D;
  167. border-radius: 6px;
  168. .tab_item_t {
  169. font-family: AlibabaPuHuiTiH;
  170. font-weight: 600;
  171. font-size: 16px;
  172. color: #FFFFFF;
  173. letter-spacing: 0;
  174. text-align: center;
  175. line-height: 20px;
  176. }
  177. .tab_item_c {
  178. font-family: AlibabaPuHuiTiM;
  179. font-size: 14px;
  180. color: #FFFFFF;
  181. letter-spacing: 0;
  182. text-align: center;
  183. line-height: 21px;
  184. }
  185. }
  186. }
  187. &__item:last-child {
  188. margin-right: 0;
  189. }
  190. }
  191. .tab__line {
  192. display: block;
  193. height: 6rpx;
  194. position: absolute;
  195. bottom: 15rpx;
  196. left: 0;
  197. z-index: 1;
  198. border-radius: 3rpx;
  199. position: relative;
  200. background: $uni-color-primary;
  201. }
  202. }
  203. </style>