Home > Blockchain >  HTML slider (input of type range) centered on 0
HTML slider (input of type range) centered on 0

Time:09-14

This is a classical enter image description here

CodePudding user response:

let inputs = document.querySelectorAll("input[type=range]");

inputs.forEach((range) => {
  range.addEventListener("input", () => {
    styleRange(range);

    // debugging porpuses, delete the console.logs then
    console.clear();
    console.log(range.value);
  });
});

function styleRange(input) {
  setWidth(input);
  setPosition(input);

  function setWidth(input) {
    input.parentElement.style.setProperty(
      "--before-width",
      `${calcBeforeWidth(input)}px`,
    );
  }

  function setPosition(input) {
    input.parentElement.style.setProperty(
      "--before-left",
      `${calcPositionLeft(input)}px`,
    );
  }

  function calcBeforeWidth(input) {
    return (Math.abs(input.value) * widthPerStep(input));
  }

  function widthPerStep(input) {
    const style = window.getComputedStyle(input);
    const totalWidth = parseFloat(style.getPropertyValue("width"));
    return totalWidth / numberSteps(input);
  }

  function numberSteps(input) {
    if (input.min < input.max) return Math.abs(input.min)   Math.abs(input.max);
    return Math.abs(input.max) - Math.abs(input.min);
  }

  function calcPositionLeft(input) {
    const style = window.getComputedStyle(input);
    const totalWidth = parseFloat(style.getPropertyValue("width"));
    const inputHeight = parseFloat(style.getPropertyValue("height"));

    if (input.getAttribute("value") >= input.value)
      return (totalWidth / 2)   (inputHeight / 2) - calcBeforeWidth(input);
    return totalWidth / 2;
  }
}
body {
  margin: 0;
  height: 100vh;
  background-color: grey;
  display: grid;
  place-items: center;
}

.parent-input {
  --input-height: 0.5rem;
  --input-color: blue;
  position: relative;
  display: flex;
}

input[type="range"] {
  appearance: none;
  height: var(--input-height);
  border-radius: var(--input-height);
  accent-color: var(--input-color);
  width:90vw;
}

.parent-input::before {
  content: "";
  width: var(--before-width, 0);
  height: var(--input-height, 0);
  border-radius: var(--input-height);
  pointer-events: none;
  position: absolute;
  background-color: var(--input-color);
  top: 50%;
  transform: translateY(-50%);
  left: var(--before-left, 0);
}
<div >
  <input type="range" min="-10" value="0" max="10" />
</div>

<div >
  <input type="range" min="-80" value="0" max="80" />
</div>

CodePudding user response:

The following example has two <input>. The first <input> is rotated 180° which is the left side of zero (negative numbers) and the second <input> is the right of zero (positive numbers). The background color from zero in either direction is blue until it reaches the current value. Figure I is the formula that determines the width of that background color:

Figure I

const per = (val - min) * 100 / (max - min);

See this article for details on custom range inputs.

Details are commented in example

// Reference <form>
const slider = document.forms.slider;
// Reference all form controls
const fc = slider.elements;
// Collect all form controls with [name="rng"]
const ranges = [...fc.rng];
// For each [name="rng"] register it to the "input" event
ranges.forEach(r => r.oninput = handleRange);

/**
Event handler passes event object by default
  Get the index position of the active <input> as >idx<
  Determine the index position of the other <input> as >opp<
  Reference the other <input> as >sec<
  Reference the closest <output> as >num<
  Get the values of the active <input> [max], [min], [value] attributes
  Calculate the percentage of the active <input> width in relation to
  >min<, >max<, and >val< as >per<
*/
function handleRange(e) {
  const idx = ranges.indexOf(this);
  const opp = idx === 0 || idx % 2 === 0 ? idx   1 : idx - 1;
  const sec = ranges[opp];
  const num = this.closest('label').querySelector('.num');
  const min = this.min;
  const max = this.max;
  const val = this.value;
  const per = (val - min) * 100 / (max - min);
  // Apply >per< to active <input> background
  this.style.backgroundSize = per   '% 100%';
  // Display current <input> value
  num.value = this.classList.contains("neg") ? -val : val;
  // Toggle which <input> is active by >per<
  if (per === 0) {
    sec.classList.remove('off');
    this.classList.add('off');
  } else {
    sec.classList.add('off');
    this.classList.remove('off');
  }
  sec.value = 0;
  sec.style.backgroundSize = '0% 100%';
}
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
}

html {
  font: 300 2ch/1 Consolas;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-image: radial-gradient(rgb(237, 221, 255), rgba(195, 145, 243, 1));
}

#slider {
  display: flex;
  flex-flow: column nowrap;
  justify-content: center;
  align-items: center;
  width: 30rem;
}

label {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 12px;
}

input,
output {
  display: inline-block;
  vertical-align: middle;
  font: inherit;
}

.num {
  width: 3rem;
  padding: 5px;
  border-radius: 3px;
  text-align: center;
  color: #fff;
  background: #0045ff;
  box-shadow: 0 0 2px 0 #555;
}

.rng {
  -webkit-appearance: none;
  position: relative;
  z-index: 1;
  width: 10rem;
  height: .65rem;
  border-radius: 1rem;
  background-color: rgba(255, 255, 255, 0.6);
  background-image: linear-gradient(#0045ff, #0045ff);
  background-size: 0% 100%;
  background-position: 0.625rem 0;
  background-repeat: no-repeat;
}

.rng:focus {
  outline: none;
}

.rng::-webkit-slider-thumb {
  -webkit-appearance: none;
  height: 1.25rem;
  width: 1.25rem;
  border-radius: 1rem;
  background: #0045ff;
  box-shadow: 0 0 2px 0 #555;
  transition: background .3s ease-in-out;
  cursor: ew-resize;
}

.rng::-moz-range-thumb {
  -webkit-appearance: none;
  height: 1.25rem;
  width: 1.25rem;
  border: none;
  border-radius: 1rem;
  background: #0045ff;
  box-shadow: 0 0 2px 0 #555;
  transition: background .3s ease-in-out;
  cursor: ew-resize;
}

.rng:hover::-webkit-slider-thumb {
  background: #0002ff;
}

.rng:hover::-moz-range-thumb {
  background: #0002ff;
}

.rng:focus::-webkit-slider-thumb {
  outline: 3px solid #0045ff;
  outline-offset: 0.125rem;
}

.rng:focus::-moz-range-thumb {
  outline: 3px solid #0045ff;
  outline-offset: 0.125rem;
}

.rng.off {
  z-index: 0;
}

.rng.off::-webkit-slider-thumb {
  opacity: 0;
}

.rng.off::-moz-range-thumb {
  opacity: 0;
}

.rng::-webkit-slider-runnable-track {
  -webkit-appearance: none;
  border: none;
  background: transparent;
  box-shadow: none;
}

.rng::-moz-range-track {
  -webkit-appearance: none;
  border: none;
  background: transparent;
  box-shadow: none;
}

.neg {
  transform: rotate(180deg) translateX(-0.625rem);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.pos {
  transform: translateX(-0.625rem);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
<form id="slider">
  <label>
    <input name="rng"  type="range" min="0" max="10" value="10">
    <input name="rng"  type="range" min="0" max="10" value="0">
    <output name="num" >0</output>
  </label>

  <label>
    <input name="rng"  type="range" min="0" max="100" value="100">
    <input name="rng"  type="range" min="0" max="100" value="0">
    <output name="num" >0</output>
  </label>
</form>

  • Related