<template>
  <view class="tn-tabs-swiper-class tn-tabs-swiper" :class="[backgroundColorClass]" :style="{backgroundColor: backgroundColorStyle, marginTop: $tn.string.getLengthUnitValue(top, 'px'), zIndex: zIndex}">
    
    <scroll-view scroll-x class="tn-tabs-swiper__scroll-view" :scroll-left="scrollLeft" scroll-with-animation :style="{zIndex: zIndex + 1}">
      <view class="tn-tabs-swiper__scroll-view__box" :class="{'tn-tabs-swiper__scroll-view--flex': !isScroll}">
        
        <!-- item -->
        <view
          v-for="(item, index) in list"
          :key="index"
          :id="'tn-tabs-swiper__scroll-view__item-' + index"
          class="tn-tabs-swiper__scroll-view__item tn-text-ellipsis"
          :style="[tabItemStyle(index)]"
          @tap="emit(index)"
        >
          <tn-badge v-if="item[count] || item['count']" backgroundColor="tn-bg-red" fontColor="#FFFFFF" :absolute="true" :top="badgeOffset[0] || 0" :right="badgeOffset[1] || 0">{{ item[count] || item['count']}}</tn-badge>
          {{ item[name] || item['name'] }}
        </view>
        
        <!-- 底部滑块 -->
        <view v-if="showBar" class="tn-tabs-swiper__bar" :style="[tabBarStyle]"></view>
      </view>
    </scroll-view>
  </view>
</template>

