1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2つのthumbを持つレンジスライダーを作る

Posted at

概要

<input>要素のrange型を利用して「◯◯〜◯◯」のような範囲の選択ができるレンジスライダーを作った。

ポイントは、

  • range型をz軸に2つ重ねる
  • 手前のスライダーで奥になるスライダーの選択色を隠す

背景と課題

スライダーで値を選択する<input>要素としてrange型がある。これはつまみ(thumb)が1つのスライダーで、thumbを動かすことで値を選択できるが、「◯◯〜◯◯」といった選択ができない。

そこで、直感的にもわかりやすく、thumbが1つだけのスライダーと同じ大きさでthumbを2つ持つスライダーを実現する。

実装

完成形

作ったレンジスライダーです。
fig2.png

前提となるフレームワーク

  • jQuery
  • Bootstrap

実装は以下のとおりです。

HTML
<div class="multi-slider">
    <input type="range" class="slider-lower" value="30" min="0" max="100" data-pb-color="var(--bs-secondary-bg)">
	<input type="range" class="slider-upper" value="70" min="0" max="100" data-pb-color="var(--bs-primary)">
</div>
CSS
.slider-lower, .slider-upper {
    -webkit-appearance: none;
    appearance: none;
    cusor: pointer;
    outline: none;
    height: 8px;
    border-radius: 8px;
    background: var(--bs-secondary-bg);
}
.multi-slider ::-webkit-slider-thumb {
    background-color: var(--bs-primary);
    pointer-events: auto;
}
.multi-slider {
    display: grid;
    padding: 8px;
    width: 100%;
    /* thumb以外の操作を無効にする */
    pointer-events: none;
}
.slider-lower, .slider-upper {
    grid-column: 1;
    grid-row: 2;
}
.slider-lower {
    z-index: 1;
}
JavaScript
$(function(){
    $('.slider-lower, .slider-upper').on('load input', function(e){
        if ( Number($('.slider-lower').val()) >= Number($('.slider-upper').val()) ) {
            if ( $(this).hasClass('slider-lower') ){
                $(this).val(Number($('.slider-upper').val())-1);
            } else {
                $(this).val(Number($('.slider-lower').val())+1);
            }
        }
        ratio = ($(this).val()/$(this).prop("max"))*100;
        left = $(this).data('pb-color');
        if ( $(this).hasClass('slider-lower') ){
            right = 'rgba(255,255,255,0.0)';
        } else {
            right = 'var(--bs-secondary-bg)';
        }
        $(this).css('background','linear-gradient(90deg, '+left+' '+ratio+'%, '+right+' '+ratio+'%)');
    });
    $('.slider-lower, .slider-upper').trigger('load');
});

実現方法の概略

<input>要素のrange型は、要素と-webkit-slider-runnable-track-webkit-slider-thumbという2つの疑似要素から構成される。
fig1.png

基本的なアイディアは、まず、図のような2つのスライダーを用意する。
fig3.png

slider-lowerは最小値を選択するスライダーで、thumbの左を背景色に右を透明にする。slider-upperは最大値を選択するスライダーで、左を選択色に右を背景色にする。このようにthumbを境界にtrackを塗り分けたスライダーをslider-lowerが手前になるように配置することで、最小値から最大値の間だけが選択色が見えるようにする。

具体的な実装の説明

trackの塗り分け

trackの塗り分けはCSSで設定する方法とJavaScriptで設定する方法がある。今回は、調整が用意なためJavaScriptを使う。まず、ベースとなるスタイルを設定する。

CSS
.slider-lower, .slider-upper {
    -webkit-appearance: none;
    appearance: none;
    cusor: pointer;
    outline: none;
    height: 8px;
    border-radius: 8px;
    background: var(--bs-secondary-bg);
}
.multi-slider ::-webkit-slider-thumb {
    background-color: var(--bs-primary);
}

要素自体、ここではslider-lowerslider-upperにスタイルを設定する。-webkit-appearance: none;appearance: none;でウィジェットの機能を非表示にし、以下基本的なスタイルを設定する。
cursor: pointer;でカーソルがあたったときの形を設定する。
outline: none;でフォーカスがあたったときに枠を表示させない。
height, border-radiusでtrackの高さと端のスタイルを設定する。
backgroundでtrackの背景色を設定する。--bs-secodary-bgとしたのはBootstrapに合わせるため。

.multi-slider ::webkit-slider-thumbthumb{}では、thumbのスタイルを設定する。ウィジェットの標準スタイルで十分だが、ここでは選択色と合わせるためにbackground-color--bs-primaryを設定する。

次にJavaScriptで塗り分ける。
スクリプトは前掲のものを再掲する。

JavaScript
$(function(){
    $('.slider-lower, .slider-upper').on('load input', function(e){
        if ( Number($('.slider-lower').val()) >= Number($('.slider-upper').val()) ) {
            if ( $(this).hasClass('slider-lower') ){
                $(this).val(Number($('.slider-upper').val())-1);
            } else {
                $(this).val(Number($('.slider-lower').val())+1);
            }
        }
        ratio = ($(this).val()/$(this).prop("max"))*100;
        left = $(this).data('pb-color');
        if ( $(this).hasClass('slider-lower') ){
            right = 'rgba(255,255,255,0.0)';
        } else {
            right = 'var(--bs-secondary-bg)';
        }
        $(this).css('background','linear-gradient(90deg, '+left+' '+ratio+'%, '+right+' '+ratio+'%)');
    });
    $('.slider-lower, .slider-upper').trigger('load');
});

スクリプトはslider-lowerまたはslider-upperloadまたはthumbを動かされたときに実行される。
最初のif文は、左右のthumbが逆転しないように制御している。
次は、thumbの位置を求めratioに持つ。
次に、thumbの左側の色をleftに、右側の色をrightに持つ。左側の色は、HTMLで<input>要素のdata-pb-color属性に持たせている。
最後に、要素の背景色をlinear-gradientで2色に塗り分ける。
なお、本質からは外れるが、読み込み時にloadが発火されなかったのでtrigger('load')で強制的に発火させている。

ここまでで、図のようなスライダーができているはず。
fig3.png

2つのスライダーを重ねる

CSS
.multi-slider {
    display: grid;
    padding: 8px;
    width: 100%;
}
.slider-lower, .slider-upper {
    grid-column: 1;
    grid-row: 2;
}
.slider-lower {
    z-index: 1;
}

2つのスライダーをラップするdiv要素(multi-slider)をdisplay: grid;でグリッドコンテナ化し、slider-lowerslider-upperを同じセルに配置する。また、slider-lowerz-index:1;で手前に配置する。

これで見た目は完成形と同じになるが、右側のthumbを操作できないので、pointer-eventsを使って以下のように設定する。

.multi-slider ::-webkit-slider-thumb {
    pointer-events: auto;
}
.multi-slider {
    pointer-events: none;
}

multi-slider全体をpointer-events: none;でポインターを無効にする。これはtrackをクリックしたときにthumbが移動するのを防ぐ。
次に、multi-slider要素の中の-webkit-slider-thumb疑似要素だけにpointer-events: auto;で操作権を与える。

まとめ

range型の<input>属性を2つ重ねることで、「◯◯〜◯◯」といった範囲選択ができるレンジスライダーを作った。選択範囲と背景の塗り分けをCSSとJavaScriptを使って実装した。なお、今回の実装では-webkit付きの疑似要素を使っておりFirefox向けの実装は入っていない。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?