萬事開頭難,一直都想寫博客,分享下本身的東西,科室無奈於工做比較忙,也不知道寫點啥好,太淺了吧,以爲沒意思,太深吧,我也不會,恰好公司最近要咱們封裝一批組件,就借花獻佛拿出來分享一下吧!以前也沒寫過湊合看吧
哈哈。。。
首先來看下這個組件的樣子, 它長這樣: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; } }
下面是技術總結,主要說下這個組件須要注意的地方:佈局