javascript - React 中的双面输入 slider

标签 javascript reactjs slider

我在下面更新中链接的沙箱中有一个几乎可以工作的解决方案,你应该从这个开始。

我有一个不使用 React 创建的(单击“运行代码片段”)

var thumbsize = 14;

function draw(slider,splitvalue) {

    /* set function vars */
    var min = slider.querySelector('.min');
    var max = slider.querySelector('.max');
    var thumbsize = parseInt(slider.getAttribute('data-thumbsize'));
    var rangewidth = parseInt(slider.getAttribute('data-rangewidth'));
    var rangemin = parseInt(slider.getAttribute('data-rangemin'));
    var rangemax = parseInt(slider.getAttribute('data-rangemax'));

    /* set min and max attributes */
    min.setAttribute('max',splitvalue);
    max.setAttribute('min',splitvalue);

    /* set css */
    min.style.width = parseInt(thumbsize + ((splitvalue - rangemin)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
    max.style.width = parseInt(thumbsize + ((rangemax - splitvalue)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
    min.style.left = '0px';
    max.style.left = parseInt(min.style.width)+'px';
    
    /* correct for 1 off at the end */
    if(max.value>(rangemax - 1)) max.setAttribute('data-value',rangemax);

    /* write value and labels */
    max.value = max.getAttribute('data-value'); 
    min.value = min.getAttribute('data-value');

}

function init(slider) {
    /* set function vars */
    var min = slider.querySelector('.min');
    var max = slider.querySelector('.max');
    var rangemin = parseInt(min.getAttribute('min'));
    var rangemax = parseInt(max.getAttribute('max'));
    var avgvalue = (rangemin + rangemax)/2;

    /* set data-values */
    min.setAttribute('data-value',rangemin);
    max.setAttribute('data-value',rangemax);
    
    /* set data vars */
    slider.setAttribute('data-rangemin',rangemin); 
    slider.setAttribute('data-rangemax',rangemax); 
    slider.setAttribute('data-thumbsize',thumbsize); 
    slider.setAttribute('data-rangewidth',slider.offsetWidth);

    /* draw */
    draw(slider,avgvalue);

    /* events */
    min.addEventListener("input", function() {update(min);});
    max.addEventListener("input", function() {update(max);});
}

function update(el){
    /* set function vars */
    var slider = el.parentElement;
    var min = slider.querySelector('#min');
    var max = slider.querySelector('#max');
    var minvalue = Math.floor(min.value);
    var maxvalue = Math.floor(max.value);
    
    /* set inactive values before draw */
    min.setAttribute('data-value',minvalue);
    max.setAttribute('data-value',maxvalue);

    var avgvalue = (minvalue + maxvalue)/2;

    /* draw */
    draw(slider,avgvalue);
}

var sliders = document.querySelectorAll('.min-max-slider');
sliders.forEach( function(slider) {
    init(slider);
});
.min-max-slider {
  position: relative;
  width: 200px;
  text-align: center;
  margin-bottom: 50px;
 }

.min-max-slider > label {
  display: none;
 }
 
.min-max-slider > input {
  cursor: pointer;
  position: absolute;
}


/* webkit specific styling */
.min-max-slider > input {
  -webkit-appearance: none;
  outline: none!important;
  background: transparent;
  background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}

.min-max-slider > input::-webkit-slider-thumb {
  -webkit-appearance: none; /* Override default look */
  appearance: none;
  width: 14px; /* Set a specific slider handle width */
  height: 14px; /* Slider handle height */
  background: #eee; /* Green background */
  cursor: pointer; /* Cursor on hover */
  border: 1px solid gray;
  border-radius: 100%;
}

.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<div class="min-max-slider" data-legendnum="2">
    <label for="min">Minimum price</label>
    <input id="min" class="min" name="min" type="range" step="1" min="0" max="3000" />
    <label for="max">Maximum price</label>
    <input id="max" class="max" name="max" type="range" step="1" min="0" max="3000" />
</div>

我尝试使用 React 创建相同类型的组件。但是每个拇指 handle 只能滑动到中间点,与非 react 示例一样,较小的 slider 根本不能高于较高的 slider ,反之亦然。
我还想要一些方法使超出拇指 handle 范围的 slider 颜色更深。

var thumbsize = 14;

const Slider = ({min, max}) => {
    const avg = (min + max)/2;
  const width = 300
  const minWidth = thumbsize + ((avg - min)/(max - min))*(width - (2*thumbsize))
  const styles={
      min:{
        width: minWidth,
        left: 0
      },
      max:{
        width: thumbsize + ((max - avg)/(max - min))*(width - (2*thumbsize)),
        left: minWidth
      }
  }
  
    return (
    <div 
      class="min-max-slider"
      data-legendnum="2"
      data-rangemin={min}
      data-rangemax={max}
      data-thumbsize={thumbsize}
      data-rangewidth={width}
    >
      <label htmlFor="min">Minimum price</label>
      <input 
        id="min"
        className="min"
        style={styles.min}
        name="min"
        type="range"
        step="1"
        min={min}
        max={avg}
        data-value={min}
       />
      <label htmlFor="max">Maximum price</label>
      <input
        id="max"
        className="max"
        style={styles.max}
        name="max"
        type="range"
        step="1"
        min={avg}
        max={max}
        data-value={max}
       />
    </div>
  )
}

ReactDOM.render(<Slider min={300} max={3000} />, document.querySelector("#root"))
 



.min-max-slider {
  position: relative;
  width: 200px;
  text-align: center;
  margin-bottom: 50px;
 }

.min-max-slider > label {
  display: none;
 }
 
.min-max-slider > input {
  cursor: pointer;
  position: absolute;
}


/* webkit specific styling */
.min-max-slider > input {
  -webkit-appearance: none;
  outline: none!important;
  background: transparent;
  background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}

.min-max-slider > input::-webkit-slider-thumb {
  -webkit-appearance: none; /* Override default look */
  appearance: none;
  width: 14px; /* Set a specific slider handle width */
  height: 14px; /* Slider handle height */
  background: #eee; /* Green background */
  cursor: pointer; /* Cursor on hover */
  border: 1px solid gray;
  border-radius: 100%;
}

.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>


更新
使用下面lissetdm的解决方案,我已经能够创建一个双面 slider ,您可以在this sandbox上查看。 ,但它仍然存在一些问题。
  • 橙色看起来并不总是与灰色重叠

  • enter image description here
    有时,出乎意料的是,这种差异非常明显
    enter image description here
    enter image description here
  • 当两个拇指 block 都被拖到边缘时,一个将另一个向下推

  • enter image description here
  • 我似乎无法删除这个轮廓,它并不总是出现

  • enter image description here

    我在 中对此进行了测试火狐

    最佳答案

    随着 react 的工作,您需要将 avg 移动到组件的状态,并在输入 onChange 事件期间更新它的值:

    const Slider = ({ min, max }) => {
      const [avg, setAvg] = useState((min + max) / 2);
      const [minVal, setMinVal] = useState(avg);
      const [maxVal, setMaxVal] = useState(avg);
    
      const width = 300;
      const minWidth =
        thumbsize + ((avg - min) / (max - min)) * (width - 2 * thumbsize);
      const styles = {
        min: {
          width: minWidth,
          left: 0
        },
        max: {
          width: thumbsize + ((max - avg) / (max - min)) * (width - 2 * thumbsize),
          left: minWidth
        }
      };
    
      useEffect(() => {
        setAvg((maxVal + minVal) / 2);
      }, [minVal, maxVal]);
    
    
      return (
        <div
          className="min-max-slider"
          data-legendnum="2"
          data-rangemin={min}
          data-rangemax={max}
          data-thumbsize={thumbsize}
          data-rangewidth={width}
        >
          <label htmlFor="min">Minimum price</label>
          <input
            id="min"
            className="min"
            style={styles.min}
            name="min"
            type="range"
            step="1"
            min={min}
            max={avg}
            value={minVal}
            onChange={({ target }) => setMinVal(Number(target.value))}
          />
          <label htmlFor="max">Maximum price</label>
          <input
            id="max"
            className="max"
            style={styles.max}
            name="max"
            type="range"
            step="1"
            min={avg}
            max={max}
            value={maxVal}
            onChange={({ target }) => setMaxVal(Number(target.value))}
          />
        </div>
      );
    };
    
    更新

    I can't seem to remove this outline, which doesn't always appear


    出现此行是因为浏览器检测到输入无效。要禁用此效果,请使用:
    input[type="range"]:invalid {
      box-shadow: none;
    }
    

    The orange doesn't always look like it overlaps the gray



    When both thumb pieces are dragged to the edges, the one pushes the other down


    我无法重现这种情况,但我建议您使用最小和最大位置控制背景颜色:
    CSS:
    input[type="range"] {
      --minRangePercent: 0%;
      --maxRangePercent: 0%;
      height: .4rem;
    }
    
    .min-max-slider > input.min {
      background-image: linear-gradient(
        to right,
        silver 0%,
        silver var(--minRangePercent),
        orange var(--minRangePercent),
        orange 100%
      );
    }
    
    .min-max-slider > input.max {
      background-image: linear-gradient(
        to right,
        orange 0%,
        orange var(--maxRangePercent),
        silver var(--maxRangePercent),
        silver 100%
      );
    }
    
    JS:
    const minPercent = ((minVal - min) / (avg - min)) * 100;
    const maxPercent = ((maxVal - avg) / (max - avg)) * 100;
    const styles = {
        min: {
          width: minWidth,
          left: 0,
          "--minRangePercent": `${minPercent}%`
        },
        max: {
          width: thumbsize + ((max - avg) / (max - min)) * (width - 2 * thumbsize),
          left: minWidth,
          "--maxRangePercent": `${maxPercent}%`
        }
     };
    
    Working example

    关于javascript - React 中的双面输入 slider ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65095361/

    相关文章:

    javascript - 有没有比没有 else 子句的 if 语句更优雅的解决方案?

    html - 固定导航栏在滚动时被图像 slider 重叠

    reactjs - react Hook : how to handle co-dependent useCallbacks

    javascript - 更新对象列表中的特定属性

    javascript - 带三元运算符的 ReactJs 语法错误

    javascript - slider 问题 - 最后一张幻灯片上的奇怪重置

    css - 如何在 Zerif Lite Wordpress 主题中添加响应 slider

    javascript - 在我在Iphone中选择日期之前,html输入类型日期调用javascript函数

    javascript - [Vue 警告] : Error in render: "TypeError: Converting circular structure to JSON

    javascript - 从 dom 中删除 iframe