React 中的双面输入滑块,在达到初始范围 0 后,它还会向左移动并增加值,为什么?

问题描述

我正在尝试了解并修复双面输入滑块的错误

enter image description here

上面的滑块起始位置是 0,但由于光标移动不正确,我得到的值为 52。达到 0 后,我可以向左移动的值

SCSS:

$border-radius: 20px;

$primary: #709fdc;
$base: #071739;
$shadow-color:  #274684;
$lighter-shadow: rgba($shadow-color,.2);
$white: #fff;
$gray: #8c8c8c;
$lighter-gray: rgba($gray,.1);
$time-line-width: 240px;
$transition: .3s all ease;

@mixin dragIndicator($property,$background,$z-index) {
  #{$property}{
      position: absolute;
      top: 0;
      z-index: $z-index;
      width: 0;
      height: 5px;
      border-radius: 5px;
      background: $background;
      &:hover{
        &::before{
          opacity: 1;
        }
        &::after{
          opacity: 1;
        }
      }
      &::before{
        opacity: 0;
        content: attr(data-content);
        display: block;
        position: absolute;
        top: -40px;
        right: -23px;
        width: 40px;
        padding: 3px;
        text-align: center;
        color: white;
        background: $shadow-color;
        border-radius: $border-radius;
    }
    &::after{
      opacity: 0;
      content:'';
      display: block;
      position: absolute;
      top: -18px;
      right: -8px;
      border-top: 8px solid $shadow-color;
      border-left:8px solid transparent;
      border-right:8px solid transparent;
    }
    #{$property}-drag{
      position: absolute;
      right: -7.5px;
      top: -5px;

      width: 15px;
      height: 15px;

      border-radius: 50%;
      background: $base;
      transition: all .3s;
      &:hover{
        Box-shadow: 0 0 0 6px $lighter-shadow;
      }
    }
  }
}

body{
  font-family: 'Rubik',sans-serif;
  color: $base;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  background: $lighter-gray;
  .card{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 50px;
    padding-top: 25px;
    margin-top: 40px;
    border-radius: $border-radius;
    Box-shadow: 0px 0px 20px 0px $lighter-shadow;
    background: $white;
    overflow: hidden;
    h2{
      margin-bottom: 40px;
    }
    .current-value{
      width: 100%;
      
      label{
        display: inline-flex;
        width: 50px;
        font-size: 20px;
      }
      input{
          margin: 0;
          max-width: 40px;
          margin-bottom: 5px;
          font-size: 16px;
          color: white;
          padding: 5px;
          padding-left: 15px;
          border: none;
          border-radius: $border-radius;
          background: $shadow-color;
        } 
    }
    .values{
      display: flex;
      justify-content: space-between;
      font-weight: 600;
      margin-top: 30px;
      margin-bottom: 10px;
      width: $time-line-width;
    }
    #slider{
      position: relative;
      margin: 0 auto;
      width: $time-line-width;
      height: 5px;
      background: $primary;
      border-radius: 5px;
      cursor: pointer;
      
      @include dragIndicator("#min",$primary,2);
      @include dragIndicator("#max",$shadow-color,1);
    }
  }
}


.fa-instagram{
  position: absolute;
  color: $base;
  top: 3%;
  right: 2%;
  font-size: 38px;
}
.fa-instagram:hover{
  font-size: 42px;
  color: $shadow-color;
  transition: all .1s linear;
  cursor: pointer;
}


input[type="number"]::-webkit-outer-spin-button,input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"] {
    -moz-appearance: textfield;
}

*:focus{
  outline: none;
  Box-shadow: 0 0 0 2px $primary;
}

反应代码

class DoubleRangeSlider extends React.Component {
  state = {
    sliderWidth: 0,offsetSliderWidht: 0,min: 0,max: 200,minValueBetween: 10,currentMin: 55,inputMin: 55,currentMax: 100,inputMax: 100
  };

 componentDidMount() {
   const { currentMin,currentMax,max } = this.state;
   
   this.minValue.style.width = (currentMin*100)/max + "%";
   this.maxValue.style.width = (currentMax*100)/max + "%";
   
   this.setState({
     sliderWidth: this.slider.offsetWidth,offsetSliderWidht: this.slider.offsetLeft,})
 }

  setMin = (e) => {
    const { min,max,minValueBetween } = this.state;
    const inputMin = e.target.value;
    
    this.setState({
        inputMin
    });
    
    if((inputMin >= min) && (inputMin <= (currentMax-minValueBetween))){
      this.setState({
        currentMin: parseInt(inputMin)
      }); 

      this.minValue.style.width = (inputMin*100)/max + "%";
    }
  }

  changeMinValue = (e) => {
    e.preventDefault();

    document.addEventListener('mousemove',this.onMouseMoveMin);
    document.addEventListener('mouseup',this.onmouseupMin);
    
    document.addEventListener('touchmove',this.onMouseMoveMin);
    document.addEventListener('touchend',this.onmouseupMin);
  }