<script>
  import componentsColor from '../../libs/mixin/components_color.js'
  const { windowWidth } = uni.getSystemInfoSync()
  
  export default {
    mixins: [componentsColor],
    name: 'tn-tabs-swiper',
    props: {
      // 标签列表
      list: {
        type: Array,
        default() {
          return []
        }
      },
      // 列表数据tab名称的属性
      name: {
        type: String,
        default: 'name'
      },
      // 列表数据微标数量的属性
      count: {
        type: String,
        default: 'count'
      },
      // 当前活动的tab索引
      current: {
        type: Number,
        default: 0
      },
      // 菜单是否可以滑动
      isScroll: {
        type: Boolean,
        default: true
      },
      // 高度
      height: {
        type: Number,
        default: 80
      },
      // 距离顶部的距离(px)
      top: {
        type: Number,
        default: 0
      },
      // item的高度
      itemWidth: {
        type: [String, Number],
        default: 'auto'
      },
      // swiper的宽度
      swiperWidth: {
        type: Number,
        default: 750
      },
      // 选中时的颜色
      activeColor: {
        type: String,
        default: '#01BEFF'
      },
      // 未被选中时的颜色
      inactiveColor: {
        type: String,
        default: '#080808'
      },
      // 选中的item样式
      activeItemStyle: {
        type: Object,
        default() {
          return {}
        }
      },
      // 是否显示底部滑块
      showBar: {
        type: Boolean,
        default: true
      },
      // 底部滑块的宽度
      barWidth: {
        type: Number,
        default: 40
      },
      // 底部滑块的高度
      barHeight: {
        type: Number,
        default: 6
      },
      // 自定义底部滑块的样式
      barStyle: {
        type: Object,
        default() {
          return {}
        }
      },
      // 单个tab的左右内边距
      gutter: {
        type: Number,
        default: 30
      },
      // 微标的偏移数[top, right]
      badgeOffset: {
        type: Array,
        default() {
          return [20, 22]
        }
      },
      // 是否加粗字体
      bold: {
        type: Boolean,
        default: false
      },
      // 滚动至中心目标类型
      autoCenterMode: {
        type: String,
        default: 'window'
      },
      zIndex: {
        type: Number,
        default: 1
      }
    },
    computed: {
      currentIndex() {
        const current = Number(this.current)
        // 判断是否超出
        if (current > this.list.length - 1) {
          return this.list.length - 1
        }
        if (current < 0) return 0
        return current
      },
      // 滑块需要移动的距离
      scrollBarLeft() {
        return Number(this.tabLineDx) + Number(this.tabLineAddDx)
      },
      // 滑块宽度转换为px
      barWidthPx() {
        return uni.upx2px(this.barWidth)
      },
      // 将swiper宽度转换为px
      swiperWidthPx() {
        return uni.upx2px(this.swiperWidth)
      },
      // tab样式
      tabItemStyle() {
        return index => {
          let style = {
            height: this.$tn.string.getLengthUnitValue(this.height),
            lineHeight: this.$tn.string.getLengthUnitValue(this.height),
            fontSize: this.fontSizeStyle || '28rpx',
            color: this.tabsInfo.length > 0 ? (this.tabsInfo[index] ? this.tabsInfo[index].color : this.activeColor) : this.inactiveColor,
            padding: this.isScroll ? `0 ${this.gutter}rpx` : '',
            flex: this.isScroll ? 'auto' : '1',
            zIndex: this.zIndex + 2
          }
          if (index === this.currentIndex) {
            if (this.bold) {
              style.fontWeight = 'bold'
            }
            Object.assign(style, this.activeItemStyle)
          }
          return style
        }
      },
      // 底部滑块样式
      tabBarStyle() {
        let style = {
          width: this.$tn.string.getLengthUnitValue(this.barWidth),
          height: this.$tn.string.getLengthUnitValue(this.barHeight),
          borderRadius: `${this.barHeight / 2}rpx`,
          backgroundColor: this.activeColor,
          left: this.scrollBarLeft + 'px'
        }
        Object.assign(style, this.barStyle)
        return style
      },
    },
    data() {
      return {
        // 滚动scroll-view的左边滚动距离
        scrollLeft: 0,
        // 存放tab菜单节点信息
        tabsInfo: [],
        // 屏幕宽度
        windowWidth: 0,
        // 滑动动画结束后对应的标签Index
        animationFinishCurrent: this.current,
        // 组件的宽度
        componentsWidth: 0,
        // 移动距离
        tabLineAddDx: 0,
        tabLineDx: 0,
        // 颜色渐变数组
        colorGradientArr: [],
        // 两个颜色之间的渐变等分
        colorStep: 100,
      }
    },
    watch: {
      current(value) {
        this.change(value)
        this.setFinishCurrent(value)
      },
      list() {
        this.$nextTick(() => {
          this.init()
        })
      }
    },
    mounted() {
      this.init()
    },
    methods: {
      // 初始化
      async init() {
        await this.getTabsInfo()
        this.countLine3Dx()
        this.getQuery(() => {
          this.setScrollViewToCenter()
        })
        // 获取渐变颜色数组
        this.colorGradientArr = this.$tn.color.colorGradient(this.inactiveColor, this.activeColor, this.colorStep)
      },
      // 发送事件
      emit(index) {
        this.$emit('change', index)
      },
      // tabs发生变化
      change() {
        this.setScrollViewToCenter()
      },
      // 获取各个tab的节点信息
      getTabsInfo() {
        return new Promise((resolve, reject) => {
          let view = uni.createSelectorQuery().in(this)
          for (let i = 0; i < this.list.length; i++) {
            view.select('#tn-tabs-swiper__scroll-view__item-'+i).boundingClientRect()
          }
          view.exec(res => {
            const arr = []
            for (let i = 0; i < res.length; i++) {
              // 添加颜色属性
              res[i].color = this.inactiveColor
              if (i === this.currentIndex) res[i].color = this.activeColor
              arr.push(res[i])
            }
            this.tabsInfo = arr
            resolve()
          })
        })
      },
      // 查询components信息
      getQuery(cb) {
        try {
          let view = uni.createSelectorQuery().in(this).select('.tn-tabs-swiper')
          view.fields({
              size: true
            },
            data => {
              if (data) {
                this.componentsWidth = data.width
                if (cb && typeof cb === 'function') cb(data)
              } else {
                this.getQuery(cb)
              }
            }
          ).exec()
        } catch (e) {
          this.componentsWidth = windowWidth
        }
      },
      // 当swiper滑动结束的时候，计算滑块最终停留的位置
      countLine3Dx() {
        const tab = this.tabsInfo[this.animationFinishCurrent]
        // 让滑块中心点和当前tab中心重合
        if (tab) this.tabLineDx = tab.left + tab.width / 2 - this.barWidthPx / 2 - this.tabsInfo[0].left
      },
      // 把活动的tab移动到屏幕中心
      setScrollViewToCenter() {
        let tab = this.tabsInfo[this.animationFinishCurrent]
        if (tab) {
          let tabCenter = tab.left + tab.width / 2
          let parentWidth
          // 活动tab移动到中心时，以屏幕还是tab组件宽度为基准
          if (this.autoCenterMode === 'window') {
            parentWidth = windowWidth
          } else {
            parentWidth = this.componentsWidth
          }
          this.scrollLeft = tabCenter - parentWidth / 2
        }
      },
      // 设置偏移位置
      setDx(dx) {
        
        // 计算下一个标签的步进值
        let nextIndexStep = Math.ceil(Math.abs(dx / this.swiperWidthPx))
        let nextTabIndex = dx > 0 ? this.animationFinishCurrent + 1 : this.animationFinishCurrent - 1
        // 处理索引超出边界问题
        nextTabIndex = nextTabIndex <= 0 ? 0 : nextTabIndex
        nextTabIndex = nextTabIndex >= this.list.length ? this.list.length - 1 : nextTabIndex
        
        // 当前tab中心点x轴坐标
        let currentTab = this.tabsInfo[this.animationFinishCurrent]
        let currentTabX = currentTab.left + currentTab.width / 2
        
        // 下一个tab中心点x轴坐标
        let nextTab = this.tabsInfo[nextTabIndex]
        let nextTabX = nextTab.left + nextTab.width / 2
        
        // 两个tab之间的距离
        let distanceX = Math.abs(nextTabX - currentTabX)
        this.tabLineAddDx = (dx / this.swiperWidthPx) * distanceX
        this.setTabColor(this.animationFinishCurrent, nextTabIndex, dx)
      },
      // 设置tab的颜色
      setTabColor(currentTabIndex, nextTabIndex, dx) {
        let nextIndexStep = Math.ceil(Math.abs(dx / this.swiperWidthPx))
        if (Math.abs(dx) > this.swiperWidthPx) {
          dx = dx > 0 ? dx - (this.swiperWidthPx * (nextIndexStep - 1)) : dx + (this.swiperWidthPx * (nextIndexStep - 1))
        }
        let colorIndex = Math.abs(Math.ceil((dx / this.swiperWidthPx) * 100))
        let colorLength = this.colorGradientArr.length
        // 处理超出索引边界
        colorIndex = colorIndex >= colorLength ? colorLength - 1 : colorIndex <= 0 ? 0 : colorIndex
        if (nextIndexStep > 1) {
          // 设置下一个tab的颜色
          // 设置之前tab的颜色为默认颜色
          if (dx > 0) {
            this.tabsInfo[nextTabIndex + (nextIndexStep - 1) > this.tabsInfo.length - 1 ? this.tabsInfo.length - 1 : nextTabIndex + (nextIndexStep - 1)].color = this.colorGradientArr[colorIndex]
            this.tabsInfo[nextTabIndex + (nextIndexStep - 2) > this.tabsInfo.length - 1 ? this.tabsInfo.length - 1 : nextTabIndex + (nextIndexStep - 2)].color = this.colorGradientArr[colorLength - 1 - colorIndex]
          } else {
            this.tabsInfo[nextTabIndex - (nextIndexStep - 1) < 0 ? 0 : nextTabIndex - (nextIndexStep - 1)].color = this.colorGradientArr[colorIndex]
            this.tabsInfo[nextTabIndex - (nextIndexStep - 2) < 0 ? 0 : nextTabIndex - (nextIndexStep - 2)].color = this.colorGradientArr[colorLength - 1 - colorIndex]
          }
        } else {
          // 设置下一个tab的颜色
          this.tabsInfo[nextTabIndex].color = this.colorGradientArr[colorIndex]
          // 设置当前tab的颜色
          this.tabsInfo[currentTabIndex].color = this.colorGradientArr[colorLength - 1 - colorIndex]
        }
        
      },
      // swiper滑动结束
      setFinishCurrent(current) {
        // 如果滑动的索引不一致，修改tab颜色变化，因为可能会有直接点击tab的情况
        this.tabsInfo.map((item, index) => {
          if (current == index) item.color = this.activeColor
          else item.color = this.inactiveColor
          return item
        })
        this.tabLineAddDx = 0
        this.animationFinishCurrent = current
        this.countLine3Dx()
      }
    }
  }
</script>

<style lang="scss" scoped>
  
  /* #ifndef APP-NVUE */
  ::-webkit-scrollbar {
    display: none;
    width: 0 !important;
    height: 0 !important;
    -webkit-appearance: none;
    background: transparent;
  }
  /* #endif */
  
  /* #ifdef H5 */
  // 通过样式穿透，隐藏H5下，scroll-view下的滚动条
  scroll-view ::v-deep ::-webkit-scrollbar {
  	display: none;
  	width: 0 !important;
  	height: 0 !important;
  	-webkit-appearance: none;
  	background: transparent;
  }
  /* #endif */
  
  .tn-tabs-swiper {
    &__scroll-view {
      position: relative;
      width: 100%;
      white-space: nowrap;
      
      &__box {
        position: relative;
        /* #ifdef MP-TOUTIAO */
        white-space: nowrap;
        /* #endif */
      }
      
      &__item {
        position: relative;
        /* #ifndef APP-NVUE */
        display: inline-block;
        /* #endif */
        text-align: center;
        transition-property: background-color, color;
      }
      
      &--flex {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
      }
    }
    
    &__bar {
      position: absolute;
      bottom: 0;
    }
  }
  
</style>
