时间:2022-01-10 15:17:34 | 栏目:JavaScript代码 | 点击:次
在实际项目中,遇到上下滚动条和左右滚动条不在一个DIV内部,所以某些情况下,右侧滚动条不可见。但是需要咋同一个视口内显示两个滚动条。
一个解决思路是:自定义滚动条,隐藏原始滚动条。
scrollbar.js
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import '../css/scrollbar.css'; const propTypes = { eventBus: PropTypes.object.isRequired, }; class ScrollBar extends Component { constructor(props) { super(props); this.state = { isDraging: false, // X: bottom scrollbar offset left, range [0, innerWidth - 100]. When dragging, x is changing x: null, // clickX 表示拖动滚动条时,鼠标点击的位置距离滚动条左侧的距离, range [0, 100], When dragging, clickX isn't changing clickX: 0, }; } componentDidMount() { this.unsubscribeScrollToColumn = this.props.eventBus.subscribe('set-scrollbar-left', this.setScrollBarLeft); document.addEventListener('mouseup', this.onMouseUp); } componentWillUnmount() { this.unsubscribeScrollToColumn(); document.removeEventListener('mouseup', this.onMouseUp); } /** * 这个函数处理联动(界面滚动时,触发滚动条滚动)这里的100是滚动条的宽度 */ setScrollBarLeft = (leftRatio) => { // when bottom scrollbar is dragging, can't set scrollBa left if (this.state.isDraging) return; this.setState({ x: (window.innerWidth - 100) * leftRatio, }); } /** * 当鼠标按下,开始拖动,设置当前的位置为初始拖动的位置 */ handleMouseDown = (e) => { this.setState({ isDraging: true, clickX: e.nativeEvent.offsetX, }); } /** * 当鼠标抬起时,停止拖拽,设置当前的点击位置是0(这个有没有必要设置) */ onMouseUp = () => { if (this.state.isDraging) { setTimeout(() => { this.setState({ isDraging: false, clickX: 0 }); }, 100); } } /** * 当拖拽进行时(鼠标按下并开始移动),获取当前的位移,计算新的偏移量 * 注意:可以向右滚动,可以向左滚动 * 当拖拽进行时,应该计算出当前的比例,然后Grid水平滚动 * 现在的问题,如果鼠标拖动时移动到滚动条外部,那么无法触发拖动 * */ onMouseMove = (e) => { e.persist(); if (this.state.isDraging) { // 新距离 = 原始距离 + (当前滚动的距离 - 初始滚动的距离) let newX = this.state.x + e.nativeEvent.offsetX - this.state.clickX; newX = Math.min(newX, window.innerWidth - 100); // 最大的拖动不能超过右侧边界 this.setState({ x: newX }); const leftRatio = newX / (window.innerWidth - 100); } } renderBottomToolbar = () => { return ( <div className="antiscroll-scrollbar antiscroll-scrollbar-horizontal antiscroll-scrollbar-shown" style={{transform: `translateX(${this.state.x}px)`}} draggable="true" onMouseDown={this.handleMouseDown} onMouseMove={this.onMouseMove} onMouseUp={this.onMouseUp} ></div> ); } // todo: rightToolbar event handle renderRightToolbar = () => { return ( <div className="antiscroll-scrollbar antiscroll-scrollbar-vertical antiscroll-scrollbar-shown" ></div> ); } render() { return ( <div id="scrollOverlay" className="antiscroll-wrap"> {this.renderBottomToolbar()} {this.renderRightToolbar()} </div> ); } } ScrollBar.propTypes = propTypes; export default ScrollBar;
对应的 scrollbar.css
#scrollOverlay { display: inline-block; overflow: hidden; position: fixed; left: 0; right: 0; top: 156px; bottom: 0; z-index: 4; pointer-events: none; opacity: .7; } #scrollOverlay .antiscroll-scrollbar { pointer-events: auto; z-index: 2; background-color: hsla(0,0%,0%,0.28); box-shadow: inset 0 0 0 1px hsl(0,0%,100%); border-radius: 5px; } #scrollOverlay .antiscroll-scrollbar-horizontal { height: 12px; width: 100px; position: absolute; bottom: 32px; } #scrollOverlay .antiscroll-scrollbar-vertical { width: 12px; height: 100px; position: absolute; right: 0; } /* 隐藏原始滚动对象的滚动条 */ .react-demo::-webkit-scrollbar { width: 0; }
具体使用,我们在 Grid 中加入这个滚动条
import ScrollBar from '../components/scrollbar'; // Grid 原生滚动,触发回调函数 onScroll = () => { // todo: when clientWidth is smaller than innerWidth, don't show bottom scrollBar let scrollLeftRatio = this._scrollLeft / (clientWidth - window.innerWidth); // 当原生DOM左右滚定时,获取当前滚动的比例(偏移量/全部宽度),并设置滚动条进行滚动 this.setScrollLeftRatio(scrollLeftRatio); } setScrollLeftRatio = (scrollLeftRatio) => { this.props.eventBus.dispatch('set-scrollbar-left', scrollLeftRatio); } // 在原始滚动元素中,传入eventBus,便于事件传值处理 // <ScrollBar eventBus={this.props.eventBus}/>
自定义滚动条也有很多开源第三方组件,我们优先使用第三方库实现(处理滚动条计算考虑情况较多)