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="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}