TeamWorking

JavaScript 発展編

ここでは JavaScript の発展的な内容として、HTML の操作やグラフィックス関係のライブラリーについて説明する。

まずは HTML (CSS) での色の表現方法について説明し、その後、p5.js というグラフィックスを作成するための初心者向けの JavaScript のライブラリーを紹介する。さらに、一般的な HTML を操作するための JavaScript の関数についても説明する。

CSS Color

この資料のプログラム例では、手頃な例として色を扱うことが多いので、まず、最初に HTML (CSS) での色の表現方法について説明する。

CSS には "red", "blue" などの色名1で指定するほかに RGB, HSL, OkLCh などの色の表現方法がある。

RGB

RGB は、色を赤(Red)、緑(Green)、青(Blue)の光の三原色の加算で表現する方法である。

CSS では rgb(R G B) の形式の文字列で色を指定する。 R, G, B はそれぞれ赤、緑、青の成分で 0 から 255 の数値である。 以下にいくつかの例を示す。

rgb( 0 0 0) rgb(127 0 0) rgb(255 0 0)
rgb( 0 127 0) rgb(127 127 0) rgb(255 127 0)
rgb( 0 255 0) rgb(127 255 0) rgb(255 255 0)

rgb( 0 0 127) rgb(127 0 127) rgb(255 0 127)
rgb( 0 127 127) rgb(127 127 127) rgb(255 127 127)
rgb( 0 255 127) rgb(127 255 127) rgb(255 255 127)

rgb( 0 0 255) rgb(127 0 255) rgb(255 0 255)
rgb( 0 127 255) rgb(127 127 255) rgb(255 127 255)
rgb( 0 255 255) rgb(127 255 255) rgb(255 255 255)

また rgb(R G B / A) の形式の文字列で A にアルファ値(不透明度)を指定することもできる。 アルファ値は 0 から 1 の数で、0 が完全に透明、1 が完全に不透明を表す。

HSL

HSL は、色相(Hue)、彩度(Saturation)、明度(Lightness)で色を表現する方法である。 CSS では hsl(H S L) の形式の文字列で色を指定する。 H は色相で、0 から 360 の数値で表される。 0 が赤、120 が緑、240 が青を表す。 S は彩度で、0 % から 100 % で表される。 0% がグレー、100 % が最も鮮やかな色を表す。 L は明度で、0 % が黒、100 % が白を表す。

なお、最後に / A の形式を付け加えるとアルファ値も指定することができるのは RGB のときと同じである。

以下にいくつかの例を示す。

hsl(180 100% 50%) hsl(120 100% 50%) hsl( 60 50% 50%) hsl(300 50% 80%) hsl(240 25% 50%)

OkLCh

HSL は同じ明度、彩度であっても色相によって人間に感じられる明るさがかなり異なる、という欠点がある。このような欠点を解消するために提案されたのが OkLCh である。

OkLCh は、明度(Lightness)、彩度(Chroma)、色相(Hue)、で色を表現する方法である。 CSS では oklch(L C H) の形式の文字列で色を指定する。 L は明度で、0 % から 100 % で表される。 C は彩度で、0 % からのパーセントで表される。 H は色相で、0 から 360 の数値で表される。

彩度と明度を固定すると、人間の眼に“同じように”見える色相の変化を表現できる。

なお、最後に / A の形式… 。

以下にいくつかの例を示す。

oklch(50% 100% 180) oklch(50% 100% 120) oklch(50% 50% 60) oklch(100% 50% 300) oklch(50% 25% 240)

RGB, HSL, OkLCh のほかにも HWB や Lab などの色の表現方法があるが、ここでは説明を割愛する。

p5.js

p5.js は、プログラミングの初心者でも容易に扱えるように設計された、 グラフィックスやアニメーションを作成するための JavaScript のライブラリーである。 Web ページ上のエディター(p5.js Web Editor)からでも簡単に作成して試すこともできる。p5.js Web Editor で実行するときにはバージョンの選択に注意する。この資料で紹介しているプログラムは、p5.js のバージョン 2.0.0 以降で動作するが、p5.js Web Editor は最初は古いバージョンが選択されていることがある。

p5.js のセットアップ

ここでは HTML ページに p5.js で作成したグラフィックスを埋め込む方法を説明する。

まず、HTML の <head></head> タグの間に次のように書いて、 ライブラリーを読み込むようにする(2.2.2 のところは、その時点での適切なバージョンに置き換える)。

