React中自己实现选项卡动画

在一个斑马开发的react项目中,由于无合适组件,需要自己写一个底部滑线动画的选项卡, 数据由接口返回,

底部滑线的宽度及向左移动的距离都需要动态获取

涉及到的问题:

  • React中map循环,给每个元素建立ref
  • css3 transition动画
// 模拟数据
const data = [
  {
    "title": "数字社区引擎",
    "imgSrc": "https://img.alicdn.com/tfs/TB1MaLKRXXXXXaWXFXXXXXXXXXX-480-260.png",
    "description": "我是描述描述描述,描述最多三行描述最多三三行描述最多三行描述最多三行最多三行",
    "abilityCode": "template",
    "detailUrl": ""
  },
  {
    "title": "数字引擎",
    "imgSrc": "https://img.alicdn.com/tps/TB1bewbNVXXXXc5XXXXXXXXXXXX-1000-300.png",
    "description": "我是描述描述描述,描述最多三行描述最多三行三行描述最多三描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行最多三行",
    "abilityCode": "template2",
    "detailUrl": ""
  },
  {
    "title": "数字社区引擎数字社区引擎",
    "imgSrc": "https://img.alicdn.com/tfs/TB1MaLKRXXXXXaWXFXXXXXXXXXX-480-260.png",
    "description": "我是描述描述描述,描述最多三行描述最多三三行描述最多三行描述最多三行最多三行",
    "abilityCode": "template3",
    "detailUrl": ""
  },
  {
    "title": "数字引擎数字引擎",
    "imgSrc": "https://img.alicdn.com/tps/TB1bewbNVXXXXc5XXXXXXXXXXXX-1000-300.png",
    "description": "我是描述描述描述,描述最多三行描述最多三行三行描述最多三描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行描述最多三行最多三行",
    "abilityCode": "template4",
    "detailUrl": ""
  }
];

export default class WrapComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeIndex: 0,
      lineLeft: 0,
      lineWidth: 88,
    };
    this.domRef = React.createRef();
  }

  handleClick = (e, ind) => {
    this.setState({
      activeIndex: ind,
    }, () => {
      // 获取选中元素的宽及距离左边的距离
      const { offsetLeft, offsetWidth } = this.refs['slider-bar-active'] || {};
      this.setState({
        lineLeft: offsetLeft,
        lineWidth: offsetWidth,
      })
    });
  }

  renderTabBar = () => {
    const { activeIndex, lineLeft, lineWidth } = this.state;
    return (<div className="slider-bar-wrap">
      <div className="slider-bar-box">
        {
          data.map((item, ind) => (
            <div
              ref={ind === activeIndex ? 'slider-bar-active' : ''}
              className={`slider-bar-item ${ind === activeIndex ? 'slider-bar-active' : ''}`}
              onClick={(e) => this.handleClick(e, ind)}
            >{item.title}</div>
          ))
        }
        <div style={{  `${lineWidth}px`, left: `${lineLeft}px` }} className="active-line" />
      </div>
    </div>)
  }

  render() {
    return (
      <div className="component-wrapper">
        { this.renderTabBar() }
      </div>
    )
  }
}

// css样式
.slider-bar-wrap {
  margin: 20px auto;
  display: flex;
  justify-content: center;
}

.slider-bar-box {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.slider-bar-item {
  height: 50px;
  font-family: PingFangSC-Regular;
  font-size: 14px;
  color: #181818;
  letter-spacing: 0;
  text-align: center;
  line-height: 50px;
  cursor: pointer;
}

.slider-bar-active {
    color: #FF6A00;
}

.active-line {
   71px;
  height: 2px;
  background: #FF6A00;
  position: absolute;
  bottom: 0;
  transition: all 0.3s ease;
}

  

 
原文地址:https://www.cnblogs.com/puerile/p/14097445.html