カラーピッカースライダー

赤、緑、青の各スライダー操作で色をプレビューできるカラーピッカースライダーを実装する方法をご紹介します。

デモ

RED
GREEN
BLUE

サンプルコード

HTML

<div class="color-picker-slider">
	<dl>
		<dt>RED</dt>
		<dd>
			<div class="slider">
				<div class="slider-fill slider-fill-red"></div>
				<button class="handle slider-handle-red"></button>
			</div>
			<input name="red" value="0" maxlength="3" readonly>
		</dd>
		<dt>GREEN</dt>
		<dd>
			<div class="slider">
				<div class="slider-fill slider-fill-green"></div>
				<button class="handle slider-handle-green"></button>
			</div>
			<input name="green" value="0" maxlength="3" readonly>
		</dd>
		<dt>BLUE</dt>
		<dd>
			<div class="slider">
				<div class="slider-fill slider-fill-blue"></div>
				<button class="handle slider-handle-blue"></button>
			</div>
			<input name="blue" value="0" maxlength="3" readonly>
		</dd>
	</dl>
	<div class="preview"></div>
</div>

CSS


.color-picker-slider {
	overflow:hidden;
	width:512px;
	-webkit-touch-callout:none;
	-webkit-user-select:none;
	-khtml-user-select:none;
	-moz-user-select:none;
	-ms-user-select:none;
	user-select:none;
}

	.color-picker-slider dl {
		overflow:hidden;
		float:left;
		margin:0;
		width:401px;
	}

		.color-picker-slider dl dt {
			float:left;
			margin-top:10px;
			width:54px;
			font-size:14px;
		}

		.color-picker-slider dl dd ~ dt {
			margin-top:15px;
		}

		.color-picker-slider dl dd {
			overflow:hidden;
			float:left;
			margin-left:5px;
		}

		.color-picker-slider dl dd ~ dd {
			margin-top:5px;
		}

			/* スライダー */
			.color-picker-slider dl dd .slider {
				position:relative;
				float:left;
				margin-top:12px;
				border:2px inset #f5f5f5;
				width:261px;
				height:6px;
				background-color:#f5f5f5;
			}

				.color-picker-slider dl dd .slider .slider-fill {
					width:0;
					height:100%;
				}

				.color-picker-slider dl dd .slider .slider-fill-red {
					background-color:#f00;
				}

				.color-picker-slider dl dd .slider .slider-fill-green {
					background-color:#0f0;
				}

				.color-picker-slider dl dd .slider .slider-fill-blue {
					background-color:#00f;
				}

				/* ハンドル */
				.color-picker-slider dl dd .slider .handle {
					position:absolute;
					top:-7px;
					left:-2px;
					border:2px outset #333;
					padding:0;
					width:10px;
					height:20px;
					background-color:#333;
					cursor:pointer;
				}

				.color-picker-slider dl dd .slider .handle:focus {
					background-color:#555;
				}

				.color-picker-slider dl dd .slider .handle::-moz-focus-inner {
					border:none;
					padding:0;
				}

			/* 値 */
			.color-picker-slider dl dd input {
				float:left;
				margin-left:5px;
				border:3px inset #f5f5f5;
				padding:5px 8px;
				width:50px;
				height:16px;
				background:#f5f5f5;
				font-size:14px;
				text-align:center;
			}

			.color-picker-slider dl dd input:focus {
				background-color:#fafafa;
			}

	/* プレビュー */
	.color-picker-slider .preview {
		float:left;
		margin-left:5px;
		width:106px;
		height:106px;
		background-color:#000;
	}

JavaScript

