クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【p5.js】Generative Design with p5.js「文字 P_3_1_1_01」のコードを読み解く

p5.jsの続きです。前回は「Generative Design with p5.js」の「P.2. 形」の「02」「03」を見ました。今回は画像編「P.3. 文字」の「P_3_1_1_01」を見ていきます。それでは行きましょう!

【目次】

※参考:前回記事
【p5.js】Generative Design with p5.js「形 P_2_0_02、03」のコードを読み解く - クモのようにコツコツと

※参考:p5.jsを習得するためにやったことまとめ
qiita.com

文字 P_3_1_1_01

書籍「Generative Design with p5.js」より

ソースコード一覧
※参考:Generative Design with p5.js[p5.js版ジェネラティブデザイン] ―ウェブでのクリエイティブ・コーディング

文字 P_3_1_1_01

赤く点滅するバーがだんだん伸びる。早くタイピングすると小さい文字が出て、遅くタイピングすると大きい文字が出る。

※参考:p5.js Web Editor

JSコード全体

// P_3_1_1_01
//
// Generative Gestaltung – Creative Coding im Web
// ISBN: 978-3-87439-902-9, First Edition, Hermann Schmidt, Mainz, 2018
// Benedikt Groß, Hartmut Bohnacker, Julia Laub, Claudius Lazzeroni
// with contributions by Joey Lee and Niels Poldervaart
// Copyright 2018
//
// http://www.generative-gestaltung.de
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * typewriter. time reactive.
 *
 * MOUSE
 * position y           : adjust spacing (line height)
 *
 * KEYS
 * a-z                  : text input (keyboard)
 * backspace/delete     : delete last typed letter
 * ctrl                 : save png
 */
'use strict';

var textTyped = 'Type slow and fast!';
var fontSizes = [textTyped.length];
var minFontSize = 15;
var maxFontSize = 800;
var newFontSize = 0;

var pMillis = 0;
var maxTimeDelta = 5000.0;

var spacing = 2; // line height
var tracking = 0; // between letters
var font;

function setup() {
  createCanvas(800, 600);

  font = 'Arial';

  noCursor();
  noStroke();

  // init fontSizes
  for (var i = 0; i < textTyped.length; i++) {
    fontSizes[i] = minFontSize;
  }
}

function draw() {
  background(255);
  textAlign(LEFT);
  fill(0);

  spacing = map(mouseY, 0, height, 0, 120);
  translate(0, 200 + spacing);

  var x = 0;
  var y = 0;
  var fontSize = 20;

  for (var i = 0; i < textTyped.length; i++) {
    // get fontsize for the actual letter from the array
    fontSize = fontSizes[i];
    textFont(font, fontSize);
    var letter = textTyped.charAt(i);
    var letterWidth = textWidth(letter) + tracking;

    if (x + letterWidth > width) {
      // start new line and add line height
      x = 0;
      y += spacing;
    }

    // draw letter
    text(letter, x, y);
    // update x-coordinate for next letter
    x += letterWidth;
  }

  // blinking cursor after text
  var timeDelta = millis() - pMillis;
  newFontSize = map(timeDelta, 0, maxTimeDelta, minFontSize, maxFontSize);
  newFontSize = min(newFontSize, maxFontSize);

  fill(200, 30, 40);
  if (int(frameCount / 10) % 2 == 0) fill(255);
  rect(x, y, newFontSize / 2, newFontSize / 20);
}

function keyReleased() {
  // export png
  if (keyCode == CONTROL) saveCanvas(gd.timestamp(), 'png');
}

function keyTyped() {
  if (keyCode >= 32) {
    textTyped += key;
    fontSizes.push(newFontSize);
  } else if (keyCode == BACKSPACE || keyCode == DELETE) {
    if (textTyped.length > 0) {
      textTyped = textTyped.substring(0, max(0, textTyped.length - 1));
      fontSizes.pop();
    }
  }
  // reset timer
  pMillis = millis();
}

冒頭コメントの前半はクレジット。 後半の概要の訳。

タイプライター。 時間反応。 マウス 位置y:間隔を調整します(行の高さ) キー a-z:テキスト入力(キーボード) バックスペース/削除:最後に入力した文字を削除します ctrl:pngを保存

全体構成

// グローバル変数
var textTyped = 'Type slow and fast!';
// 他

