input type="range"でつまみのない丸っこいスライダーを作る

作りたいもの

つまみを非表示もどきにして丸っこくしたい

動作確認環境

コード

まずは HTML と CSS でできるところまで作るゾ1

<input type="range" value="0" min="0" step="1" max="5" />
input[type="range"] {
  -webkit-appearance: none; /* デフォルトのCSSをリセット */
  --range-height: 20px;
  --range-width: 250px;
  width: var(--range-width);
  height: var(--range-height);
  border-radius: 10px;
  background-color: skyblue;
  cursor: pointer; /* つまんでる感を出す(←?) */
  /* つまみのCSS */
  &::-webkit-slider-thumb {
    -webkit-appearance: none; /* デフォルトのCSSをリセット */
    width: 0; /* つまみを非表示に見せるため0にする */
    height: 0; /* つまみを非表示に見せるため0にする */
    border: calc(var(--range-height) / 2) solid blue; /* range の半分の高さで先っちょの丸みを作る */
    border-radius: 50%;
  }
  &:focus {
    outline: none;
  }
}

デフォルトの CSS をリセットしたら、つまみの前後で色の変化がなくなるの悲しみ。

ということで JS でつまみの前後の色を変える。

(() => {
  const input = document.querySelector('input[type="range"]');
  const min = input.min;
  const max = input.max;
  const width = parseInt(window.getComputedStyle(input).width);

  setRangeStyle(input, min, max, width);
  input.addEventListener("input", () => {
    setRangeStyle(input, min, max, width);
  });
})();
function setRangeStyle(input, min, max, width) {
  const border = (width / max) * input.value;
  // グラデの変化を急にしてつまみの前後を再現する
  input.style.background = `linear-gradient(90deg, blue ${border}px, skyblue ${border}px)`;
}

やったねたえちゃん!完成だよ!

⊂ 二二二( ^ ω ^)二二 ⊃ ブーン

(^ ω ^) おっ?

(´・ω・`)・・・?

Σ(゜ □ ゜;)ナンテコッタイ!!!

適当な div を作って step の箇所を確認してみる2

ズレてやがる・・・

多分、開始と終了で border radius の起点が変わるから、中間点で調整されていってズレてくっぽい(伝われ)

ズレないようにつまみの位置を調整してみる

input[type="range"] {
  -webkit-appearance: none;
  --range-height: 20px;
  --range-width: 250px;
  width: var(--range-width);
  height: var(--range-height);
  --slider-thumb-right: var(--range-width); /* 追加。初期値をrangeでみたときに0始まりになるようにする */
  border-radius: 10px;
  background-color: skyblue;
  cursor: pointer;
  position: relative; /* 追加 */
  &::-webkit-slider-thumb {
    position: absolute; /* 追加 */
    top: 0; /* 追加 */
    right: var(--slider-thumb-right); /* 追加 */
    -webkit-appearance: none;
    width: 0;
    height: 0;
    border: calc(var(--range-height) / 2) solid blue;
    border-radius: 50%;
  }
  &:focus {
    outline: none;
  }
}
(() => {
  const input = document.querySelector('input[type="range"]');
  const min = input.min;
  const max = input.max;
  const style = window.getComputedStyle(input);
  const width = parseInt(style.width);
  const height = parseInt(style.height);

  setRangeStyle(input, min, max, width, height);
  input.addEventListener("input", () => {
    setRangeStyle(input, min, max, width, height);
  });
})();

function setRangeStyle(input, min, max, width, height) {
  const border = (width / max) * input.value;
  // グラデの変化を急にしてつまみの前後を再現する
  input.style.background = `linear-gradient(90deg, blue ${border}px, skyblue ${border}px)`;
  let right = height / 2;
  if (input.value === min) {
    right = height; // つまみの起点 = rangeの最小値と同じ
  } else if (input.value === max) {
    right = 0; // つまみの終点 = rangeの最大値と同じ
  }
  input.style.setProperty(
    "--slider-thumb-right",
    `calc(${(width / max) * (max - input.value)}px - ${right}px)`
  );
}

✌✌✌

CodePen

See the Pen input range by m1y474 (@m1y474) on CodePen.

最後に

このブログ書いてるときに、JS使わずにワイがやりたいことやってる人見つけて鬱\(^o^)/

See the Pen Input Range with Gradient colors (CSS) by Victoria Azola Silva (@VickyAzola) on CodePen.

追記

でも↑これだとグラデの割合が絶対的にしか指定できないっぽい!ワイの作ったやつなら相対的にできるゾ!メンタル保てる!!

See the Pen input range gradient by m1y474 (@m1y474) on CodePen.

参考

zenn.dev


  1. WebKit 系のプロパティ使ってるから Safari でも動くと思うけど動作確認はしてないから注意してくれよな!Firefox はもちろん確認してないゾ!
  2. もしかしたら range の中に区切り線の入れられるのかもしれないけどやり方がわからなかった。