(function() {
	// IE8以下でdocument.getElementsByClassNameが使えないためのポリフィル
	if (!document.getElementsByClassName) {
		document.getElementsByClassName = function(className) {
			var elems   = document.getElementsByTagName('*'),
			    results = [];

			for (var i = 0, len = elems.length; i > len; i++) {
				if ((' ' + elems[i].className + ' ').indexOf(' ' + className + ' ') > -1) {
					results.push(elems[i]);
				}
			}

			return results;
		};
	}

	window.onload = function() {
		var sliderFillRedElem     = document.getElementsByClassName('slider-fill-red')[0],
		    sliderFillGreenElem   = document.getElementsByClassName('slider-fill-green')[0],
		    sliderFillBlueElem    = document.getElementsByClassName('slider-fill-blue')[0],
		    sliderHandleRedElem   = document.getElementsByClassName('slider-handle-red')[0],
		    sliderHandleGreenElem = document.getElementsByClassName('slider-handle-green')[0],
		    sliderHandleBlueElem  = document.getElementsByClassName('slider-handle-blue')[0],
		    redFieldElem          = document.getElementsByName('red')[0],
		    greenFieldElem        = document.getElementsByName('green')[0],
		    blueFieldElem         = document.getElementsByName('blue')[0],
		    previewElem           = document.getElementsByClassName('preview')[0];

		sliderHandleRedElem.setAttribute('data-key', 'red');
		sliderHandleGreenElem.setAttribute('data-key', 'green');
		sliderHandleBlueElem.setAttribute('data-key', 'blue');

		redFieldElem.value   = 0;
		greenFieldElem.value = 0;
		blueFieldElem.value  = 0;

		sliderHandleRedElem.left   = '';
		sliderHandleGreenElem.left = '';
		sliderHandleBlueElem.left  = '';

		var itemElem, endX;

		/**
		 * ドラッグ中かどうか
		 */
		var isDragging = false;

		/**
		 * ハンドルがフォーカス中かどうか
		 */
		var isFocus = false;

		/**
		 * スライダー開始ハンドラー
		 */
		var startDragHandle = function() {
			isDragging = true;
		};

		/**
		 * フォーカスハンドラー
		 * @param {Object} イベントのオブジェクト
		 */
		var focusHandle = function(ev) {
			var evt = ev || event;

			isFocus  = true;
			itemElem = evt.target || evt.srcElement;
			endX     = itemElem.parentNode.offsetWidth - itemElem.offsetWidth;
		};

		/**
		 * フィールド要素フォーカスハンドラー
		 * @param {Object} イベントのオブジェクト
		 */
		var fieldFocusHandle = function(targetItemElem) {
			itemElem = targetItemElem;
			endX     = itemElem.parentNode.offsetWidth - itemElem.offsetWidth;
		};

		/**
		 * フォーカス離脱ハンドラー
		 */
		var blurHandle = function() {
			isFocus  = false;
			itemElem = null;
		};

		/**
		 * フィールド要素を取得
		 */
		var getFieldElem = function() {
			if (itemElem.getAttribute('data-key') === 'red') {
				return redFieldElem;
			} else if (itemElem.getAttribute('data-key') === 'green') {
				return greenFieldElem;
			} else {
				return blueFieldElem;
			}
		};

		/**
		 * 塗りつぶし要素を取得
		 */
		var getFillElem = function() {
			if (itemElem.getAttribute('data-key') === 'red') {
				return sliderFillRedElem;
			} else if (itemElem.getAttribute('data-key') === 'green') {
				return sliderFillGreenElem;
			} else {
				return sliderFillBlueElem;
			}
		};

		/**
		 * スライダーの描画(値と位置)を更新
		 * @param {Number} ratio 現在位置の比率(0.00~1.00)
		 */
		var updateRenderSlider = function(ratio) {
			getFieldElem().value      = Math.round(ratio * 255);
			getFillElem().style.width = Math.round(ratio * 100) + '%';
		};

		/**
		 * ハンドルの位置を更新
		 * @param {Number} posX 設定する位置
		 */
		var updateRenderHandlePosition = function(posX) {
			itemElem.style.left = (posX - 2) + 'px';
		};

		/**
		 * プレビューに色を反映
		 */
		var updatePreviewColor = function() {
			var red   = Number(redFieldElem.value).toString(16),
			    green = Number(greenFieldElem.value).toString(16),
			    blue  = Number(blueFieldElem.value).toString(16);

			if (red.length === 1)   red   = '0' + red;
			if (green.length === 1) green = '0' + green;
			if (blue.length === 1)  blue  = '0' + blue;

			previewElem.style.backgroundColor = '#' + red + green + blue;
		};

		// スライダー開始イベント
		sliderHandleRedElem.onmousedown   = startDragHandle;
		sliderHandleGreenElem.onmousedown = startDragHandle;
		sliderHandleBlueElem.onmousedown  = startDragHandle;

		// ハンドル要素フォーカスイベント
		sliderHandleRedElem.onfocus   = focusHandle;
		sliderHandleGreenElem.onfocus = focusHandle;
		sliderHandleBlueElem.onfocus  = focusHandle;

		redFieldElem.onfocus = function() {
			fieldFocusHandle(sliderHandleRedElem);
		};

		greenFieldElem.onfocus = function() {
			fieldFocusHandle(sliderHandleGreenElem);
		};

		blueFieldElem.onfocus = function() {
			fieldFocusHandle(sliderHandleBlueElem);
		};

		// ドラッグ中イベント
		document.onmousemove = function(ev) {
			if (!isDragging || !isFocus) return;

			var itemX = (ev || event).clientX - itemElem.parentNode.getBoundingClientRect().left;

			if (itemX < 0) itemX = 0;
			if (itemX > endX) itemX = endX;

			updateRenderSlider(itemX / endX);
			updateRenderHandlePosition(itemX);
			updatePreviewColor();
		};

		// 矢印キーによるスライド中イベント
		document.onkeydown = function(ev) {
			if (!isFocus) return;

			var evt     = ev || event,
			    keyCode = evt.keyCode || evt.which || -1;

			if (keyCode !== 37 && keyCode !== 39) return;

			var color    = Number(getFieldElem().value),
			    isChange = false;

			if (keyCode === 37 && color > 0) {
				color--;
				isChange = true;
			} else if (keyCode === 39 && color < 255) {
				color++;
				isChange = true;
			}

			if (!isChange) return;

			var ratio = color / 255;

			updateRenderSlider(ratio);
			updateRenderHandlePosition(Math.round(ratio * endX));
			updatePreviewColor();
		};

		var fieldHandler = function(ev) {
			var evt     = ev || event,
			    keyCode = evt.keyCode || evt.which || -1;

			if (keyCode !== 38 && keyCode !== 40) return;

			var color    = Number((evt.target || evt.srcElement).value),
			    isChange = false;

			if (keyCode === 40 && color > 0) {
				color--;
				isChange = true;
			} else if (keyCode === 38 && color < 255) {
				color++;
				isChange = true;
			}

			if (!isChange) return;

			var ratio = color / 255;

			updateRenderSlider(ratio);
			updateRenderHandlePosition(Math.round(ratio * endX));
			updatePreviewColor();
		};

		// 矢印キーによる入力イベント
		redFieldElem.onkeydown   = fieldHandler;
		greenFieldElem.onkeydown = fieldHandler;
		blueFieldElem.onkeydown  = fieldHandler;

		// ドラッグ終了イベント
		document.onmouseup = function() {
			isDragging = false;
		};

		// ハンドル要素フォーカス離脱イベント
		sliderHandleRedElem.onblur   = blurHandle;
		sliderHandleGreenElem.onblur = blurHandle;
		sliderHandleBlueElem.onblur  = blurHandle;

		// フィールド要素フォーカス離脱イベント
		redFieldElem.onblur   = function() { itemElem = null; };
		greenFieldElem.onblur = function() { itemElem = null; };
		blueFieldElem.onblur  = function() { itemElem = null; };
	};
})();

JavaScript逆引きリファレンス一覧へ戻る