<script src='https://cdn.jsdelivr.net/npm/p5@2.2.2/lib/p5.min.js'></script>

さらに、<body></body> タグの間(の最後のほう)に次のようなコードを挿入する。

<script>
function setup() {
  createCanvas(320, 180); // キャンバスのサイズを指定
}

function draw() {
  background("lightgray"); // 背景色を設定
  stroke("green");
  fill("orange");
  ellipse(200, 100, 180, 120);
}
</script>

このコードは以下のような画像を生成する。

p5.js 例1

function setup() { ... }setup という関数を定義する部分である。... の部分は、p5.js のライブラリーによって自動的に最初に実行される。 通常はこの中でキャンバス(描画される領域)のサイズを指定して作成する。

デフォルトでは HTML 文書の末尾にキャンバスが追加される。 キャンバスの位置を変更したい場合は、HTML のなかに <canvas id="myCanvas"></canvas> のように canvas タグを先に作成しておき、createCanvas の第 3 引数としてそのキャンバス要素を指定する。

<canvas id="myCanvas"></canvas><script>
function setup() {
  createCanvas(320, 180, document.getElementById("myCanvas")); 
}
  
</script>

function draw() { ... }draw という関数を定義する部分である。この ... の部分はページの描画のときに呼び出されるので、描画のためのプログラムの文を追加していく。

なお、この書き方では、1 つのウェブページに 1 つの絵(キャンバス)しか作成できない。 その場合、複数のウェブページを作成し、iframe タグを使って 1 つのページに読み込むようにする方法がある。 または、p5.js のインスタンスモードを使う方法もあるが、ここでは説明を割愛する。

p5.js の描画関数

描画のための関数としては、次のようなものがある。 ここでは主なものだけを紹介する。 p5.js のリファレンスにはもっとたくさんの関数が説明されている。

なお、色の指定の仕方はいろいろあるが、上の CSS Color で説明した形式をそのまま文字列として指定することができる。例えば、 stroke("hsl(180 100% 50%)") のように書くことができる。

以下にこれらの関数の一部を使った例を示す。

<script>
function setup() {
  createCanvas(320, 180); // キャンバスのサイズを指定
}

function draw() {
  background("lightgray");
  noFill();
  stroke("red");
  line(25, 25, 125, 75);
  stroke("green");
  rect(25, 25, 100, 50);
  stroke("blue");
  fill("orange");
  ellipse(200, 50, 120, 60);
  noStroke();
  fill("cyan");
  triangle(50, 170, 200, 170, 100, 120);
  fill("magenta");
  triangle(75, 160, 225, 160, 125, 110);
  fill("yellow");
  triangle(100, 150, 250, 150, 150, 100);
}
</script>

このプログラムは以下のような画像を生成する。

p5.js 例2

なお、以降の例では、function setup() { ... } の部分は変わらないので、 function draw() { ... }中身(... の部分)だけを示すことにする。

プログラム例(二重の繰り返し)

以下に、p5.js を使ったグラフィックスのプログラム例をいくつか示す。 まずは繰り返しを使った例である。

  noStroke();
  for (y = 0; y < 180; y += 10) {
    for (x = 0; x < 320; x += 10) {
      fill(`oklch(90% ${100 - x / 3.2}% ${y * 2})`);
      ellipse(x + 5, y + 5, 8, 8);
    }
  } 

p5.js 例3

p5.js の文字列の関数

文字列(テキスト)の描画関数も用意されている。

文字列には絵文字を含めることも可能である。

p5.js の便利な関数

最後にこれらの関数を使ったいくつかの p5.js のプログラム例を示す。

プログラム例(三角関数)

  const n = 100;
  const fruits =["🍏", "🍑", "🍇", "🍐"];
  textSize(10);
  for (let count = 0; count < n; count++) {
    let r = 20 + count / n * 70, t = count * 19;
    let x = 160 + 1.6 * r * cos(t / 180 * 3.14), 
        y = 100 + r * sin(t / 180 * 3.14);
    text(fruits[count % fruits.length], x, y);
  }

p5.js 例4

プログラム例(乱数)

  textSize(18);
  const chars = ["", "", "", "", "", "", ""];
  for (let x = 0; x < 320; x += 20) {
    for (let y = 0; y < 180; y += 20) {
      let h = random(360);
      fill(`oklch(50% 100% ${h})`);
      let c = random(chars);
      text(c, x, y + 20);
    }
  }
  noLoop(); // draw() は一度だけ実行する

