| import React from 'react'; |
| import ReactDOM from 'react-dom'; |
| import { autobind } from 'core-decorators'; |
| import classnames from 'classnames'; |
| import { throttle } from '../../../utils'; |
| |
| import './index.scss'; |
| |
| class Slider extends React.Component { |
| constructor(props) { |
| super(props); |
| this.container = null; |
| this.state = { |
| screenIndex: 0, |
| visibleNum: 1, |
| }; |
| } |
| |
| componentDidMount() { |
| this.throttleAdjust = throttle(() => { |
| this.setState({ |
| visibleNum: this.getVisibleNum(), |
| }); |
| }, 200); |
| window.addEventListener('resize', this.throttleAdjust); |
| // 做重新布局 |
| /* eslint-disable react/no-did-mount-set-state */ |
| this.setState({ |
| visibleNum: this.getVisibleNum(), |
| }); |
| } |
| |
| componentWillUnmount() { |
| window.removeEventListener('resize', this.throttleAdjust); |
| } |
| |
| @autobind |
| getVisibleNum() { |
| // 比较粗略的计算,假定一屏的子节点外边距之和不会超过一个子节点的宽度 |
| /* eslint-disable react/no-find-dom-node */ |
| let result = 1; |
| const containerWidth = this.container.getBoundingClientRect().width; |
| const childWidth = this.sliderItemChild0.getBoundingClientRect ? this.sliderItemChild0.getBoundingClientRect().width : ReactDOM.findDOMNode(this.sliderItemChild0).getBoundingClientRect().width; |
| if (containerWidth && childWidth) { |
| result = Math.floor(containerWidth / childWidth); |
| } |
| // 有可能result为0,下面的除法就会出现分母为0,从而导致得出Inifity,无限循环导致浏览器崩溃 |
| return result || 1; |
| } |
| |
| @autobind |
| getListWidth() { |
| let width = 0; |
| const { children } = this.props; |
| const { visibleNum } = this.state; |
| const len = React.Children.count(children); |
| // 最终分成的屏数 |
| const splitNum = Math.ceil(len / visibleNum); |
| if (this.container) { |
| const containerWidth = this.container.getBoundingClientRect().width; |
| width = containerWidth * splitNum; |
| } |
| return width; |
| } |
| |
| changeScreen(i) { |
| const { screenIndex } = this.state; |
| // const total = React.Children.count(this.props.children); |
| if (i !== screenIndex) { |
| this.setState({ |
| screenIndex: i |
| }); |
| } |
| } |
| |
| @autobind |
| renderSliderList() { |
| const { children } = this.props; |
| const { screenIndex, visibleNum } = this.state; |
| const splitGroup = []; |
| const len = React.Children.count(children); |
| // 分成的屏数 |
| const splitNum = Math.ceil(len / visibleNum); |
| /* eslint-disable no-plusplus*/ |
| for (let i = 0; i < splitNum; i++) { |
| splitGroup.push(Array.from(children).slice(i * visibleNum, (i + 1) * visibleNum)); |
| } |
| return ( |
| <div |
| className="slider-list" |
| style={{ |
| transform: `translateX(-${screenIndex * ((this.container && this.container.getBoundingClientRect().width) || 0)}px)`, |
| transition: 'transform 500ms ease', |
| width: this.getListWidth(), |
| }} |
| > |
| {splitGroup.map((group, i) => { |
| return ( |
| <div |
| className="slider-screen" |
| style={{ width: (this.container && this.container.getBoundingClientRect().width) || 0 }} |
| key={i} |
| ref={(node) => { this[`sliderScreen${i}`] = node; }} |
| > |
| { |
| group.map((child, j) => ( |
| <div |
| className="slider-item" |
| key={j} |
| > |
| {React.cloneElement(child, { |
| ref: (node) => { |
| this[`sliderItemChild${(i * visibleNum) + j}`] = node; |
| } |
| })} |
| </div> |
| ) |
| ) |
| } |
| </div> |
| ); |
| })} |
| </div> |
| ); |
| } |
| |
| @autobind |
| renderControl() { |
| const { children } = this.props; |
| const { screenIndex, visibleNum } = this.state; |
| const len = React.Children.count(children); |
| // 分成的屏数 |
| const splitNum = Math.ceil(len / visibleNum); |
| const temp = []; |
| for (let i = 0; i < splitNum; i++) { |
| temp.push(( |
| <span |
| key={i} |
| className={ |
| classnames({ |
| 'slider-control-item': true, |
| 'slider-control-item-active': i === screenIndex, |
| }) |
| } |
| onClick={this.changeScreen.bind(this, i)} |
| /> |
| )); |
| } |
| return ( |
| <div className="slider-control"> |
| {temp} |
| </div> |
| ); |
| } |
| |
| render() { |
| return ( |
| <div className="slider" ref={(node) => { this.container = node; }}> |
| {this.renderSliderList()} |
| {this.renderControl()} |
| </div> |
| ); |
| } |
| } |
| |
| export default Slider; |