function setup() {
  // ページ読み込み時の処理
}

function draw() {
  // 一定時間ごとに繰り返し実行
}

function keyReleased() {
  // キーが離れた時の処理
}

function keyTyped() {
  // 文字キーを押した時の処理
}

keyTyped()メソッドは文字以外のキーは無視するようだ。

The keyTyped() function is called once every time a key is pressed, but action keys such as Backspace, Delete, Ctrl, Shift, and Alt are ignored.

keyTyped()関数は、キーが押されるたびに1回呼び出されますが、Backspace、Delete、Ctrl、Shift、Altなどのアクションキーは無視されます。

※参考:reference | p5.js

グローバル変数

今回は冒頭のグローバル変数の設定が多い。改行が入って3グループに分かれている。

最初のグループ

var textTyped = 'Type slow and fast!';
var fontSizes = [textTyped.length];
var minFontSize = 15;
var maxFontSize = 800;
var newFontSize = 0;
  • 変数textTypedは文字列「Type slow and fast!'」
  • 変数fontSizesは配列でtextTypedの文字数(length)が入る
  • 変数minFontSizemaxFontSizenewFontSizeは数値。

textTypedの文字数は19なので配列fontSizesには19が入る。

次のグループ

var pMillis = 0;
var maxTimeDelta = 5000.0;
  • 変数pMillismaxTimeDelta共に数値

最後のグループ

var spacing = 2; // line height
var tracking = 0; // between letters
var font;
  • 変数spacing(線の高さ)、tracking(文字間)は数値
  • 変数fontは宣言のみで値はまだ無い

setup()メソッドの中身

ページ読み込み時の処理

function setup() {
  createCanvas(800, 600);

  font = 'Arial';

  noCursor();
  noStroke();

  // init fontSizes
  for (var i = 0; i < textTyped.length; i++) {
    fontSizes[i] = minFontSize;
  }
}
  • createCanvas()メソッドで幅800px、高さ600pxのキャンバスを作成
  • fontArial
  • noCursor()メソッドでカーソルを非表示
  • noStroke()メソッドで線を無しに
  • for文でtextTypedの数量分ループ
  • 配列fontSizesi番目にminFontSizeを入れる

minFontSizeは「15」なので配列fontSizesには「15」が19個(textTypedの文字数)入る。

draw()メソッドの中身

draw()メソッド全体

一定時間ごとに繰り返し実行

function draw() {
  background(255);
  textAlign(LEFT);
  fill(0);

  spacing = map(mouseY, 0, height, 0, 120);
  translate(0, 200 + spacing);

  var x = 0;
  var y = 0;
  var fontSize = 20;

  for (var i = 0; i < textTyped.length; i++) {
    // get fontsize for the actual letter from the array
    fontSize = fontSizes[i];
    textFont(font, fontSize);
    var letter = textTyped.charAt(i);
    var letterWidth = textWidth(letter) + tracking;

    if (x + letterWidth > width) {
      // start new line and add line height
      x = 0;
      y += spacing;
    }

    // draw letter
    text(letter, x, y);
    // update x-coordinate for next letter
    x += letterWidth;
  }

  // blinking cursor after text
  var timeDelta = millis() - pMillis;
  newFontSize = map(timeDelta, 0, maxTimeDelta, minFontSize, maxFontSize);
  newFontSize = min(newFontSize, maxFontSize);

  fill(200, 30, 40);
  if (int(frameCount / 10) % 2 == 0) fill(255);
  rect(x, y, newFontSize / 2, newFontSize / 20);
}

ここが一番ボリュームが多いのでブロックごとに見ていく。

draw()関数

draw()関数の中身

