import React, { Component } from ‘react‘ import PropTypes from ‘prop-types‘ import assign from ‘object-assign‘ import _ from ‘lodash‘ import CX from ‘classnames‘ import ‘./index.less‘ /* * * ProgressBar * vertical 设置
import React, { Component } from ‘react‘
import PropTypes from ‘prop-types‘
import assign from ‘object-assign‘
import _ from ‘lodash‘
import CX from ‘classnames‘
import ‘./index.less‘
/**
* ProgressBar
* vertical 设置进度条是否垂直显示
* trackHover trackHover事件
* onSlide 事件函数获取percent值
* percent 设置滑块位置,0~100之间
* style 最外层div的样式
* slidedStyle 滑块左侧划过部分的样式
* trackStyle 滑块右侧未划过部分的样式
* ballStyle 滑块的样式
* showHoverStyle 是否设置hover时的样式
* hoverStyle 最外层div的样式
* hoverSlidedStyle 滑块左侧划过部分的样式
* hoverTrackStyle 滑块右侧未划过部分的样式
* hoverBallStyle 滑块的样式
* dragInfo 滑动滑块时显示在滑块上方的提示信息,默认没有提示信息
* dragInfoWrapStyle 滑块提示信息父级元素的样式,可用于调整提示信息的位置
* previewInfo 指针在进度条内时显示的指针位置进度提示信息
* previewInfoWrapStyle previewInfo 提示信息父级元素的样式,可用于调整提示信息的位置
* onCursorSlide 事件函数获取当前指针处的percent 可用于更新previewInfo
*/
class ProgressBar extends Component {
static propTypes = {
vertical: PropTypes.bool,
onSlide: PropTypes.func,
style: PropTypes.object,
slidedStyle: PropTypes.object,
trackStyle: PropTypes.object,
ballStyle: PropTypes.object,
showHoverStyle: PropTypes.bool,
hoverStyle: PropTypes.object,
hoverSlidedStyle: PropTypes.object,
hoverTrackStyle: PropTypes.object,
hoverBallStyle: PropTypes.object,
percent: PropTypes.number,
dragInfo: PropTypes.element,
dragInfoWrapStyle: PropTypes.object,
previewInfo: PropTypes.element,
previewInfoWrapStyle: PropTypes.object,
onCursorSlide: PropTypes.func,
disableSlide: PropTypes.bool,
}
static defaultProps = {
vertical: false,
onSlide: _.noop,
style: {},
slidedStyle: {},
trackStyle: {},
ballStyle: {},
showHoverStyle: false,
hoverStyle: {},
hoverSlidedStyle: {},
hoverTrackStyle: {},
hoverBallStyle: {},
percent: 0,
dragInfo: null,
dragInfoWrapStyle: {},
previewInfo: null,
previewInfoWrapStyle: {},
onCursorSlide: _.noop,
disableSlide: false,
}
state = {
percent: this.props.percent,
cursorPercent: 0,
moveFlag: false,
cursorInSlideBall: false,
cursorInComponent: false,
}
componentDidMount() {
this.rangeSlideElem.addEventListener(‘mousedown‘, this.onWrapElemMouseDown)
this.rangeSlideElem.addEventListener(‘mouseenter‘, this.onWrapElemMouseEnter)
this.rangeSlideElem.addEventListener(‘mousemove‘, this.onWrapElemMouseMove)
this.rangeSlideElem.addEventListener(‘mouseleave‘, this.onWrapElemMouseLeave)
this.rangeSlideElem.addEventListener(‘click‘, this.handleSlide)
document.body.addEventListener(‘mousemove‘, this.onBodyMouseMove)
document.body.addEventListener(‘mouseup‘, this.onBodyMouseUp)
document.body.addEventListener(‘mouseleave‘, this.onBodyMouseLeave)
}
componentWillReceiveProps(nextProps) {
if (nextProps.percent !== this.props.percent) {
this.setState({
percent: nextProps.percent,
})
}
}
componentWillUnmount() {
document.body.removeEventListener(‘mousemove‘, this.onBodyMouseMove)
document.body.removeEventListener(‘mouseup‘, this.onBodyMouseUp)
document.body.removeEventListener(‘mouseleave‘, this.onBodyMouseLeave)
}
getPercent = (e) => {
let percentage
if (this.props.vertical === false) {
const offsetLeft = this.rangeSlideElem.getBoundingClientRect().x
const { offsetWidth } = this.rangeSlideElem
percentage = Math.round(((e.pageX - offsetLeft) / offsetWidth) * 100)
} else {
const offsetTop = this.rangeSlideElem.getBoundingClientRect().y
const { offsetHeight } = this.rangeSlideElem
percentage = Math.round((1 - (e.pageY - offsetTop) / offsetHeight) * 100)
}
percentage = Math.max(Math.min(percentage, 100), 0)
return percentage
}
onWrapElemMouseDown = () => {
this.setState({
moveFlag: true,
})
}
onBodyMouseMove = _.throttle((e) => {
if (this.state.moveFlag) {
this.handleSlide(e)
}
}, 30)
onBodyMouseUp = () => {
this.setState({
moveFlag: false,
})
}
onBodyMouseLeave = this.onBodyMouseUp
handleSlide = (e) => {
if (this.props.disableSlide === true) {
return
}
const percent = this.getPercent(e)
this.props.onSlide(percent)
this.setState({
percent,
})
}
onSlideBallMouseEnter = () => {
this.setState({
cursorInSlideBall: true,
})
}
onSlideBallMouseLeave = () => {
this.setState({
cursorInSlideBall: false,
})
}
onWrapElemMouseEnter = (e) => {
const cursorPercent = this.getPercent(e)
this.props.onCursorSlide(cursorPercent)
this.setState({
cursorInComponent: true,
cursorPercent,
})
}
onWrapElemMouseMove = (e) => {
const cursorPercent = this.getPercent(e)
this.props.onCursorSlide(cursorPercent)
this.setState({
cursorPercent,
})
}
onWrapElemMouseLeave = () => {
this.setState({
cursorInComponent: false,
})
}
rangeSlideElem
activeBarElem
render() {
const { cursorInComponent } = this.state
const showHoverStyle = cursorInComponent && this.props.showHoverStyle
const wrapStyles = assign({}, showHoverStyle ? this.props.hoverStyle : this.props.style)
let slideTrackStyles
if (this.props.vertical === true) {
slideTrackStyles = assign({}, showHoverStyle ? this.props.hoverTrackStyle : this.props.trackStyle, {
height: `${100 - this.state.percent}%`,
})
} else {
slideTrackStyles = assign({}, showHoverStyle ? this.props.hoverTrackStyle : this.props.trackStyle, {
width: `${100 - this.state.percent}%`,
})
}
const activeBarStyles = assign({}, showHoverStyle ? this.props.hoverSlidedStyle : this.props.slidedStyle)
const slideBallStyles = assign({}, showHoverStyle ? this.props.hoverBallStyle : this.props.ballStyle)
const dragInfoWrapStyle = assign({}, this.props.dragInfoWrapStyle)
const previewInfoWrapStyle = assign({}, this.props.previewInfoWrapStyle, {
left: `${this.state.cursorPercent}%`,
})
const showDragInfo = this.state.cursorInSlideBall || this.state.moveFlag
const showPreviewInfo = showDragInfo === false && this.state.cursorInComponent
return (
<div
className={CX({
‘horizontal-progress-bar-component-wrap‘: this.props.vertical === false,
‘vertical-progress-bar-component-wrap‘: this.props.vertical === true,
})}
style={wrapStyles}
ref={(r) => {
this.rangeSlideElem = r
}}
>
<div className="active-bar" style={activeBarStyles} />
<div
className="slide-track"
ref={(r) => {
this.activeBarElem = r
}}
style={slideTrackStyles}
>
<div
className="slide-ball"
style={slideBallStyles}
onMouseEnter={this.onSlideBallMouseEnter}
onMouseLeave={this.onSlideBallMouseLeave}
/>
{
showDragInfo && (
<div
className="drag-info-element-wrap"
style={dragInfoWrapStyle}
>
{this.props.dragInfo}
</div>
)
}
</div>
{
showPreviewInfo && (
<div
className="preview-info-element-wrap"
style={previewInfoWrapStyle}
>
{this.props.previewInfo}
</div>
)
}
</div>
)
}
}
export default ProgressBar
样式如下:
.horizontal-progress-bar-component-wrap { width: 100%; height: 12px; margin: 0; position:relative; cursor: pointer; .active-bar { position:absolute; top: 50%; left: 0; margin-top: -2px; width: 100%; height: 4px; border-radius: 4px; background: linear-gradient(to right, #81d5fa, #3977f6); } .slide-track { width: 50%; height: 4px; position:absolute; top: 50%; right: 0; margin-top: -2px; border-radius: 4px; background: #fff; .slide-ball { width: 12px; height: 12px; position: absolute; left: -6px; top: -4px; border-radius: 50%; cursor: pointer; background: url(‘~ROOT/shared/assets/image/vn-circle-blue-42-42.png‘) no-repeat center; background-size: 12px; } .drag-info-element-wrap { position: absolute; left: -24px; top: -46px; } } .preview-info-element-wrap { position: absolute; top: -32px; margin-left: -24px; } } .vertical-progress-bar-component-wrap { width: 12px; height: 100%; margin: 0; position: relative; cursor: pointer; .active-bar { position:absolute; bottom: 0; left: 50%; margin-left: -2px; height: 100%; width: 4px; border-radius: 4px; background: linear-gradient(to top, #81d5fa, #3977f6); } .slide-track { position:absolute; height: 50%; width: 4px; right: 50%; top: -2px; margin-right: -2px; border-radius: 4px; background: #fff; .slide-ball { width: 12px; height: 12px; position: absolute; left: -4px; bottom: -6px; border-radius: 50%; cursor: pointer; background: url(‘~ROOT/shared/assets/image/vn-circle-blue-42-42.png‘) no-repeat center; background-size: 12px; } .drag-info-element-wrap { position: absolute; left: -46px; bottom: -24px; } } .preview-info-element-wrap { position: absolute; left: -32px; margin-bottom: -24px; } }