p5.js 例5

プログラム例(乱数 2)

  noStroke();
  for (let i = 0; i < 300; i++) {
    const x = random(5, 315);
    const y = random(5, 175);
    const h = random(0, 360);
    let a = 1;
    if ((x - 160) * (x - 160) * 0.316 + (y - 90) * (y -90) > 4900) {
      a = 0.1;
    }
    fill(`oklch(80% 70% ${h} / ${a})`)
    ellipse(x, y, 10, 10);
  }
  noLoop();

p5.js 例6

マウスクリックに対するインタラクションやアニメーションなども記述できるので、ぜひ p5.js のリファレンスを調べてほしい。

p5.js のインスタンスモード

ここまで解説した function setup() { ... }function draw() { ... } を定義する方法は、p5.js のグローバルモードと呼ばれるものである。この方法は簡単だが、1 つのウェブページに 1 つの絵(キャンバス)しか作成できない。

複数のキャンバスを作成したい場合は、iframe を使う方法と p5.js のインスタンスモードを使う方法がある。ここではインスタンスモードの書き方を紹介する。 最初の例をインスタンスモードで書くと次のようになる。

const sketch = (p) => {
  p.setup = function() {
    p.createCanvas(320, 180);
  };
  p.draw = function() {
    p.background("lightgray");
    p.stroke("green");
    p.fill("orange");
    p.ellipse(200, 100, 180, 120);
  };
};

new p5(sketch, "div1");

つまり、 function setup() { }function draw() { } を、 const sketch = (p) => { p.setup = () => { }; p.draw = () => { }; }; new p5(sketch, "div1"); という形に書き換え、さらに の内部の createCanvasbackground などの p5.js の関数を p.createCanvasp.background のように p.~ の形に書き換える。 最後の new p5(sketch, "div1"); の部分は、HTML のなかに <div id="div1"></div> のように div タグを作成してあったとして、その div タグの最後にキャンバスを追加する。第 2 引数を省略して new p5(sketch); とすると、ページの末尾にキャンバスが追加される。


DOM の操作

JavaScript は DOM (Document Object Model) と呼ばれる仕組みを使って、 HTML 文書の要素を操作することができる。 DOM は、HTML 文書を JavaScript のオブジェクトとして表現したものである。

以下では DOM が提供する膨大なメソッド・プロパティーのうち、 使用頻度の高いものをごく一部選んで紹介する。

この節で紹介するプログラム例はDOM 操作プログラム例のページで実際に動かすことができる。

document オブジェクト

document オブジェクトは、HTML に読み込まれる JavaScript のコードに最初から用意される、現在の HTML 文書を表すオブジェクトである。このオブジェクトを通じて JavaScript から DOM を操作する。

document オブジェクトが提供するメソッドとして次のようなものがある。

Element のメソッドとプロパティー

上の getElementByIdquerySelectorAll で取得した要素は、Element と呼ばれるクラスに属するオブジェクトである。 Element オブジェクトは次のようなメソッドやプロパティーを持つ。 詳細は MDN の Element を読むと調べることができる。

innerHTMLstyle は Element のプロパティーである。

プログラム例 (insertAdjacentHTML)

次のプログラムはinsertAdjacentHTML メソッドの使用例である。

<p id="gradation"></p><script>
const gradation = document.getElementById("gradation");
for (let i = 0; i < 360; i += 15) {
    const color = `oklch(80% 100% ${i})`;
    gradation.insertAdjacentHTML("beforeend",
        `<span style="color: ${color};">█</span>`);
}
</script>

出力結果は次のようになる。

プログラム例(多次元配列と for ~ of 文)

多次元配列と for ~ of 文を使っている例を示す。

<p id="squares"></p><script>
const board = [["blue", "white", "red"],
               ["black", "yellow", "red"],
               ["orange", "white", "green"]];

const squares = document.getElementById("squares");
for (let row of board) {
    for (let color of row) {
        squares.insertAdjacentHTML("beforeend",
            `<span style="color: ${color};">█</span>`);
    }
    squares.insertAdjacentHTML("beforeend", "<br>");
}
</script>

出力結果は次のようになる。




イベントリスナー

