liuyuno-tabs.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <template>
  2. <view class="tab-box" id="tab-box" v-if="tabList.length > 0">
  3. <view class="horizontal">
  4. <scroll-view :scroll-x="true" style="white-space: nowrap; display: flex;" scroll-with-animation
  5. :scroll-left="slider.scrollLeft">
  6. <view class="flex-y-center">
  7. <block v-for="(item, index) in tabList" :key="index">
  8. <view class="flex-grow-1 flex-center">
  9. <view class="item" :class="{ active: activeIndex === index }" :id="'tab_'+index"
  10. @click="tabClick(index)">{{ item.text || item }}
  11. </view>
  12. </view>
  13. </block>
  14. </view>
  15. <view class="underline" :style="'transform:translateX(' + slider.left + 'px);width:' + slider.width + 'px'"></view>
  16. </scroll-view>
  17. </view>
  18. </view>
  19. </template>
  20. <script>
  21. export default {
  22. name: 'liuyuno-tabs',
  23. props: {
  24. tabData: {
  25. type: Array,
  26. default: () => []
  27. },
  28. defaultIndex: {
  29. type: Number,
  30. default: 0
  31. },
  32. underLinePadding: {
  33. type: Number,
  34. default: 10
  35. },
  36. },
  37. data() {
  38. return {
  39. tabList: [],
  40. tabListSlider: [],
  41. box: {
  42. left: 0,
  43. right: 0,
  44. top: 0,
  45. width: 0,
  46. height: 0,
  47. bottom: 0,
  48. },
  49. slider: {
  50. left: 0,
  51. width: 0,
  52. scrollLeft: 0
  53. },
  54. activeIndex: 0
  55. };
  56. },
  57. watch: {
  58. tabData(value) {
  59. this.tabList = value;
  60. setTimeout(() => {
  61. this.updateTabWidth();
  62. }, 500);
  63. },
  64. },
  65. mounted() {
  66. this.tabList = this.tabData;
  67. this.activeIndex = this.defaultIndex;
  68. setTimeout(() => {
  69. const query = uni.createSelectorQuery().in(this);
  70. query.select('.tab-box').boundingClientRect((res) => {
  71. console.log(res)
  72. this.box = res;
  73. this.updateTabWidth();
  74. }).exec();
  75. }, 500);
  76. },
  77. methods: {
  78. tabClick(index) {
  79. this.activeIndex = index;
  80. this.tabToIndex(index);
  81. this.$emit('tabClick', index);
  82. },
  83. tabToIndex(index) {
  84. let _slider = this.tabListSlider[index];
  85. this.slider = {
  86. left: _slider.left + (_slider.width / 2) - (_slider.width / 4),
  87. width: (_slider.width) / 2,
  88. scrollLeft: _slider.scrollLeft,
  89. // left: _slider.left + this.underLinePadding,
  90. // width: _slider.width - this.underLinePadding * 2,
  91. // scrollLeft: _slider.scrollLeft,
  92. }
  93. },
  94. updateTabWidth(index = 0) {
  95. let data = this.tabList;
  96. if (data.length == 0) return false;
  97. const query = uni.createSelectorQuery().in(this);
  98. query.select('#tab_' + index).boundingClientRect((res) => {
  99. let _prev_slider = this.tabListSlider[index - 1];
  100. this.tabListSlider[index] = {
  101. left: res.left - this.box.left,
  102. width: res.width,
  103. scrollLeft: res.left - this.box.left - (_prev_slider ? _prev_slider.width : 0),
  104. }
  105. if (this.activeIndex == index) {
  106. this.tabToIndex(this.activeIndex);
  107. }
  108. index++;
  109. if (data.length > index) {
  110. this.updateTabWidth(index);
  111. }
  112. }).exec();
  113. }
  114. }
  115. }
  116. </script>
  117. <style lang="scss">
  118. .tab-box {
  119. width: 100%;
  120. color: rgba(0, 0, 0, 0.8);
  121. display: flex;
  122. height:100rpx;
  123. background: #fff;
  124. font-size: 28upx;
  125. box-shadow: 0 1px 5px rgba(0, 0, 0, 0.06);
  126. position: relative;
  127. z-index: 10;
  128. overflow: hidden;
  129. }
  130. .horizontal .active {
  131. color: #000;
  132. }
  133. .horizontal {
  134. width: 100%;
  135. }
  136. .wd33 {
  137. width: 33%;
  138. }
  139. .item {
  140. display: inline-block;
  141. text-align: center;
  142. padding: 0 30upx;
  143. height: 80rpx;
  144. line-height: 90upx;
  145. font-size: 32rpx;
  146. color: #A0A0A0;
  147. }
  148. .underline {
  149. height: 4upx;
  150. background-color:#000;
  151. border-radius: 3px;
  152. transition: .5s;
  153. }
  154. </style>