  onMouseMoveMin = (e) => {
    const { min,minValueBetween,sliderWidth,offsetSliderWidht } = this.state;
    
    const dragedWidht = e.clientX - offsetSliderWidht;
    const dragedWidhtInPercent = (dragedWidht*100)/sliderWidth;
    const currentMin = Math.abs(parseInt((max * dragedWidhtInPercent)/100));
    
    console.log(e.pageX,e.clientX,offsetSliderWidht);
    
    console.log(currentMin,(currentMax-minValueBetween));
    
    console.log((max * dragedWidhtInPercent)/100);
 
    if( (currentMin >= min) && (currentMin <= (currentMax-minValueBetween))){
      this.minValue.style.width = dragedWidhtInPercent + "%";
      this.minValue.dataset.content = currentMin;

      this.setState({
        currentMin,inputMin: currentMin
      })
    }
  }

  onmouseupMin = () => {
    document.removeEventListener('mouseup',this.onmouseupMin);
    document.removeEventListener('mousemove',this.onMouseMoveMin);
    
    document.removeEventListener('touchend',this.onMouseMoveMin);
    document.removeEventListener('touchmove',this.onmouseupMin);
  }
  
  
  setMax = (e) => {
    const { min,currentMin,minValueBetween } = this.state;

    const inputMax = e.target.value;
    
    this.setState({
        inputMax
    });

    if((inputMax >= currentMin + minValueBetween) && (inputMax <= max)){
      
      this.setState({
        currentMax: parseInt(inputMax)
      });
      this.maxValue.style.width = (inputMax*100)/max + "%";
    }
      
  }
  
  changeMaxValue = (e) => {
    e.preventDefault();

    document.addEventListener('mousemove',this.onMouseMoveMax);
    document.addEventListener('mouseup',this.onmouseupMax);
    
    document.addEventListener('touchmove',this.onMouseMoveMax);
    document.addEventListener('touchend',this.onmouseupMax);
  }

  onMouseMoveMax = (e) => {
    const { max,offsetSliderWidht} = this.state; 
    const maxWalueThumb = this.maxValue;
    const dragedWidht = e.clientX - offsetSliderWidht;
    const dragedWidhtInPercent = (dragedWidht*100)/sliderWidth;
    const currentMax = Math.abs(parseInt((max * dragedWidhtInPercent)/100));
    
    if( (currentMax >= (currentMin + minValueBetween)) && (currentMax <= max)){
      
      maxWalueThumb.style.width = dragedWidhtInPercent + "%";
      maxWalueThumb.dataset.content = currentMax;
      this.setState({
        currentMax,inputMax: currentMax
      })
    }
  }

  onmouseupMax = () => {
    document.removeEventListener('mouseup',this.onmouseup);
    document.removeEventListener('mousemove',this.onMouseMoveMax);
    
    document.removeEventListener('touchend',this.onmouseup);
    document.removeEventListener('touchmove',this.onMouseMoveMax);
  }
  
  maxForMin = () => {
    const { currentMax,minValueBetween} = this.state; 
    return currentMax - minValueBetween;
  }
  
  minForMax = () => {
    const { currentMin,minValueBetween} = this.state; 
    return currentMin + minValueBetween;
  }


  render() {
    const { min,inputMin,inputMax,minValueBetween } = this.state;
    
    return (
      <div className="card"> 
        <h2>Double range slider</h2>
        <div className="current-value">
          <label htmlFor="min-input">Min: </label>
          <input 
            id="min-input" 
            type="number" 
            onChange={this.setMin} 
            value={inputMin}
            min={min}
            max={this.maxForMin}/>
          
          <br/>
          <label htmlFor="max-input">Max: </label>
          <input 
            id="max-input" 
            type="number" 
            onChange={this.setMax} 
            value={inputMax}
            min={this.minForMax}
            max={max}/>
          
        </div>
        
        <div className="values">
          <div>{ min }</div>
          <div>{ max }</div>
        </div>
          
        <div ref={ref => this.slider = ref} id="slider">

          <div ref={ref => this.minValue = ref} id="min" data-content={currentMin}>
            <div ref={ref => this.minValueDrag = ref} id="min-drag" onMouseDown ={this.changeMinValue} onTouchStart={this.changeMinValue}></div>
          </div>

          <div ref={ref => this.maxValue = ref} id="max" data-content={currentMax}>
            <div ref={ref => this.maxValueDrag = ref} id="max-drag" onMouseDown={this.changeMaxValue} onTouchStart={this.changeMaxValue}></div>
          </div>

        </div>
      </div>
    )
  }
}

ReactDOM.render(
  <DoubleRangeSlider/>,document.getElementById('root')
)

代码https://codepen.io/OlgaKoplik/pen/mdybLmE

解决方法

在 Stackoverflow 聊天社区问了同样的问题,其中一位成员帮我解决了上述问题。

问题:

const currentMin = Math.abs(parseInt((max * dragedWidhtInPercent)/100));

解决方案:他建议删除 currentMin const 中的 Math.abs()。删除后它工作正常,0后不动。

const currentMin = parseInt((max * dragedWidhtInPercent)/100);