万事开头难,一直都想写博客,分享下本身的东西,科室无奈于工做比较忙,也不知道写点啥好,太浅了吧,以为没意思,太深吧,我也不会,恰好公司最近要咱们封装一批组件,就借花献佛拿出来分享一下吧!以前也没写过凑合看吧
哈哈。。。
首先来看下这个组件的样子, 它长这样:react
主要功能包括:jquery
接下来看下代码和思路吧!
首先观察一下组件的样式左右两个按钮且宽度固定,响应式改变的主要是中间部分的宽度,
antd组价选择使用Layout布局来写,代码以下:浏览器
布局定下来了估计剩下的都会写我就不啰嗦了,须要注意的是咱们在componentDidMount生命周期中记录下页面初次更新时,每个单元格的位置做为初始位置,
下面主要说下鼠标触发的滚动是怎么是实现的;
废话很少说直接上代码:antd
// 时间轴滚动事件 timeLineClick = (direction) => { const {moveCount} = this.props; let self = this; // 移动过程当中禁止点击 if (!this.allowClick) return; // 因使用scrollLeft 未达到最大值时scrollLeft已经达到最大 临界判断 let lastPositionLeft = $('.diagnosis_box').eq(this.timeLineHTMLArr.length - 1).position().left; let lastWidth = $('.diagnosis_box').eq(this.timeLineHTMLArr.length - 1).width(); let timeLinBoxWidth = $('#time_line_box').width() + _padding; if (direction === 'left' && $('.diagnosis_box').eq(0).position().left >= _padding) { return; } else if (direction === 'right') { if (Math.floor(lastPositionLeft + lastWidth) <= timeLinBoxWidth) { return } } this.allowClick = false; // 因拖拽屏幕会致使scrollLeft 有所变更 所这里从新定位下 this.timeLineCounter let currentPosition = $('#time_line_box').scrollLeft() + _padding; if (this.isResize || (direction === 'left' && Math.floor(lastPositionLeft + lastWidth) <= timeLinBoxWidth)) { this.timeLineHTMLArr.some((item, index) => { if (index < this.timeLineHTMLArr.length - 1 && item <= currentPosition && currentPosition <= this.timeLineHTMLArr[index + 1] ) { this.timeLineCounter = this.isResize ? index + 1 : index } }); } let count = 0; // 根据操做移动timeline if (direction === 'left') { if (this.timeLineCounter >= 0) { this.timeLineCounter = this.timeLineCounter - moveCount >= 0 ? this.timeLineCounter - moveCount : 0; let leftCount = Math.floor((currentPosition - self.timeLineHTMLArr[self.timeLineCounter]) / 5); let displacementTime = 200 / leftCount; function moveLeft(count) { setTimeout(() => { self.isResize = false; let counts = count + 1; let _left = currentPosition - _padding - counts * 5; $('#time_line_box').scrollLeft(_left); if (leftCount > count) { moveLeft(counts); } else { self.allowClick = true; let newLastPositionLeft = $('.diagnosis_box').eq(self.timeLineHTMLArr.length - 1).position().left; self.setState({ left: self.timeLineCounter === 0 ? false : true, right: Math.floor(newLastPositionLeft + lastWidth) <= timeLinBoxWidth ? false : true }); } }, displacementTime) } moveLeft(count); } } else if (direction === 'right') { if (this.timeLineCounter <= this.timeLineHTMLArr.length - 1) { this.timeLineCounter = this.timeLineCounter + moveCount < this.timeLineHTMLArr.length - 1 ? this.timeLineCounter + moveCount : this.timeLineHTMLArr.length - 1; let leftCount = Math.floor((self.timeLineHTMLArr[self.timeLineCounter] - currentPosition) / 5); let displacementTime = 200 / leftCount; function moveRight(count) { setTimeout(() => { self.isResize = false; let counts = count + 1; let _left = currentPosition - _padding + counts * 5; $('#time_line_box').scrollLeft(_left); if (leftCount > count) { moveRight(counts); } else { self.allowClick = true; let newLastPositionLeft = $('.diagnosis_box').eq(self.timeLineHTMLArr.length - 1).position().left; self.setState({ left: self.timeLineCounter === 0 ? false : true, right: Math.floor(newLastPositionLeft + lastWidth) <= timeLinBoxWidth ? false : true }); } }, displacementTime) } moveRight(count); } } };
这里的逻辑主要是鼠标点击触发的过程,须要注意的几点:less
目的是为了修正偏移量;剩下的就很少说了直接看代码吧!ide
import React, {PureComponent} from 'react'; import styles from './TimeLine.less'; import {Icon, Layout} from 'antd'; import $ from "jquery"; import PropTypes from 'prop-types'; /* * 公共时间轴 组件 接受定制显示模块 此组件提供模拟 获取TimeLineHTML函数 * 必须给定特性的 className = diagnosis_box; 每次点击保证diagnosis_box到达最左侧 * 本身定制部分的样式在 父组件内定义 * _padding 为time_line_box两边的padding 因归入原生计算 因此统一赋值 * */ const _padding = 24; const {Sider, Content} = Layout; let eleDrag = false; class TimeLine extends PureComponent { static propTypes = { timeLineHTML: PropTypes.array.isRequired, // 默认选项 moveCount: PropTypes.number, // 每次点击移动个数 height: PropTypes.number, mouseSliding: PropTypes.bool // 是否支持鼠标滑动事件 }; static defaultProps = { moveCount: 3, height: 65, mouseSliding: true }; constructor(props) { super(props); this.state = { left: false, right: true }; this.timeLineHTMLArr = []; this.timeLineCounter = 0; this.allowClick = true; this.isResize = false; this.initClientX = null; } componentDidMount() { let self = this; // 首次进入加载全部模块的positionleft 做为移动距离断定 if (!this.timeLineHTMLArr.length) { $('.diagnosis_box').each(function () { self.timeLineHTMLArr.push($(this).position().left); }); } window.onresize = () => { let target = this; if (target.resizeFlag) { clearTimeout(target.resizeFlag); } target.resizeFlag = setTimeout(function () { self.isResize = true; target.resizeFlag = null; }, 100); }; } // 时间轴滚动事件 timeLineClick = (direction) => { const {moveCount} = this.props; let self = this; // 移动过程当中禁止点击 if (!this.allowClick) return; // 因使用scrollLeft 未达到最大值时scrollLeft已经达到最大 临界判断 let lastPositionLeft = $('.diagnosis_box').eq(this.timeLineHTMLArr.length - 1).position().left; let lastWidth = $('.diagnosis_box').eq(this.timeLineHTMLArr.length - 1).width(); let timeLinBoxWidth = $('#time_line_box').width() + _padding; if (direction === 'left' && $('.diagnosis_box').eq(0).position().left >= _padding) { return; } else if (direction === 'right') { if (Math.floor(lastPositionLeft + lastWidth) <= timeLinBoxWidth) { return } } this.allowClick = false; // 因拖拽屏幕会致使scrollLeft 有所变更 所这里从新定位下 this.timeLineCounter let currentPosition = $('#time_line_box').scrollLeft() + _padding; if (this.isResize || (direction === 'left' && Math.floor(lastPositionLeft + lastWidth) <= timeLinBoxWidth)) { this.timeLineHTMLArr.some((item, index) => { if (index < this.timeLineHTMLArr.length - 1 && item <= currentPosition && currentPosition <= this.timeLineHTMLArr[index + 1] ) { this.timeLineCounter = this.isResize ? index + 1 : index } }); } let count = 0; // 根据操做移动timeline if (direction === 'left') { if (this.timeLineCounter >= 0) { this.timeLineCounter = this.timeLineCounter - moveCount >= 0 ? this.timeLineCounter - moveCount : 0; let leftCount = Math.floor((currentPosition - self.timeLineHTMLArr[self.timeLineCounter]) / 5); let displacementTime = 200 / leftCount; function moveLeft(count) { setTimeout(() => { self.isResize = false; let counts = count + 1; let _left = currentPosition - _padding - counts * 5; $('#time_line_box').scrollLeft(_left); if (leftCount > count) { moveLeft(counts); } else { self.allowClick = true; let newLastPositionLeft = $('.diagnosis_box').eq(self.timeLineHTMLArr.length - 1).position().left; self.setState({ left: self.timeLineCounter === 0 ? false : true, right: Math.floor(newLastPositionLeft + lastWidth) <= timeLinBoxWidth ? false : true }); } }, displacementTime) } moveLeft(count); } } else if (direction === 'right') { if (this.timeLineCounter <= this.timeLineHTMLArr.length - 1) { this.timeLineCounter = this.timeLineCounter + moveCount < this.timeLineHTMLArr.length - 1 ? this.timeLineCounter + moveCount : this.timeLineHTMLArr.length - 1; let leftCount = Math.floor((self.timeLineHTMLArr[self.timeLineCounter] - currentPosition) / 5); let displacementTime = 200 / leftCount; function moveRight(count) { setTimeout(() => { self.isResize = false; let counts = count + 1; let _left = currentPosition - _padding + counts * 5; $('#time_line_box').scrollLeft(_left); if (leftCount > count) { moveRight(counts); } else { self.allowClick = true; let newLastPositionLeft = $('.diagnosis_box').eq(self.timeLineHTMLArr.length - 1).position().left; self.setState({ left: self.timeLineCounter === 0 ? false : true, right: Math.floor(newLastPositionLeft + lastWidth) <= timeLinBoxWidth ? false : true }); } }, displacementTime) } moveRight(count); } } }; // 拖拽鼠标使用时间轴滑动 onMouseDown = (e) => { e.stopPropagation(); e.preventDefault(); eleDrag = true; this.initClientX = e.clientX; }; onMouseMoveCapture = (e) => { e.stopPropagation(); e.preventDefault(); if (!this.allowClick) return; if (eleDrag) { let currentPosition = $('#time_line_box').scrollLeft(); if (this.initClientX > e.clientX) { // 向右拖动鼠标 let lastPositionLeft = $('.diagnosis_box').eq(this.timeLineHTMLArr.length - 1).position().left; let lastWidth = $('.diagnosis_box').eq(this.timeLineHTMLArr.length - 1).width(); let timeLinBoxWidth = $('#time_line_box').width() + _padding; if (Math.floor(lastPositionLeft + lastWidth) <= timeLinBoxWidth) { return } let displacement = this.initClientX - e.clientX; $('#time_line_box').scrollLeft(currentPosition + displacement); } else if (this.initClientX < e.clientX) {// 向左拖动鼠标 if ($('.diagnosis_box').eq(0).position().left >= _padding) { return; } let displacement = currentPosition - (e.clientX - this.initClientX); $('#time_line_box').scrollLeft(displacement); } this.initClientX = e.clientX; } }; noDragging = (e) => { e.stopPropagation(); e.preventDefault(); eleDrag = false; this.initClientX = null; }; render() { const { props: { timeLineHTML, height, mouseSliding }, state: { left, right, } } = this; return ( <div className={styles.TimeLine} style={{ height, lineHeight: `${height}px` }}> <Layout> <Sider width={20} style={{ position: 'relative', background: 'rgba(0, 0, 0, .25)', textAlign: 'center', cursor: left ? 'pointer' : 'not-allowed' }}> <span style={{ position: 'absolute', left: '-1px', width: '100%', height: '100%', textAlign: 'center', display: 'inline-block', cursor: left ? 'pointer' : 'not-allowed' }} onMouseEnter={() => { if ($('.diagnosis_box').eq(0).position().left >= _padding - 0.01) { this.setState({left: false}); } else { this.setState({left: true}); } }} onClick={() => { this.timeLineClick('left') }} /> <Icon type="left"/> </Sider> <Content style={{ height: '100%', overflow: 'hidden', position: 'relative' }}> {mouseSliding ? <div onMouseDown={this.onMouseDown} onMouseMoveCapture={this.onMouseMoveCapture} onMouseUp={this.noDragging} onMouseLeave={this.noDragging} id='time_line_box' className={styles.time_line_box}> {timeLineHTML} </div> : <div id='time_line_box' className={styles.time_line_box}> {timeLineHTML} </div>} </Content> <Sider width={20} style={{ position: 'relative', background: 'rgba(0, 0, 0, .25)', textAlign: 'center', cursor: right ? 'pointer' : 'not-allowed' }}> <span style={{ position: 'absolute', left: '-1px', width: '100%', height: '100%', textAlign: 'center', display: 'inline-block', cursor: right ? 'pointer' : 'not-allowed' }} onMouseEnter={() => { let lastPositionLeft = $('.diagnosis_box').eq(this.timeLineHTMLArr.length - 1).position().left; let lastWidth = $('.diagnosis_box').eq(this.timeLineHTMLArr.length - 1).width(); let timeLinBoxWidth = $('#time_line_box').width() + _padding; if (Math.floor(lastPositionLeft + lastWidth) <= timeLinBoxWidth) { this.setState({right: false}); } else { this.setState({right: true}); } }} onClick={() => { this.timeLineClick('right') }} /> <Icon type="right"/> </Sider> </Layout> </div> ) } } export default TimeLine;
下面是对应的less:函数
.TimeLine{ height: 100%; :global{ .ant-layout.ant-layout-has-sider{ height: 100%; } } .time_line_box{ position: relative; padding: 0 24px; height: 100%; overflow: hidden; white-space: nowrap; } }
下面是技术总结,主要说下这个组件须要注意的地方:布局