スクリーンショット_2019-08-16_7

#xhack勉強会 レポ 「 Generative Art With JavaScript」(2019年6月19日)

初のnote投稿です!

全くの未経験からプログラミング学習を初めて約2ヶ月のいわゆるゴリゴリの「 初学者 」です。

先日参加した #xhack のJavaScript勉強会ですが、せっかくなので参加して終わりではなく、自分自身のインプットとして定着させるため、内容をまとめてみました。

(こちら、本勉強会Connpassページのリンクです↓↓↓↓)


■今回の勉強会のゴール

こんなの↓↓↓ を、JavaScriptだけで書きます

無数の丸が画面内を浮遊している... 

まさにアート!
JavaScriptだけでこんなのが作れるようになるなんて、始まる前からわくわくです!

■本題に入る前に

これまで何度かXhack勉強会に参加させてもらっていますが、
そこで得られるものは本題の内容(今回だったらjsでアートをつくろう!に必要なコードの知識)だけではないです。

私の思うxhack勉強会に参加するメリットは、

◯講義中に代表の松田さんはじめ講師の方から伝授されるいろんなお得情報(おすすめのサイトや本など)

プロジェクターで映し出された講師のPC画面内でプロのテクニックが垣間見れる
(なんだ今のコマンド!?あの謎の便利アイコンはなに!?帰ってググろ!となる)

今の自分の実力を測ることができる
(家でゆっくり見れば理解できる内容でも、勉強会ではけっこう早いペースで進むので自分がどの程度理解できているのかがよく分かる。私は中盤くらいからペースについていけずよく置いていかれてます笑)

強強の方のレベルの高い質問に触れられる
 (現役エンジニアの方も多く参加されており、実務レベルってこういうことか〜ってのを実感できます。)

リアルな場での人脈につながる
 (Twitterでよく見る方と出会えたりしますね^^)

などなど、特に未経験の私にとっては家にこもって勉強しているだけでは絶対に得られないメリットがたくさんあるわけですね。

(つまり、おすすめです!ってこと)

そんな盛りだくさんな勉強会ですが、今回紹介してもらった便利ツールを3つほど紹介します。


①ブラウザ上でjsの動きを確認!「w3schools」

エディタを使用しなくても、ブラウザ上でコードの動作確認ができます
エディタを編集 → ブラウザ更新
というステップを踏まなくてよいので、ちょっとした動作確認をするにはとても便利です!(jsだけでなく、HTMLやCSSやPHPなども使えます)


②chrome デベロッパーツールの「Sources」パネル

ElementsパネルやConsoleパネルは割と使っていましたが、Sourcesパネルはこれまで使ったことがありませんでした。

ブレークポイント(指定した行で動作を一時停止する)を設定することで、for文内の変数がどのように変わっていくかなど、裏で動いている処理を細かく観察できます
開始15分でこのツールを教えて頂き、これ知れただけでも来た価値あるな!もう帰っていいな!と思えるほどでした。


さらに詳しく知りたい方はこういったサイトを参考にしてみてください
↓↓


③MDN web docs

こちらは皆さんにとってはお馴染みでしょうか。私は以前参加したxhack勉強会で知りました。リファレンス(参照)全般で活躍できる場面がたくさんあります。
jsだけでなく、web開発に関わる様々な情報が詰まってます。

またチュートリアルも充実しており、ここだけでもある程度体系的な学習ができるようです。


■「CANVAS API」で図形を書く

それでは本題です!
まずは最も基本的な形のコードです。

<!DOCTYPE html>
   <html>
   <body>
   
   <canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
   </canvas>
   
   <script>
   var c = document.getElementById("myCanvas");
   var ctx = c.getContext("2d");
   ctx.fillStyle = "#FF0000";
   ctx.fillRect(20, 20, 150, 100);
   </script>
   
   </body>
   </html>

<canvas>タグで画面上にキャンバスを描き、getContext("2d")でCANVAS APIを呼び出し、キャンバス上で図形を描きます。
getContextメソッドに渡せる引数は2dのみのようなので、getContext("2d")までセットで覚えちゃっていいみたいです。

上記コードを先ほど紹介した「w3schools」で実行するとこんな感じです!
(左側へコード入力、緑の「Run >>」ボタンを押すと右側へ結果が表示されます。)


