效果图

在这里插入图片描述

大概思路

1、导航栏用ScrollView写,不过样式得加上white-space: nowrap;文本不换行,
2、在导航栏上定义id用来**Promise.all()**获取元素属性,
Promise.all()可以将多个Promise实例包装成一个新的Promise实例,
3、 再用Taro.createSelectorQuery()获取每一个DOM节点属性,

视图

<View className='tab_index_wrapp'>
  <ScrollView scrollWithAnimation className='tab_scroll_view' 
    scrollX id='tabList' scrollLeft={offsetLeft}
  >
    <View className='tab_view'>
      {
        list.map((item, index) => {
          return (
            <View 
              key={index} className='tab' id={`item-${index}`}
              onClick={this.setLineItem.bind(this, index)}
            >{item.title}</View>
          )
        })
      }
      <View className='tab_line' style={lineStyle}></View>
    </View>
  </ScrollView>

  <View className='tab_swiper_wrapp'>
    <Swiper className='tab_swiper' current={actvieIndex} onChange={this.swiperBindchange.bind(this)}>
      {
        listItem.map((item, index) => {
          return (
            <SwiperItem key={index} className='tab_swiper'>
              <View>{item.id}</View>
            </SwiperItem>
          )
        })
      }
    </Swiper>
  </View>
</View>

视图的样式

.tab_index_wrapp{
  height: 100vh;
  background-color: #f5f5f5;
  overflow: hidden;
  .tab_scroll_view{
    position: relative;
    width: 100%;
    font-size: 28px;
    background-color: #fff;
    margin-bottom: 20px;
    .tab_view{
      white-space: nowrap;
      position: relative;
      .tab{
        height: 70px;
        line-height: 70px;
        display: inline-block;
        margin-left: 45px;
        margin-right: 65px;
        color: #5f5f5f;
        box-sizing: border-box;
        &:last-child{
          margin-right: 45px;
        }
      }
      .tab_line{
        height: 2PX;
        border-radius: 20%;
        background-color: aqua;
        position: absolute;
        bottom: 0;
        transition: all .3s linear;
      }
    }
  }
  .tab_swiper_wrapp{
    width: 100%;
    height: 100%;
    .tab_swiper{
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  }
}

逻辑

constructor() {
 super(...arguments)
  this.state = {
    list: [
      {title: '理想', id: 0},
      {title: '梦想', id: 1},
      {title: '搬砖', id: 2},
      {title: '给予', id: 3},
      {title: '爱情', id: 4},
      {title: '赚钱', id: 5},
      {title: '疯子', id: 6},
      {title: '信念', id: 7},
      {title: '信仰', id: 8},
      {title: '谢谢', id: 9},
    ],
    listItem: [
      {title: '理想', id: 0},
      {title: '梦想', id: 1},
      {title: '搬砖', id: 2},
      {title: '给予', id: 3},
      {title: '爱情', id: 4},
      {title: '赚钱', id: 5},
      {title: '疯子', id: 6},
      {title: '信念', id: 7},
      {title: '信仰', id: 8},
      {title: '谢谢', id: 9},
    ],
    actvieIndex: 0,     // 标签下标
    offsetLeft: "",     // scroll-left位置
    viewWidth: "",      // scroll-view视图宽度
    lineStyle: "",      // 下划线样式
  }
}
componentWillMount () { 
 let that = this
  // Promise.all可以将多个Promise实例包装成一个新的Promise实例
  Promise.all([that.getList('#tabList'), that.lineItem(that.state.list, '#item-')])
    .then(([nav, list]) => {
      /**
       * nav: 是指获取that.getList('#tabList')导航栏的元素属性,
       * list:是指获取that.lineItem(that.state.list, '#item-')
       *       that.state.list下的每一个对象的元素属性,
       * 
       * that.state.viewWidth / 2:为什么除以2,因为小程序是 rpx 单位
       */
      that.state.viewWidth = nav.width
      that.state.list = list
      that.state.lineStyle = `width: ${list[that.state.actvieIndex].itemWidth}px;
                              transform: translateX(${list[that.state.actvieIndex].offsetLeft}px);`
      that.setState({
        offsetLeft: list[that.state.actvieIndex].offsetLeft - that.state.viewWidth / 2 + list[that.state.actvieIndex].itemWidth / 2,
        lineStyle: that.state.lineStyle,
      })
  })
}

// 获取元素属性,DOM节点
getList(e) {
  return new Promise(function(resolve, reject) {
    query = Taro.createSelectorQuery().select(e).boundingClientRect(res => {
      resolve(res)
    }).exec()
  })
}

// 初始化项目
lineItem(e, selector) {
  let that = this
  return new Promise(function(resolve, reject) {
    e.forEach((item, index) => {
      // that.getList(selector + index) 去 that.state.list 中获取每一个对象元素属性,
      that.getList(selector + index).then(res => {
        console.log('%c res', 'color:green', res)
        // res.left 是左边的距离, res.width 是每个对象元素的宽度
        item.offsetLeft = res.left
        item.itemWidth = res.width
      })
    });
    resolve(e) 
  })
}

// 点击切换
setLineItem(e) {
  let index = e
  this.setState({
    actvieIndex: index,
    offsetLeft: this.state.list[index].offsetLeft - this.state.viewWidth / 2 + this.state.list[index].itemWidth / 2,
    lineStyle: `width: ${this.state.list[index].itemWidth}px;
                transform: translateX(${this.state.list[index].offsetLeft}px)`,
  })
}

// 滑动切换
swiperBindchange(e) {
  let index = e.detail.current
  this.setState({
    actvieIndex: index,
    offsetLeft: this.state.list[index].offsetLeft - this.state.viewWidth / 2 + this.state.list[index].itemWidth / 2,
    lineStyle: `width: ${this.state.list[index].itemWidth}px;
                transform: translateX(${this.state.list[index].offsetLeft}px)`,
  })
}

用到的API和方法

1、不懂Promise.all()用法,可以去官网看看:
点击链接
2、获取DOM节点Taro官网:
点击链接

第一次写这个,以上有什么不对的,欢迎各位大佬来指出,谢谢

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