liuyuno-tabs.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template>
  2. <view class="_tab-box" :style="{fontSize: defaultConfig.fontSize + 'rpx', color: defaultConfig.color}">
  3. <scroll-view id="_scroll" :scroll-x="true" class="scroll-view-h" scroll-with-animation :scroll-left="slider.scrollLeft">
  4. <view class="_scroll-content">
  5. <view class="_tab-item-box" :class="[defaultConfig.itemWidth ? '_clamp' : '_flex']">
  6. <block v-for="(item, index) in tabList" :key="index" >
  7. <view
  8. class="_item"
  9. :id="'_tab_'+index"
  10. :class="{ '_active': tagIndex === index }"
  11. :style="{color: tagIndex == index ? defaultConfig.activeColor : defaultConfig.color, 'width': defaultConfig.itemWidth ? defaultConfig.itemWidth + 'rpx' : ''}"
  12. @click="tabClick(index)">{{ item[defaultConfig.key] || item }}</view>
  13. </block>
  14. </view>
  15. <view class="_underline" :style="{
  16. transform: 'translateX(' + slider.left + 'px)',
  17. width: slider.width + 'px',
  18. height: defaultConfig.underLineHeight + 'rpx',
  19. backgroundColor: defaultConfig.underLineColor,
  20. }" />
  21. </view>
  22. </scroll-view>
  23. </view>
  24. </template>
  25. <script>
  26. export default {
  27. name: 'liuyuno-tabs',
  28. props: {
  29. tabData: {
  30. type: Array,
  31. default: () => []
  32. },
  33. activeIndex: {
  34. type: Number,
  35. default: 0
  36. },
  37. config: {
  38. type: Object,
  39. default:() => {
  40. return {}
  41. }
  42. },
  43. },
  44. data() {
  45. return {
  46. tabList: [],
  47. tagIndex: 0,
  48. slider: {
  49. left: 0,
  50. width: 0,
  51. scrollLeft: 0
  52. },
  53. scorll: {},
  54. defaultConfig: {
  55. // 要显示的key
  56. key: 'name',
  57. // 字体大小 rpx
  58. fontSize: 34,
  59. // 字体颜色
  60. color: '#313131',
  61. // 激活字体颜色
  62. activeColor: '#3D93D0',
  63. // item宽度 0为自动
  64. itemWidth: 0,
  65. // 下划线左右边距,文字宽度加边距 rpx
  66. underLinePadding: 10,
  67. // 下划线宽度 rpx 注意:设置了此值 underLinePadding 失效
  68. underLineWidth: 0,
  69. // 下划线高度 rpx
  70. underLineHeight: 4,
  71. // 下划线颜色
  72. underLineColor: '#3D93D0',
  73. },
  74. };
  75. },
  76. watch: {
  77. tabData(value) {
  78. this.updateData();
  79. setTimeout(() => {
  80. this.updateTabWidth();
  81. }, 0);
  82. },
  83. config(value) {
  84. this.updateConfig();
  85. },
  86. },
  87. mounted() {
  88. this.updateConfig();
  89. this.updateData();
  90. this.tagIndex = this.activeIndex;
  91. this.$nextTick(() => {
  92. this.calcScrollPosition();
  93. })
  94. },
  95. methods: {
  96. updateData() {
  97. let data = [];
  98. if (typeof(this.tabData[0])=='string') {
  99. this.tabData.forEach((item, index) => {
  100. data.push({
  101. name: item,
  102. })
  103. });
  104. this.defaultConfig.key = 'name';
  105. } else {
  106. data = JSON.parse(JSON.stringify(this.tabData));
  107. }
  108. this.tabList = data;
  109. },
  110. updateConfig() {
  111. this.defaultConfig = Object.assign(this.defaultConfig, this.config);
  112. },
  113. calcScrollPosition() {
  114. const query = uni.createSelectorQuery().in(this);
  115. query.select('#_scroll').boundingClientRect((res) => {
  116. this.scorll = res;
  117. this.updateTabWidth();
  118. }).exec();
  119. },
  120. updateTabWidth(index = 0) {
  121. let data = this.tabList;
  122. if (data.length == 0) return false;
  123. const query = uni.createSelectorQuery().in(this);
  124. query.select('#_tab_' + index).boundingClientRect((res) => {
  125. data[index]._slider = {
  126. width: res.width,
  127. left: res.left,
  128. scrollLeft: res.left - (data[index - 1] ? data[index - 1]._slider.width : 0),
  129. };
  130. if (this.tagIndex == index) {
  131. this.tabToIndex(this.tagIndex);
  132. }
  133. index++;
  134. if (data.length > index) {
  135. this.updateTabWidth(index);
  136. }
  137. }).exec();
  138. },
  139. tabToIndex(index) {
  140. let _slider = this.tabList[index]._slider;
  141. let width = uni.upx2px(this.defaultConfig.underLineWidth);
  142. if (!width) {
  143. if (this.defaultConfig.itemWidth) {
  144. width = uni.upx2px(this.defaultConfig.itemWidth);
  145. } else {
  146. width = this.tabList[index][this.defaultConfig.key].length * uni.upx2px(this.defaultConfig.fontSize);
  147. }
  148. width += uni.upx2px(this.defaultConfig.underLinePadding) * 2;
  149. }
  150. let scorll_left = this.scorll.left || 0;
  151. this.slider = {
  152. left: _slider.left - scorll_left + (_slider.width - width) / 2,
  153. width: width,
  154. scrollLeft: _slider.scrollLeft - scorll_left,
  155. }
  156. },
  157. tabClick(index) {
  158. this.tagIndex = index;
  159. this.tabToIndex(index);
  160. this.$emit('tabClick', index);
  161. }
  162. }
  163. }
  164. </script>
  165. <style lang="scss" scoped>
  166. ._tab-box {
  167. width: 100%;
  168. display: flex;
  169. font-size: 26rpx;
  170. position: relative;
  171. height: 90rpx;
  172. line-height: 90rpx;
  173. z-index: 10;
  174. .scroll-view-h{
  175. white-space:nowrap;
  176. width: 100%;
  177. height: 100%;
  178. box-sizing: border-box;
  179. ._scroll-content {
  180. width: 100%;
  181. height: 100%;
  182. position:relative;
  183. ._tab-item-box {
  184. height: 100%;
  185. &._flex {
  186. display: flex;
  187. ._item {
  188. flex: 1;
  189. }
  190. }
  191. &._clamp {
  192. ._item {
  193. overflow:hidden;
  194. text-overflow:ellipsis;
  195. white-space:nowrap;
  196. }
  197. }
  198. ._item {
  199. height: 100%;
  200. display: inline-block;
  201. text-align: center;
  202. padding: 0 30rpx;
  203. position: relative;
  204. text-align: center;
  205. color: #333;
  206. &._active {
  207. color: #e54d42;
  208. }
  209. }
  210. }
  211. ._underline {
  212. height: 4rpx;
  213. background-color: #e54d42;
  214. border-radius: 6rpx;
  215. transition: .5s;
  216. position: absolute;
  217. bottom: 0;
  218. }
  219. }
  220. }
  221. }
  222. </style>