これがCANVAS APIを使った図形描写の最も基本的な形です。
ここから、水玉アートを目指してどんどんいきます!


■色をランダムに取得したい


水玉アートを作るためには、色をランダムに変える必要があります。
色の指定方法にはいくつかありますが、今回はrgbで指定する方法です。

color: rgb(255,136,34);

こんな感じのやつですね。
rgbの数字をランダムに変えてやれば、色をランダムに変えられそうです。

ということで、まずはコードを見てみましょう!

// 0-255のランダムな数値を取得する
function getRandomNumber()
{
	let x = Math.random() * 255;
 return Math.round(x);
}

// ランダムな色を返す関数
function getRandomColor()
{
 let r = getRandomNumber();
 let g = getRandomNumber();
 let b = getRandomNumber();
 return `rgb(${r},${g},${b})`
}

getRandomNumber (// 0-255のランダムな数値を取得する)
getRandomColor (// ランダムな色を返す)
という2つの関数を宣言しています。

まず、getRandomNumber関数のなかで Math.random() という関数が登場します。

Math.random()は、0~1の数値をランダムに返す関数です。

Consoleパネルで実行してみると、たしかに毎回違う数字が返ってくることが確認できます。(←このデバック方法もこの時教わりました)

この0〜1の数値に255を掛け算し、Math.round()四捨五入してやると、0~255の数値がランダムに返ってくる というわけですね!

そして、 getRandomNumber  で取得したランダム数値を、getRandomColor  にてrgbそれぞれに変数として代入することで、ランダムな色を取得しています。

■for文を使ってたくさんの四角を描こう!


さらに以下のjsコードを追加します。

// x, y, width, height を持つオブジェクト
// draw関数を呼び出すと、四角形を描画する
var data = {
 x: 0,
 y: 0,
 width: 0,
 height: 0,
 setData: function(_x, _y, _width, _height) {
   this.x = _x;
   this.y = _y;
   this.width = _width;
   this.height = _height;
 },
 draw: function() {
   ctx.fillStyle = getRandomColor();
   ctx.fillRect(this.x, this.y, this.width, this.height);
 }
};

for (var i = 0; i < 50; i++) {
 let obj1 = new Object(data);
 obj1.setData(10*i, 10*i, 50, 50);
 obj1.draw();
}


そうすると、こうなります。↓↓↓
(色はランダムなのでリロードするごとに変わります。)

ここでやっていることは、

変数 data内 に x, y, width, height だけでなく、中身が関数である  setData, draw  もオブジェクトとして連想配列形式で格納し、それらをfor文の中で実行する ということ。

関数もオブジェクトにできる というのがポイント。
(あとで実際に動作をみながら確認していきます!)

それでは、覚えたばかりのデバロッパーツールのSourcesパネルで動きを細かく見ていきましょう!

今度はエディタでここまでのコードを書き、ブラウザで読み込みます。

そして、↓↓↓こんなのが表示されたのを確認したら、コードの最後、draw関数を呼び出している行(私の場合は 105行目 )をブレークポイントに設定し、リロードしてみると...


こんな感じになります。

上から順にコードを読みこんでいき、for文で i=0 が一度実行され、ブレークポイントである105行目で一旦停止している状態です。

次に再生ボタン(みたいなやつ)を押すと、次のブレークポイントまで動作します。押してみましょう!

色はランダムですが、このように四角が1個出てきます。

止まっていた 105 行目から処理がスタートし、
x, y 0(=キャンバスの始点)の位置を起点に obj.draw() が実行されて四角が1つ生成され、またfor文の変数 i がインクリメントとなり、obj1 x y10となっています。

そして2つ目の四角を生成する obj.draw()の位置で一時停止しています。

ちなみに、この時の i の値をConsoleパネルで 確認すると

ちゃんと 1 になってますね!
だんだんデベロッパーツールの扱いにも慣れてきました!

それではもう一度再生ボタンを押します。

もう説明は不要ですね!

同じことを10回繰り返すと↓↓こんな感じ。


実際にはこれを目にも止まらぬスピードで連続処理しているので、同時にたくさんの四角が出現しているように見えますが、コンピューターでの処理はこんな流れで行われているということですね。

■forを入れ子にする(forの中にfor)

今度はこちらのjsコードを使用。

       var c = document.getElementById("myCanvas");
       var ctx = c.getContext("2d");
   
       function getRandomNumber()
       {
         return Math.round(Math.random() * 255);
       }
   
       function getRandomColor()
       {
         let r = getRandomNumber();
         let g = getRandomNumber();
         let b = getRandomNumber();
         return `rgb(${r},${g},${b})`
       }
   
       var data = {
         name: 'hoge',
         x: 0,
         y: 0,
         width: 0,
         height: 0,
         setData: function(_x, _y, _width, _height, _color) {
           this.x = _x;
           this.y = _y;
           this.width = _width;
           this.height = _height;
         },
         draw: function() {
           ctx.fillStyle = getRandomColor();
           ctx.fillRect(this.x, this.y, this.width, this.height);
         }
       };
   
       function drawRect(_num){
         for (let i = 0; i < _num; i++) {
           for (let j = 0; j < _num; j++) {
             let obj1 = new Object(data);
             obj1.setData(50*i, 50*j, 50, 50);
             obj1.draw();
           }
         }
       }
   
       drawRect(10);

function drawRect(_num) 以下が新しくなってます。

for文の中にfor文を入れ込み、それを一つの関数としてまとめています。

ブラウザで表示するとこんな感じです。

アートっぽくなってきました。笑

こいつの処理の中身をSourcesパネルで1ずつ見てみます!

先ほどと同様に obj.draw(); の行をブレークポイントとし、処理一回ごとの画面キャプチャをGIF画像にしてます。(GIF画像初めてつくりました^^)

まず j だけがインクリメントし、最大値までいったら i インクリメントして j からまたインクリメントしていく というのが分かりますね。

この処理を10*10=100回、瞬間的に行っているわけですね。


それでは、

       drawRect(10);

こいつを setInterval  を使って連続的に呼び出します!

setInterval は

setInterval(引数1,引数2);
// 引数1 : 実行したいオブジェクト
// 引数2 : 指定ミリ秒ごとに引数1を実行

こうやって使うんでしたね!


ということは

setInterval(drawRect(10), 100);

これで 100ミリ秒ごと に drawRect(10) 関数を呼び出せる。
そうすれば、毎回ランダムに色が変わって目がチカチカするはず!
(そう、ポリゴンショックのように!)





・・・ならない。

1回実行して終わりだ。なぜだ。





答えは、

 drawRect(10) は オブジェクト ではないので setInterval で 指定ミリ秒ごとに実行してくれないから 」だそうです。



え?って感じですよね笑



結論からいうと、drawRect のあとの()を外して
drawRect として第一引数に渡せばよいらしい。

setInterval(drawRect, 100);

こんな感じ。


drawRect()   のように  ()  をつけると、関数を実行した結果を返す
一方、() をつけずに drawRect とすると、オブジェクトとなり、
他の関数へ引数として渡せるようになります。


松田さん曰く、ここが js初級レベルにおいてめちゃめちゃ重要なポイントらしいです。
「この違いを理解して使い分けることができれば、もう js 初学者卒業です!」とも仰っていました。


 ということは、私も今日で js 初学者卒業ってことですね!(?)


さて、()つけるつけないの違いが分かったところで解決かというと、今回はdrawRectに引数を渡さないといけないので、これでもだめです。

setInterval(drawRect, 100);

このままだと引数渡せません。


そんなときの書き方が数パターンあります!どーん!

パターン① 無名関数として引数を渡し、その中で呼び出す

setInterval( function(){
 drawRect(10)
}, 100);


パターン② ①の変形(?) アロー関数で渡す

setInterval( () =>{
 drawRect(10)
}, 100);


パターン③ 文字列で渡す

setInterval( "drawRect(10)", 100);

これで動く理由が私には理解できません笑


パターン④ 第3引数として渡す

setInterval( drawRect, 100 ,10);

一番シンプル!


今挙げたパターンならどれでも動くみたいです。


■水玉アートを描く!(ようやく!!)


さていよいよ今回の主役登場です。

まずはhtml含めた全コードを!どーーーーん!

<!DOCTYPE html>
<html lang="ja">
 <head>
   <meta charset="utf-8" />
   <title>Canvas tutorial template</title>
   <script type="text/javascript">
     const NUM = 500;
     const WIDTH = 800;
     const HEIGHT = 800;
     const PI_2 = Math.PI * 2;
     var speedX = new Array(NUM);
     var speedY = new Array(NUM);
     var locX = new Array(NUM);
     var locY = new Array(NUM);
     var radius = new Array(NUM);
     var r =  new Array(NUM);
     var g =  new Array(NUM);
     var b =  new Array(NUM);
     var ctx;

     function init(){
       var canvas = document.getElementById('tutorial');
       if (canvas.getContext){
         ctx = canvas.getContext('2d');
         for(var i = 0; i < NUM; i++){
           speedX[i] = Math.random() * 8.0 - 4.0;
           speedY[i] = Math.random() * 8.0 - 4.0;
           locX[i] = WIDTH / 2;
           locY[i] = HEIGHT / 2;
           radius[i] = Math.random() * 45.0 + 1.0;
           r[i] = Math.floor(Math.random() * 96);
           g[i] = Math.floor(Math.random() * 96);
           b[i] = Math.floor(Math.random() * 96);
         }
         setInterval(draw, 33);
       }
     }

     function draw(){
       ctx.globalCompositeOperation = "source-over";
       ctx.fillStyle = "rgba(8,8,12,.2)";
       ctx.fillRect(0, 0, WIDTH, HEIGHT);
       ctx.globalCompositeOperation = "lighter";

       for(var i = 0; i < NUM; i++){
         //位置を更新
         locX[i] += speedX[i];
         locY[i] += speedY[i];

         if(locX[i] < 0 || locX[i] > WIDTH){
           speedX[i] *= -1.0;
         }

         if(locY[i] < 0 || locY[i] > HEIGHT){
           speedY[i] *= -1.0;
         }

         //更新した座標で円を描く
         ctx.beginPath();
         ctx.fillStyle = 'rgb(' + r[i] + ',' + g[i] + ',' + b[i] + ')';
         ctx.arc(locX[i], locY[i], radius[i], 0, PI_2, true);
         ctx.fill();
       }
     }
   </script>
   <style type="text/css">
     canvas { background-color:#000; border: 1px solid #999; }
   </style>
 </head>
 <body onload="init();">
   <canvas id="tutorial" width="800" height="800"></canvas>
 </body>
</html>

思ったより長くない!


今日はここまでに、

・CANVAS API の使い方
・デベロッパーツールを使ってのデバック方法
・Math.random でランダムに色を生成する方法
・for文を使って大量に図形を生成する方法
・関数とオブジェクトの話

などを勉強してきました。


実はこの水玉アートのコード、講義の一番最初にも見せてもらっていたのですが、そのときは ???? だったのが、
一通り教わった後に見るとなんとなく分かるような気がします。


もちろん、中には初見のコードもあります

・丸の図形を描くコード(ここまでやってないですからそりゃそうですね)
・Math.floor (なんか見たことある気がするぞ)
・globalCompositeOperation (????????)

でも、ぐぐれば分かるかなってレベルです。(成長!)


実際の勉強会でも、この水玉アートのコードの説明はラスト15分くらいで、それまでの1時間45分は

・CANVAS API の使い方
・デベロッパーツールを使ってのデバック方法
・Math.random でランダムに色を生成する方法
・for文を使って大量に図形を生成する方法
・関数とオブジェクトの話

に費やしていたわけですね。


この勉強会のゴールは、

水玉アートのコードを書こう!

ではなく、

水玉アートのコードが書ける基礎力を身につけよう!

だったわけですね。(素晴らしい。。。)


■終わりに

今回初めて勉強会のレポを書きました。

noteのアカウント登録から、ブログってどうやってみんな書いてるんだ?と何人かのブログを改めて見て雰囲気掴んだりと、本当に0からのスタートだったのでめちゃくちゃ時間かかってしまい、本当に大変だなと痛感しました。
(毎日ブログ書いてる人まじで尊敬!)


しかし、間違ったことを書きたくはなかったので逐一ぐぐりながら用語の意味や定義を確認したことで、相当理解が深まったことは確かです。
(間違った情報ありましたらご連絡ください。関数とかオブジェクトの話のとこなんかはけっこう怪しいです。。)

今後も勉強会に参加したらnoteへの投稿は続けていきたいと思います。

最後まで見ていただきありがとうございました。


この記事が気に入ったらサポートをしてみませんか?