function draw() {
  background(255);
  textAlign(LEFT);
  fill(0);
  • background()メソッドで背景色をRGB共255(白)に
  • textAlign()メソッドの引数LEFTで文字を左寄せに
  • fill()メソッドの引数0で塗りをRGB共0(黒)に

変数spacingでマッピング

  spacing = map(mouseY, 0, height, 0, 120);
  • spacingの値をmap()メソッドに。引数は左からmouseY, 0, height, 0, 120
  • translate()メソッドの引数

map()はマッピングで前回も出てきた。

※参考:【p5.js】Generative Design with p5.js「形 P_2_0_02、03」のコードを読み解く - クモのようにコツコツと

mouseY(マウスの縦位置)のうち0height(キャンバス高さ)までの範囲を0120までの範囲にマッピングする」という意味。

translate()メソッド実行

  translate(0, 200 + spacing);
  • translate()メソッドを実行。引数は左から0, 200 + spacing

translate()メソッドも前回に出てきた。オブジェクトの移動量の設定をするメソッド。開始点が0で終了点は200に文字間を足した値。

for文

for文(全体)

次にfor文

  var x = 0;
  var y = 0;
  var fontSize = 20;

  for (var i = 0; i < textTyped.length; i++) {
    // get fontsize for the actual letter from the array
    fontSize = fontSizes[i];
    textFont(font, fontSize);
    var letter = textTyped.charAt(i);
    var letterWidth = textWidth(letter) + tracking;

    if (x + letterWidth > width) {
      // start new line and add line height
      x = 0;
      y += spacing;
    }

    // draw letter
    text(letter, x, y);
    // update x-coordinate for next letter
    x += letterWidth;
  }

ここもボリュームが多いので部分ごとに見ていく。

変数設定

まずfor文に使われる変数が3つ設定されている。

  var x = 0;
  var y = 0;
  var fontSize = 20;
  • 変数xyfontSize共に値は数値
for文条件

まずfor文の条件

  for (var i = 0; i < textTyped.length; i++) {
   // 処理
}
  • 変数iの初期値は0
  • textTypedの文字数length分ループする
  • iに1ずつ加算する
配列から文字サイズを取得
    // get fontsize for the actual letter from the array
    fontSize = fontSizes[i];
    textFont(font, fontSize);
    var letter = textTyped.charAt(i);
    var letterWidth = textWidth(letter) + tracking;
  • コメント訳:配列から実際の文字のフォントサイズを取得します
  • fontSizeをに配列fontSizei番目に
  • textFont()メソッドを実行。引数は左からfont, fontSize
  • 変数lettertextTyped.charAt()メソッド(引数はi)の結果に。
  • 変数letterWidthtextWidth()メソッド(引数はletter)と文字間trackingの合計

textFont()メソッドはフォントの設定で、第1引数はfontのフォントであるArial、第2引数はfontSizeで上で定義されている配列fontSizesi番目のサイズが入る。

charAt()はJSの組み込みメソッドで文字を左から1文字ずつ取得する。

※参考:String.prototype.charAt() - JavaScript | MDN

変数letterWidth(文字幅)はletterの文字幅(textWidth())と文字間(tracking)の合計

新しい行を開始
    if (x + letterWidth > width) {
      // start new line and add line height
      x = 0;
      y += spacing;
    }
  • もしxletterWidthの合計がwidthより多ければ
  • コメント訳:新しい行を開始して行の高さを追加する
  • xを0に
  • yspacingを追加する

これでで新しい行(左から0、文字間文下に下がった位置)になる。

テキスト描画
    // draw letter
    text(letter, x, y);
    // update x-coordinate for next letter
    x += letterWidth;
  • コメント訳:手紙を描く
  • text()メソッドを実行。引数は左からletter, x, y
  • コメント訳:次の文字のx座標を更新する
  • xletterWidthを追加

text()メソッドはテキストの描画。引数の意味は下記

text(str, x, y, [x2], [y2])

※参考:reference | p5.js

配列letterの文字をxyの位置に描画する

カーソルの点滅

変数設定
  // blinking cursor after text
  var timeDelta = millis() - pMillis;
  newFontSize = map(timeDelta, 0, maxTimeDelta, minFontSize, maxFontSize);
  newFontSize = min(newFontSize, maxFontSize);
  • コメント訳:テキストの後のカーソルの点滅
  • 変数timeDeltamillis()からpMillisを引いた数字
  • newFontSizemap()メソッドでtimeDelta0maxTimeDeltaminFontSizemaxFontSizeにマッピングした値
  • newFontSizemin()メソッドでnewFontSizemaxFontSizeのうち小さい方の値

Returns the number of milliseconds (thousandths of a second) since starting the sketch (when setup() is called).

(setup()が呼び出されたとき)スケッチを開始してからのミリ秒数(1000分の1秒)を返します。 

※参考:reference | p5.js

min()メソッドは最小値を返すメソッド。newFontSizemaxFontSizeのうち、小さい方を返す。

Determines the smallest value in a sequence of numbers, and then returns that value.

一連の数値の最小値を決定し、その値を返します。

※参考:reference | p5.js

処理設定部分
  fill(200, 30, 40);
  if (int(frameCount / 10) % 2 == 0) fill(255);
  rect(x, y, newFontSize / 2, newFontSize / 20);
}
  • fill()メソッド実行、引数は左から200, 30, 40
  • もしframeCountを10で割った数の整数(int)を2で割った数字の余りが0なら塗りfill()メソッドの引数を255
  • rect()メソッドを実行。引数は左からx, y, newFontSizeを2で割った値, newFontSize20で割った値

