JSを使ってカラーシミュレーターを作成してみよう。
(この記事は以前に作成したものを2024年版としてリニューアルしたものです。)
作るもの
上部に色とカラーコード、下部にrgb成分を設定するスライダーと直接カラーコードを入力できるフォームが設置されている。
スライダーを動かすとリアルタイムにカラーが変化し、それに合わせて情報が書き換わる。
下のフォームに値を入れてチェックボタンを押すと
情報が書き換わる。
3桁のカラーコードを入力しても
適切に解釈される
カラーコードとして不適切値はエラーメッセージが表示される
以下のようにエラーメッセージが表示される
[0-9a-fA-F]以外の文字が使われていたり、3桁、または6桁の値でなかった場合にinvalid codeとなる。
準備
- 任意の場所(デスクトップなど)にcolorsimuという名のフォルダを作成する。
- colorsimuフォルダの中にcssフォルダ、jsフォルダを作成し、cssフォルダの中にmain.css,jsフォルダの中にmain.jsを作成する。(中身は空でよい)
- colorsimuフォルダの直下にindex.htmlを作成する。ここまででフォルダの状態は以下
index.htmlの作成
以下のようにindex.htmlを作成する
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Color Simulator</title>
<link rel="stylesheet" href="css/main.css">
<script src="js/main.js" defer></script>
</head>
<body>
<header>
<h1>Color Simulator</h1>
</header>
<main>
<!-- カラーコードの表示エリア -->
<div id="result"></div>
<!-- RGBスライダー -->
<section>
<label>
R:
<input type="range" min="0" max="255" value="255">
<span></span>
</label>
<label>
G:
<input type="range" min="0" max="255" value="255">
<span></span>
</label>
<label>
B:
<input type="range" min="0" max="255" value="255">
<span></span>
</label>
</section>
<!-- カラーコード入力 -->
<section>
<div>#<input type="text" id="code" placeholder="Enter Code:"></div>
<button id="check">check</button>
</section>
</main>
<footer>
<p>© 2024 Color Simulator</p>
</footer>
</body>
</html>
ポイント解説
カラーコードの表示エリア
<div id="result"></div>
現在の背景色のカラーコード(例:#ff0000
)が表示される領域です。このエリアの背景色や文字色がJavaScriptによって動的に変更されます。
RGBスライダー
<section>
RGBスライダーが配置されています。それぞれの色(Red, Green, Blue)の値を0~255の範囲で調整できます。<input type="range">
スライダー入力です。初期値は255に設定されています。スライダーを動かすことで、背景色が即座に変わります。<span>
スライダーの現在値を表示します。JavaScriptで動的に更新されます。
カラーコード入力
<section>
ユーザーが直接カラーコード(例:ff0000
)を入力して背景色を設定できるセクションです。<input type="text">
カラーコードを入力するためのテキストボックスです。プレースホルダーとして「Enter Code:」が表示されます。<button id="check">
入力されたカラーコードを適用するボタンです。JavaScriptのクリックイベントによって処理が実行されます。
main.cssの作成
続いてcss/main.cssを以下のように記述する
* {
box-sizing:border-box;
margin:0;
padding:0;
}
body {
background-color: #f9f9f9;
color: #333;
font-size:18px;
}
header {
background-color: #0078d4;
color: white;
text-align: center;
padding: 6px ;
}
header h1 {
font-size: 1.5rem;
}
main {
max-width: 600px;
margin: 20px auto;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* カラー表示エリア */
#result {
height: 50px;
border: 1px solid #ccc;
border-radius: 4px;
text-align: center;
line-height: 50px;
font-size: 1.5rem;
margin-bottom: 20px;
}
/* スライダーとラベル */
section label {
display: flex;
align-items: center;
margin-bottom: 10px;
}
section input[type="range"] {
margin: 0 10px;
flex: 1;
}
section span {
width: 40px;
text-align: right;
}
main section:last-child{
display:flex;
justify-content:flex-start;
gap:5px;
}
main section:last-child div{
font-size:1.5rem;
}
/* カラーコード入力 */
#code {
width:200px;
letter-spacing:2px;
padding: 5px;
margin-right: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1.5rem;
}
button {
padding: 6px 12px;
border: none;
border-radius: 4px;
background-color: #0078d4;
color: white;
cursor: pointer;
font-size:1.2rem;
}
button:hover {
opacity:0.8;
}
/* フッター */
footer {
text-align: center;
margin-top: 20px;
font-size: 1.2rem;
color: #666;
}
ポイント解説
(1)全体設定
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
- *セレクタ
全ての要素に適用されるグローバル設定です。box-sizing: border-box;
パディングやボーダーを含めたサイズを計算することで、要素のレイアウトが崩れにくくなります。margin: 0;
とpadding: 0;
要素のデフォルト余白をリセットしています。
(2)スライダーとラベル
section label {
display: flex;
align-items: center;
margin-bottom: 10px;
}
section input[type="range"] {
margin: 0 10px;
flex: 1;
}
section span {
width: 40px;
text-align: right;
}
- 配置の調整
display: flex;
とalign-items: center;
ラベル、スライダー、現在値を横並びに整列。
- スライダーのスタイル
- flex: 1;
スライダーが残りのスペースを埋めるように調整。 - margin: 0 10px;
スライダーの両端に適度なスペースを確保。
- flex: 1;
main.jsの作成
'use strict';
window.onload = () => {
/*** DOMの取得 ***/
const result = document.getElementById('result');
const sliders = document.querySelectorAll('[type="range"]');
const codeInput = document.getElementById('code');
const checkButton = document.getElementById('check');
const DARK_THRESHOLD = 380;
/*** 表示の更新処理 ***/
const updateDisplay = () => {
const rgb = Array.from(sliders).map((slider) => parseInt(slider.value));
const cCode = toHex(rgb);
updateSliders(rgb);
result.textContent = `#${cCode.toLowerCase()}`;
codeInput.value = cCode;
setColor(rgb, cCode);
};
const updateSliders = (rgb) => {
sliders.forEach((slider, index) => {
slider.value = rgb[index];
slider.nextElementSibling.textContent = rgb[index];
});
};
/*** 色の更新処理 ***/
const setColor = (rgb, cCode) => {
const isDark = rgb.reduce((sum, value) => sum + value) < DARK_THRESHOLD;
result.style.color = isDark ? 'white' : 'black';
result.style.backgroundColor = `#${cCode}`;
};
/*** 入力値からスライダーの値を設定 ***/
const setSliderValue = (inputVal) => {
const rgb = toRGB(inputVal);
if (!rgb) {
codeInput.value = 'Invalid Code!';
return;
}
updateSliders(rgb);
updateDisplay();
};
/*** RGB値からカラーコードに変換 ***/
const toHex = (rgb) =>
rgb.map((value) => value.toString(16).padStart(2, '0')).join('');
/*** カラーコードからRGBに変換 ***/
const toRGB = (code) => {
if (!/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(code)) return null;
if (code.length === 3) {
code = code.split('').map((char) => char + char).join('');
}
return [
parseInt(code.substring(0, 2), 16),
parseInt(code.substring(2, 4), 16),
parseInt(code.substring(4, 6), 16),
];
};
/*** イベントリスナーの設定 ***/
sliders.forEach((slider) => slider.addEventListener('input', updateDisplay));
checkButton.addEventListener('click', () => setSliderValue(codeInput.value));
// 初期表示の更新
updateDisplay();
};
ポイント解説
このJavaScriptコードは、HTMLとCSSで構築された「Color Simulator」アプリケーションに動的な機能を追加するものです。RGBスライダーやカラーコードの入力を通じて色を操作し、リアルタイムで結果を反映する仕組みを実現しています。それぞれの機能について詳しく解説します。
1. window.onload
window.onload = () => {
- ページが完全に読み込まれた後に実行される初期化処理を定義しています。この中で、DOMの要素取得やイベントリスナーの設定を行います。
2. DOMの取得
const result = document.getElementById('result');
const sliders = document.querySelectorAll('[type="range"]');
const codeInput = document.getElementById('code');
const checkButton = document.getElementById('check');
const DARK_THRESHOLD = 380;
result
カラーコードと背景色を表示する要素を取得します。sliders
RGBスライダーを配列形式で取得します。codeInput
カラーコードの入力フィールド。checkButton
入力されたカラーコードを適用するためのボタン。DARK_THRESHOLD
色の明るさを判定するための閾値。これを基に文字色(黒または白)を決定します。
3. 表示の更新処理
const updateDisplay = () => {
const rgb = Array.from(sliders).map((slider) => parseInt(slider.value));
const cCode = toHex(rgb);
updateSliders(rgb);
result.textContent = `#${cCode.toLowerCase()}`;
codeInput.value = cCode;
setColor(rgb, cCode);
};
- RGBスライダーの値を取得してカラーコードを計算し、以下を更新します:
- スライダーの表示。
- カラーコードのテキスト。
- 背景色や文字色。
(1)const rgb = Array.from(sliders).map((slider) => parseInt(slider.value));
このコードは、RGBスライダーの値を取得し、それを数値の配列 (rgb
) に変換する処理を行っています。以下で詳細に解説します:
1. Array.from(sliders)
sliders
は document.querySelectorAll('[type="range"]')
によって取得された NodeList
です。
NodeList
とは:- 複数の DOM 要素の集合です。
- 配列に似ていますが、厳密には配列ではないため、配列のメソッド(例:
map
)を直接使用することはできません。
Array.from(sliders)
- 目的:
NodeList
を 配列 に変換します。これにより、map
やその他の配列操作メソッドが使えるようになります。
2. .map((slider) => parseInt(slider.value))
map
メソッドは、配列の各要素を加工した新しい配列を作成します。
slider
map
によってスライダー(<input type="range">
)要素が1つずつ取り出されます。slider.value
スライダーの現在の値を文字列として取得します。- 例:
"255"
,"128"
- 例:
parseInt(slider.value)
slider.value
を文字列から整数に変換します。- 例:
"255"
→255
- 例:
3. 全体の動作
Array.from(sliders).map((slider) => parseInt(slider.value))
の処理を分解すると、以下のようになります:
sliders
を配列に変換:Array.from(sliders)
は、NodeList(例:[slider1, slider2, slider3]
)を配列として扱える形に変換します。- 各スライダーの値を取得し整数化:
map
メソッドで、各スライダー(例:slider1
)の値を取得して整数に変換します。 - 結果として新しい配列を生成:
変換された値(例:[255, 128, 64]
)が新しい配列として返されます。
4. 結果:rgb
の役割
最終的に、rgb
には以下のような値が格納されます:
const rgb = [255, 128, 64]; // 各スライダーの値
- 配列の内容:
R
スライダー、G
スライダー、B
スライダーの値がそれぞれ格納されます。 - 用途:
この配列は、色の設定(背景色や文字色の変更)や、16進数カラーコードへの変換に使われます。
5. ポイントまとめ
Array.from
を使用してNodeList
を配列に変換。map
メソッドで各スライダーの値を文字列から整数に変換。rgb
配列には[R値, G値, B値]
の形でスライダーの値が格納される。
この処理により、スライダーの値を色の操作に直接使用可能な形に変換していることがわかります。
サブ機能:updateSliders
const updateSliders = (rgb) => {
sliders.forEach((slider, index) => {
slider.value = rgb[index];
slider.nextElementSibling.textContent = rgb[index];
});
};
2. コードの詳細解説
sliders.forEach((slider, index) => {...})
sliders
は、document.querySelectorAll('[type="range"]')
によって取得されたスライダー要素の集合です。
forEach
の役割:sliders
の各スライダー要素を1つずつ取り出して処理します。- ループ内で2つの値を受け取ります:
slider
: 現在処理しているスライダー要素。index
: 現在のスライダーがsliders
配列内で何番目かを表す番号(0から開始)。
slider.value = rgb[index];
- 役割:
- スライダーの値(
value
属性)を、対応するrgb
の値に設定します。
- スライダーの値(
rgb[index]
:rgb
配列のindex
番目の値を取得します。- 例:
index
が 0 の場合はrgb[0]
、つまり R の値を取得。
- 例:
- 設定例:
rgb = [255, 128, 64]
の場合:- 最初のスライダーの値を
255
に設定。 - 2番目のスライダーの値を
128
に設定。 - 3番目のスライダーの値を
64
に設定。
- 最初のスライダーの値を
slider.nextElementSibling.textContent = rgb[index];
slider.nextElementSibling
:slider
要素(スライダー)の直後にある兄弟要素を取得します。- HTMLの構造上、これはスライダー横にある
<span>
要素に対応します。
textContent
:- この要素のテキスト内容を設定します。
- 役割:
- スライダーの現在の値をスライダー横の
<span>
要素に表示します。- 例: スライダーの値が
128
の場合、横に「128」と表示。
- 例: スライダーの値が
- スライダーの現在の値をスライダー横の
4. 色の更新処理
const setColor = (rgb, cCode) => {
const isDark = rgb.reduce((sum, value) => sum + value) < DARK_THRESHOLD;
result.style.color = isDark ? 'white' : 'black';
result.style.backgroundColor = `#${cCode}`;
};
const isDark = rgb.reduce((sum, value) => sum + value) < DARK_THRESHOLD;
コードの詳細解説
rgb
の配列:rgb
は、RGB(赤、緑、青)の色成分を格納した配列です。例えば、[255, 0, 0]
という値があれば、これは「赤色」を表します。
reduce()
メソッド:reduce()
は、配列のすべての要素を1つの値に「畳み込む(集約する)」ためのメソッドです。ここでの目的は、RGBの各色(赤、緑、青)の値を合計することです。
rgb.reduce((sum, value) => sum + value)
reduce()
は2つの引数を取ります。最初の引数は「累積値(sum
)」で、2番目は現在の配列の要素(value
)です。- この関数は、配列の最初の要素から順番に処理し、各要素を累積的に足し合わせていきます。最終的に、
rgb
配列内のすべての値の合計を返します。
rgb = [100, 150, 200]
の場合、次のように計算されます:sum = 0 + 100
→sum = 100
sum = 100 + 150
→sum = 250
sum = 250 + 200
→sum = 450
- 結果として、合計値は
450
です。
- 合計値が閾値と比較される:
< DARK_THRESHOLD
DARK_THRESHOLD
は事前に定義された閾値(例えば、380
)です。- 合計値がこの閾値より小さい場合、その色は「暗い」と判断されます。逆に、大きければ「明るい」と判断されます。
- 結果:
isDark
には、RGBの合計がDARK_THRESHOLD
より小さいかどうかに基づいてtrue
またはfalse
が代入されます。例えば、合計が450
で閾値が380
の場合、isDark
はfalse
となり、色は明るいと判断されます。
- 合計RGB値が
DARK_THRESHOLD
未満の場合、文字色を白(white
)に、それ以外の場合は黒(black
)に設定します。 - 背景色は計算されたカラーコードを反映します。
5. 入力値からスライダーの値を設定
const setSliderValue = (inputVal) => {
const rgb = toRGB(inputVal);
if (!rgb) {
codeInput.value = 'Invalid Code!';
return;
}
updateSliders(rgb);
updateDisplay();
};
- 入力されたカラーコードを解析し、スライダーの値を更新します。無効なカラーコードの場合は警告を表示します。
6. カラーコード変換処理
const toHex = (rgb) =>
rgb.map((value) => value.toString(16).padStart(2, '0')).join('');
関数の目的
この関数は、rgb
配列を受け取り、それぞれの色の成分(赤、緑、青)を16進数に変換し、最終的に一つの文字列として結合して返します。これにより、カラーコード(例: #ff5733
)を生成することができます。
コードの詳細解説
rgb.map()
:map()
は、配列の各要素に対して関数を適用し、新しい配列を返すメソッドです。ここでは、RGBの各色(赤、緑、青)を16進数に変換するために使用されています。
rgb.map((value) => value.toString(16).padStart(2, '0'))
rgb
は、RGBカラーコードを表す配列です。例えば、[255, 87, 51]
という配列が渡されると、map()
メソッドがそれぞれの色成分(255、87、51)に対して以下の処理を行います。
value.toString(16)
:toString(16)
は、数値を16進数の文字列に変換するメソッドです。ここでは、RGBの各色(0~255)を16進数の形式に変換しています。
255.toString(16)
は"ff"
に変換されます。87.toString(16)
は"57"
に変換されます。51.toString(16)
は"33"
に変換されます。
padStart(2, '0')
:padStart()
は、文字列の長さが指定した長さに満たない場合、指定した文字で文字列を埋めるメソッドです。- ここでは、16進数に変換された色成分が1桁の場合でも2桁になるようにゼロ(
'0'
)で埋めています。
- もし変換された16進数が1桁(例えば、
5
)であった場合、padStart(2, '0')
を使うことで"05"
になります。
join('')
:join('')
は、配列のすべての要素を指定した区切り文字で結合するメソッドです。ここでは区切り文字として空文字''
を指定しているため、各色の16進数の値をそのまま繋げて1つの文字列にします。
["ff", "57", "33"]
という配列をjoin('')
すると、"ff5733"
という文字列になります。
最終的な出力
この関数は、RGBの値(0~255)を16進数のカラーコードに変換して、最終的に1つのカラーコード文字列(#
付き)として返します。例えば:
- 入力:
[255, 87, 51]
- 変換後:
"#ff5733"
まとめ
この toHex
関数は、RGB配列を受け取り、その色成分を16進数に変換し、最終的にカラーコードの形式で返すものです。RGBの各色成分を toString(16)
で16進数に変換し、padStart(2, '0')
で2桁に整え、join('')
でそれらを1つの文字列として結合しています。
toHex
RGB値(10進数)を16進数カラーコードに変換します。
const toRGB = (code) => {
if (!/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(code)) return null;
if (code.length === 3) {
code = code.split('').map((char) => char + char).join('');
}
return [
parseInt(code.substring(0, 2), 16),
parseInt(code.substring(2, 4), 16),
parseInt(code.substring(4, 6), 16),
];
};
if (!/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(code)) return null;
この行は、入力されたカラーコードが有効な16進数形式であるかを確認するための検証です。
- 正規表現
/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/
:^
と$
は、それぞれ文字列の先頭と末尾を意味し、文字列全体がこのパターンに一致することを求めます。[0-9a-fA-F]{6}
は、0~9およびa~f(大文字、小文字問わず)の16進数文字が6文字続くことを意味します。これが、6桁のカラーコード(例:#ff5733
)に対応します。[0-9a-fA-F]{3}
は、16進数の文字が3文字続く形式です(例えば、ショートカラーコード:#f53
)。|
(パイプ)は「または」を意味し、3桁または6桁のカラーコードのいずれかにマッチすることを意味します。
test(code)
:test()
メソッドは、文字列code
が正規表現と一致するかどうかを判定します。正規表現に一致しない場合、null
を返すようになっています。
この行は、code
が3桁または6桁の16進数カラーコード形式でない場合、無効と見なして null
を返します。
if (code.length === 3) { code = code.split('').map((char) => char + char).join(''); }
この部分は、入力されたカラーコードが3桁の場合に、6桁に変換する処理です。
code.length === 3
:code
が3桁の場合、例えばf53
のようなショートカラーコードを処理します。
code.split('')
:split('')
メソッドは、文字列code
を1文字ずつの配列に分割します。例えば、"f53"
は["f", "5", "3"]
という配列に分割されます。
map((char) => char + char)
:map()
メソッドを使って、各文字(f
,5
,3
)を2回繰り返して結合します。例えば、"f"
は"ff"
になり、"5"
は"55"
になります。
join('')
:- 最後に、
join('')
で配列の要素を結合して、"ff5533"
という6桁のカラーコードに変換します。
- 最後に、
この変換により、f53
のような3桁のカラーコードが ff5533
に変換され、次の処理で正しくRGBに変換できるようになります。
return [
parseInt(code.substring(0, 2), 16),
parseInt(code.substring(2, 4), 16),
parseInt(code.substring(4, 6), 16)
]
この部分は、6桁のカラーコードをRGB(赤、緑、青)の配列に変換する処理です。
code.substring(0, 2)
:substring(0, 2)
は、code
の最初の2文字を抽出します(赤の成分)。- 例えば、
"ff5733"
の場合、substring(0, 2)
は"ff"
となります。
parseInt(code.substring(0, 2), 16)
:parseInt()
メソッドを使って、16進数の文字列"ff"
を10進数に変換します。"ff"
は10進数で255に相当します。- 同様に、
parseInt(code.substring(2, 4), 16)
で緑の成分("57"
→ 87)を、parseInt(code.substring(4, 6), 16)
で青の成分("33"
→ 51)を変換します。
return [255, 87, 51]
:- 最終的に、RGB配列
[255, 87, 51]
を返します。この配列が、入力された16進数カラーコードに対応するRGB値となります。
- 最終的に、RGB配列
この toRGB
関数は、16進数のカラーコード(3桁または6桁)をRGB配列に変換します。最初にコードが有効な形式かチェックし、もし3桁の場合は6桁に変換します。その後、16進数の各色成分を10進数に変換し、RGB配列として返します。
7. イベントリスナーの設定
sliders.forEach((slider) => slider.addEventListener('input', updateDisplay));
checkButton.addEventListener('click', () => setSliderValue(codeInput.value));
- スライダーの値が変更された時、
updateDisplay
が実行されます。 - 「Check」ボタンがクリックされると、入力されたカラーコードを基に色が更新されます。
8. 初期表示の更新
updateDisplay();
- ページが読み込まれた際に、初期状態でスライダーやカラーコード表示を設定します。
このスクリプトは、シンプルながらもリアルタイムで動作するカラーシミュレーターを実現しています。関数ごとに責務が分割されており、コードの再利用性やメンテナンス性が高い点が特徴です。また、イベントリスナーを適切に利用することで、インタラクティブなユーザー体験を提供しています。
コメント