addEventListener は、要素にイベントの処理を追加するためのメソッドである。value は、入力用の要素の値を取得するためのプロパティーである。

ボタンの場合は、次のように"click"に対してイベント処理を記述する。

<button id="my-button">Click!</button><script>
const myButton = document.getElementById("my-button");
myButton.addEventListener("click", ev => {
    // ここにクリックされたときの処理を書く
    // ⋮
});
</script>

ev => { ... }アロー関数式という関数の書き方であるが、詳しい説明は省く。 とにかく ... の部分に、イベントが発生したときに実行したい処理を書けば良い。

また input 要素の値を取得して処理する場合は、次のように"change"イベントに対して処理を書く。

<input id="my-input" type="text" value="nanika"><script>
  const myInput = document.getElementById("my-input");
  myInput.addEventListener("change", ev => {
    const text = myInput.value; // 入力された文字列
    // ここに text を使った処理を書く
    // ⋮
  });
</script>

プログラム例 (button)

button の click イベントを使ったプログラムの例を示す。

<button id="clickbtn">Click!</button><script>
const clickbtn = document.getElementById("clickbtn");
clickbtn.addEventListener("click", ev => {
    clickbtn.insertAdjacentHTML("beforeend", "!");
    if (clickbtn.innerHTML.length > 15) {
        clickbtn.innerHTML = "Click!";
    }
});
</script>

このボタンを何度かクリックしたときの見た目は次のようになる。

Click!!!!!

クリックするたびに ! が増えていくが、 さらに何度かクリックすると Click! に戻る。

プログラム例 (input type=”text”)

    ⋮
入力: <input type="text" id="cname" size="10"><br>
入力 (<span id="cdisplay"></span>) は 
     <span id="validity">無効な</span>色です。</p>
<script>
const cname = document.getElementById("cname");
const cdisplay = document.getElementById("cdisplay");
const validity = document.getElementById("validity");
cname.addEventListener("change", ev => {
    const color = cname.value;
    cdisplay.innerHTML = color;
    if (color && CSS.supports("color", color)) {
        cdisplay.style.color = color;
        cdisplay.style.backgroundColor = null;
        validity.innerHTML = "有効な";
    } else {
        cdisplay.style.color = "white";
        cdisplay.style.backgroundColor = "red";
        validity.innerHTML = "無効な";
    }
});
</script>

このプログラムは、input 要素に入力された文字列が有効な色であるかどうかをチェックし、結果を表示する。 このプログラムで使われているメソッド CSS.supports("property", value) は、 現在のブラウザーで CSS のプロパティー property が 値 value をサポートしているかどうかを調べるメソッドである。

green のような有効な色名を入力すると、次のように表示される。

入力: green
入力 (“green”) は 有効な色です。

一方で nocolor のような無効な色名を入力すると、次のように表示される。

入力: nocolor
入力 (“nocolor”) は 無効な色です。

プログラム例 (input type=”number”)

<input type="number" id="finput" value="0" min="0" max="22">
    の階乗は <span id="fresult">1</span> です
    ⋮
<script>
const finput = document.getElementById("finput");
const fresult = document.getElementById("fresult");
finput.addEventListener("change", ev => {
    const input = finput.value;
    let result = 1;
    for (let i = 2; i <= input; i++) {
        result *= i;
    }
    fresult.innerHTML = result;
});
</script>

このプログラムは、入力された数の階乗 (factorial) を計算して表示する。

input 要素に例えば 10 を入力すると次のように表示される。 (type=”number” の input 要素にはスピナーと呼ばれる増減のための小さなボタンが付くときもある。)

10 の階乗は 3628800 です。

プログラム例 (input type=”color”)

    ⋮ 
色を選択: <input type="color" id="cpicker" value="#ff0000"><br>
   選択した色は <code id="cstring">#ff0000</code> です。
    ⋮
<script>
const cpicker = document.getElementById("cpicker");
const cstring = document.getElementById("cstring");
cpicker.addEventListener("change", ev => {
    const color = cpicker.value;
    cstring.innerHTML = color;
    cstring.style.color = "white";
    cstring.style.backgroundColor = color;
});
</script>

このプログラムは、input 要素で選択された色を表す文字列を表示する。選択すると、次のような表示になる。

色を選択:
選択した色は #ac27d8 です。
  1. CSS Color Module Level 4 によると 148 種類ある。2014 年に追加された rebeccapurple が最後らしい。