fill()メソッドは塗りで画面上の赤いバーの色はここで設定されている。

if文の条件にあるframeCountはフレーム数。

※参考:reference | p5.js

intは整数。2で割り切れる数値=偶数の時に塗りが白になって、バーが点滅して見える。

rect()メソッドは色の記事でも出てきた長方形を作るメソッド。バーの形はここで設定されている。第1〜2引数は位置で第3〜4引数がサイズ。サイズはnewFontSizeを2や20で割った数。

※参考:【p5.js】Generative Design with p5.js「色 P_1_0_02」のコードを読み解く - クモのようにコツコツと

keyReleased()メソッドの中身

キーが離れた時の処理

function keyReleased() {
  // export png
  if (keyCode == CONTROL) saveCanvas(gd.timestamp(), 'png');
}
  • もしCONTROLキーを押したらsaveCanvas()メソッドでpng画像を保存

ここはいつもある画像保存設定。

keyTyped()メソッドの中身

文字キーを押した時の処理

function keyTyped() {
  if (keyCode >= 32) {
    textTyped += key;
    fontSizes.push(newFontSize);
  } else if (keyCode == BACKSPACE || keyCode == DELETE) {
    if (textTyped.length > 0) {
      textTyped = textTyped.substring(0, max(0, textTyped.length - 1));
      fontSizes.pop();
    }
  }
  // reset timer
  pMillis = millis();
}
  • もしキーコードが32よりも大きいキーかったら
    textTypedkeyを追加する
    fontSizespush()メソッド実行。引数はnewFontSize
  • さもなくば、BACKSPACEまたはDELETEキーを押したら
    もしtextTypedの数lengthが0より多ければ
    textTypedtextTypedsubstring()メソッドを実行。 引数は左から0, max()メソッドで、max()の引数は左から0, textTypedの数lengthマイナス1
    fontSizespop()メソッドを実行
  • リセットタイマー処理、pMillismillis()メソッドの結果に

キーコード32は「Apps」のようだが、それより前のDelateやEnterなどの制御キー以外を適用させる条件のようだ。

※参考:キーコード一覧

push()メソッドはその下のpop()メソッドとセットで使われる。push()は現在の描画スタイル設定と変換を保存し、pop()は設定を復元する。

※参考:【p5.js】Generative Design with p5.js「形 P_2_0_02、03」のコードを読み解く - クモのようにコツコツと

substring()メソッドはJSの組み込み関数で文字を取り出す。

※参考:String.prototype.substring() - JavaScript | MDN

max()メソッドは数値の最大値を返す

Determines the largest value in a sequence of numbers, and then returns that value.

一連の数値の最大値を決定し、その値を返します。 

※参考:reference | p5.js

millis()メソッドはスケッチを開始してかららの時間(ミリ秒)を返す

Returns the number of milliseconds (thousandths of a second) since starting the sketch (when setup() is called).

(setup()が呼び出されたとき)スケッチを開始してからのミリ秒数(1000分の1秒)を返します。

※参考:reference | p5.js

この関数の処理で、キーで打った文字を画面に反映させたり、キーを打つ時間をバーの大きさに反映させたりしているわけだ。

最後に

f:id:idr_zz:20200528190434j:plain

今回は結構ボリュームがあるように感じましたが、細かく見てみるとこれまで出てきたメソッドも多く、だんだんp5.jsのコードの見慣れて来ている感じがします!引き続きこの調子でコツコツと進めて行きたく思います。それではまた!


※参考:p5.jsを習得するためにやったことまとめ
qiita.com