javascript Realizes Custom Scroll Bar Effect

  • 2021-11-13 00:33:37
  • OfStack

In real projects, the up and down scroll bars and the left and right scroll bars are not inside 1 DIV, so in some cases, the right scroll bar is not visible. However, it is necessary to display two scroll bars in the same viewport.

One solution is to customize the scroll bar and hide the original scroll bar.

Custom scroll bar

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  Indicates the distance between the mouse click position and the left side of the scroll bar when dragging the scroll bar , 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);
  }

  /**
   *  This function handles linkage (when the interface scrolls, it triggers the scroll bar to scroll). 100 Is the width of the scroll bar 
   */
  setScrollBarLeft = (leftRatio) => {
    // when bottom scrollbar is dragging, can't set scrollBa left
    if (this.state.isDraging) return;
    this.setState({
      x: (window.innerWidth - 100) * leftRatio,
    });
  }

  /**
   *  When the mouse is pressed, start dragging, and set the current position as the initial dragging position 
   */
  handleMouseDown = (e) => {
    this.setState({
      isDraging: true,
      clickX: e.nativeEvent.offsetX,
    });
  }

  /**
   *  When the mouse is raised, stop dragging and set the current click position to 0 (Is it necessary to set this) 
   */
  onMouseUp = () => {
    if (this.state.isDraging) {
      setTimeout(() => {
        this.setState({ isDraging: false, clickX: 0 });
      }, 100);
    }
  }

  /**
   *  While dragging (mouse down and moving), get the current displacement and calculate the new offset 
   *  Note: You can scroll to the right, you can scroll to the left 
   *  When dragging is going on, you should calculate the current scale, and then Grid Horizontal rolling 
   *  Now, if the mouse moves to the outside of the scroll bar when dragging, the dragging cannot be triggered 
   * */ 
  onMouseMove = (e) => {
    e.persist();
    if (this.state.isDraging) {
      //  New distance  =  Original distance  +  (Current scrolling distance  -  Initial scroll distance) 
      let newX = this.state.x + e.nativeEvent.offsetX - this.state.clickX;
      newX = Math.min(newX, window.innerWidth - 100); //  The maximum drag cannot exceed the right boundary 
      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;

Scroll bar style

Corresponding 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;
}

/*  Hide the scroll bar of the original scroll object  */
.react-demo::-webkit-scrollbar {
  width: 0;
}

Specific use of scroll bars

For specific use, we added this scroll bar in Grid


import ScrollBar from '../components/scrollbar';

// Grid  Native scroll, triggering callback function 
onScroll = () => {
  // todo: when clientWidth is smaller than innerWidth, don't show bottom scrollBar
  let scrollLeftRatio = this._scrollLeft / (clientWidth - window.innerWidth);
  //  When native DOM When scrolling left and right, get the proportion (offset) of the current scroll / Full width) and set the scroll bar to scroll 
  this.setScrollLeftRatio(scrollLeftRatio);
}

setScrollLeftRatio = (scrollLeftRatio) => {
  this.props.eventBus.dispatch('set-scrollbar-left', scrollLeftRatio);
}

//  In the original scroll element, pass in eventBus Which is convenient for event value transfer processing 
// <ScrollBar eventBus={this.props.eventBus}/>

Custom scrollbars also have many open source third-party components, and we prefer to use the third-party library to implement them. (There are many considerations for handling scroll bar calculations.)


Related articles: