实现效果
组件代码:
index.js
import React from 'react'
import styles from './index.less'
import _ from 'lodash'
import PropTypes from 'prop-types'
/**
* 自定义 带渐变时间轴组件
*/
class TimeLine extends React.Component {
constructor(props) {
super(props)
}
render() {
let { style = {}, lineStyle = {}, colorOption = [], angle = 45, breakOption = {}, topTextStyle = {}, bottomTextStyle = {} } = this.props
//line style
let linearStyle = `linear-gradient(${angle}deg,`
let lineStyleClone = _.cloneDeep(lineStyle)
if (colorOption) {
colorOption.forEach((item) => {
linearStyle += `${item.color} ${item.step}%,`
})
lineStyleClone.background = linearStyle.substr(0, linearStyle.length - 1)
}
return (
<div className={styles.timeLineContainer} style={style}>
<span className={styles.timeLine} style={lineStyleClone}></span>
{
breakOption.map((item, index) => {
return <div key={index}>
<div className={`${styles.point} ${item.active === true ? styles.activePoint : ''}`}
style={{ left: `${item.step ? item.step : 0}%` }}></div>
{
(() => {
let top = _.cloneDeep(topTextStyle)
top.left = (item.step ? item.step : 0) + '%'
return <div className={styles.topText}
style={top}
>{item.topText ? item.topText : ''}</div>
})()
}
{
(() => {
let bottom = _.cloneDeep(bottomTextStyle)
bottom.left = (item.step ? item.step : 0) + '%'
return <div className={styles.bottomText}
style={bottom}
>{item.bottomText ? item.bottomText : ''}</div>
})()
}
</div>
})
}
</div>
)
}
}
TimeLine.propTypes = {
style: PropTypes.object,
lineStyle: PropTypes.object,//线条样式
colorOption: PropTypes.array.isRequired,//颜色渐变断点设置
angle: PropTypes.number,//渐变颜色旋转角
breakOption: PropTypes.array.isRequired,//断点文本,分上文本,下文本
topTextStyle: PropTypes.object,//上文本样式
bottomTextStyle: PropTypes.object,//下文本样式
}
TimeLine.defaultProps = {
style: {},
lineStyle: {},
colorOption: [],
angle: 45,
breakOption: [],
topTextStyle: {},
bottomTextStyle: {},
}
export default TimeLine
index.less
.timeLineContainer {
display: flex;
width: 100%;
position: relative;
margin: 40px 0;
user-select: none;
.timeLine {
width: 100%;
height: 4px;
border-radius: 2px;
transition: all .3s;
}
.point {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #06187D;
box-shadow: 0 0 0 5px #D8D8D8;
}
.activePoint {
background-color: #67D9FF;
box-shadow: 0 0 0 5px #2986ED;
}
.topText {
position: absolute;
width: fit-content;
bottom: 22px;
transform: translateX(-50%);
font-size: 16px;
font-family: PingFangSC-Semibold, PingFang SC, sans-serif;
font-weight: 600;
color: #FFFFFF;
}
.bottomText {
position: absolute;
width: fit-content;
top: 22px;
transform: translateX(-50%);
height: 27px;
font-size: 16px;
font-family: AlibabaPuHuiTi-Regular, AlibabaPuHuiTi, sans-serif;
font-weight: 400;
color: #FFFFFF;
line-height: 27px;
}
}
example Option示例
// option e.g.
colorOption=[
{ color: '#67D9FF', step: 0 },
{ color: '#61CDF8', step: 37 },
{
color: '#484075',
step: 37,
},
{ color: '#7E74AD', step: 100 }
]
breakOption: [
{
topText: '报名阶段',
bottomText: '9/23 ~10/24',
step: 10,
active: true,
},
{ topText: '赛前培训', bottomText: '10/25', step: 37, active: true },
{
topText: '初赛',
bottomText: '11/7',
step: 63,
active: false,
},
{ topText: '初赛成绩公布', bottomText: '11/16', step: 90, active: false }
]
<TimeLine colorOption={colorOption} breakOption={step1}/>