[69-6] Canvasにマウスの動きに合わせて描画しよう (クリックやマウスムーブとの組み合わせと Math.random)

最終更新日:2019年02月06日  (初回投稿日:2014年07月25日)

Canvas に関するここまでの記事は、単にカンバス上に画像を描画するだけでした。
これだと<img>要素で画像を貼っても同じかも。
でも Canvasは本来はもっと面白いことができるはず。そこで、マウスの動作で描画するサンプルを作ってみました。
ですので今回は、<canvas>の基礎からちょっとはずれます。

今回のサンプルはこちら。
クリックで別ウィンドウで開きます。

私は Javascript の基礎がまったく無いので、試行錯誤してこの程度ですが、エンジニアなら Canvas で もっといろんなことができるんでしょうね。

本日のINDEX
  1. ボタンクリックでランダムな位置に描画する
    1. キャンバス上の x, y 座標をランダムに指定する
    2. Math.random() について
    3. 複数の色指定を適用する
  2. クリックした位置に描画する
    1. addEventListener() でclickイベントを登録する
    2. getBoundingClientRect() でキャンバスの座標をゲットして、クリック位置を取得
    3. 複数の色指定をランダムに適用する
    4. クリック位置に描画させる
  3. マウスムーブで描画する
    1. addEventListener() でmousemoveイベントを登録する
    2. 複数の半径をランダムに適用する

ボタンクリックでランダムな位置に描画する

サンプル1は、カラー名のボタンをクリックすると、キャンバス上のランダムな位置に、グラデーションの円形が描画されます。

これは画像です↓ クリックで別ウィンドウで開きます。

HTMLはこちら。<canvas>の背景は、CSSで黒にしています。
ボタンに onclick で関数を仕込んでいます。

<p>クリックしてください→
  <input type="button" value="Orange" onclick="draw1(1)">
  <input type="button" value="Green" onclick="draw1(2)">
  <input type="button" value="Yellow" onclick="draw1(3)">
</p>
<canvas id="canvas1" class="bk" width="678" height="150">
  <p>お使いのブラウザはcanvas要素に対応していないので描画できませんでした</p>
</canvas>
<input type="button" value="クリア" onclick="clear1()">

ボタンを使うので、 onload イベント(ページがロードされたタイミングで適用する)ではなく、ボタンの onclick イベント(2〜4行目)を使っているのが、前回までと違うところです。

キャンバス上の x, y 座標をランダムに指定する

x, y座標をランダムに設定するために「Math.random()」というメソッドを使っています。

/*描画のためのx,yをランダムにつくる*/
    var x = Math.round(Math.random()*678);
    var y = Math.round(Math.random()*150);
/*〜中略〜*/
/*正円を描画*/
    ctx.beginPath();
    ctx.fillStyle = gra;
    ctx.arc(x, y, 100, 0, Math.PI*2, false);
    ctx.fill();

2行目で ランダムなx座標を「変数x」に、3行目で ランダムなy座標を「変数y」に代入しています。
その変数xとyを8行目の、正円の中心点の x, y座標に利用しているため、ランダムな場所に円が描かれる仕組みです。

「Math.random()」に678や150という数値をかけていますが、
これはこのキャンバスの横幅(678px)と高さ(150px)です。
この式を「Math.round()」の中に入れたりしていますが、これは「Math.random()」の特徴のせい。
この件は次項へ↓。

Math.random() について

「Math.random()」は、Mathオブジェクトの「メソッド」の1つで、
0から1未満の範囲で、ランダムな数値(乱数)をゲットします。
変数に数値型として「Math.random()」と書いた時点で、乱数が変数に代入されるんですって。(例:変数a なら、var a = Math.random(); で、変数aに乱数が代入されます。)

ここで発生する乱数は「1未満」なのがミソ。要するに小数です。 「浮動小数点数」と言うんだそうです。
とにかく、0.000...から 0.999...までの範囲で乱数が発生するわけです。
なので、乱数を「1以上の数」または「整数」として使いたい場合は、ひと工夫必要です。

●1から10の乱数にしたいとき、10をかけて1を足す
発生した乱数は、0.000...から0.999...の数値なので、10をかけます
そうすると、0.000...から9.999...という数値になります。
まだ10に足りないので、1を足してみます。これで 1.000...から10.999...になりました。

●0から10の乱数にしたいとき、10+1をかける。または四捨五入しちゃえ
座標だと、x,y が、0,0 ってこともあるわけで、1からじゃまずいです。
0から10の乱数が欲しいときは、10+1をかければいいわけです。
発生した乱数が、0.000...から0.999...なので、11をかければ、0.000...から10.999...になるわけで。

または、小数点以下が不用なら、10をかけて四捨五入してもOK。サンプル1はこの方法です。

●整数にしたいとき、「Math.floor()」で切り捨てるか、「Math.round()」で四捨五入する
小数点以下がジャマなときは、他の「Math.メソッド」を併用します。
「Math.floor()」は小数点以下切り捨て。Math.floor(n) で n の小数点以下を切り捨てます。
「Math.round()」は四捨五入。Math.round(n) で n を四捨五入します。

ちなみに、
「Math.ceil()」は小数点以下切り上げ、
「Math.abs()」は絶対値を返します。
 *絶対値とは0からどんだけ離れてるかの値。-10の絶対値は10。Math.abs()はマイナスを取りたい時に便利。

●幅678、高さ150pxのキャンバス内でのランダムな座標
というわけで、キャンバス内のx, y座標を、ランダムな整数にしたい場合は、
発生した乱数に、「キャンバスの横幅」または「高さ」をかけて、Math.round() で四捨五入します。

    var x = Math.round(Math.random()*678);
    var y = Math.round(Math.random()*150);

これで、x座標は0から678まで、y座標は0から150までの乱数がゲットできました。

複数の色指定を適用する

このサンプルは、ボタンの onclickイベントで、draw1(g)という関数を呼び出し、
draw1(1)でオレンジ色、draw1(2)でグリーン、draw1(3)でイエローの「色」を適用します。
引数gが、1,2,3と替わる仕組みをソースで見てみましょう。サンプル1のJavaScript全文です。 (引数gは任意に。ここではグラデーションなのでgにしました)

function draw1(g) {
    var cnvs = document.getElementById('canvas1');
    var ctx = cnvs.getContext('2d');
/*描画のためのx,yをランダムにつくる*/
    var x = Math.round(Math.random()*678);
    var y = Math.round(Math.random()*150);
/*グラデーションを3種類つくる*/
    var grad1  = ctx.createRadialGradient(x,y,0,x,y,100);
    grad1.addColorStop(0,'rgba(255,147,38,1)');
    grad1.addColorStop(1,'rgba(255,147,38,0)');
    var grad2  = ctx.createRadialGradient(x,y,0,x,y,100);
    grad2.addColorStop(0,'rgba(92,255,38,1)');
    grad2.addColorStop(1,'rgba(92,255,38,0)');
    var grad3  = ctx.createRadialGradient(x,y,0,x,y,100);
    grad3.addColorStop(0,'rgba(255,255,115,1)');
    grad3.addColorStop(1,'rgba(255,255,115,0)');	
/*引数「g」に各グラデを指定*/
    var gra;
    if (g == 1) gra = grad1;
    if (g == 2) gra = grad2;
    if (g == 3) gra = grad3;
/*正円を描画*/
    ctx.beginPath();
    ctx.fillStyle = gra;
    ctx.arc(x, y, 100, 0, Math.PI*2, false);
    ctx.fill();
}

先にグラデーションを3つ作っておきます。(8行目から16行目まで)
変数grad1で、オレンジ色の(8〜10行目)、
変数grad2にはグリーンの(11〜13行目)、
変数grad3にはイエローの(14〜16行目)グラデーションを指定しています。
それぞれ、乱数で取得した 変数x、yをグラデーションの中心点に利用しています。
これでクリックしたところを中心点にしたグラデーションができるわけです。

次に、変数graを新たに作り、これを利用して、
この関数「draw1(g)」の引数gが、
もしも g==1(gが1と同一)だったら、変数graは grad1(オレンジのグラデ)
もしも g==2 だったら、変数graは grad2(グリーンのグラデ)
もしも g==3 だったら、変数graは grad3(イエローのグラデ)
と設定します。(18〜21行目)

で、最後の色指定のところで(24行目)、円のfillStyleに 変数gra を指定します。
この後、乱数で取得した 変数x、yを中心点にした正円を描く指定をすれば完了です。

クリックした位置に描画する

サンプル2は、キャンバス上をクリックして、その地点に描画されるようにします。
ということは、クリックした座標をゲットする必要があります。

これは画像です↓ クリックで別ウィンドウで開きます。

addEventListener() でclickイベントを登録する

●イベントとは

JavaScript の「イベント」は、どのタイミングで○○させるかを指定するためのもの。○○は関数です。
ユーザがページや要素などを操作したときに、イベントが起こります。
これまで出てきた load(ロード完了時)や click(クリック時)などがイベントです。

マウスやキーボードの操作に関係あるイベントには、他に下記のものがあります。
mousemove(ポインタが移動する時)
mousedown(マウスを押した時)
mouseup(マウスボタンを離した時)
mouseover(ロールオーバー時)
mouseout(ロールアウト時)
dragdrop(ドラッグ&ドロップ時)
scroll(スクロール時)
dblclick(ダブルクリック時)
keydown(キーを押してる間)
keypress(キーを押した時)
keyup(キーを離した時)
(他にページの読み込みやフォームに関する abort, unload, error, submit, reset, change,, resize, blur, focus などがあります)

●addEventListener() は何度でもイベントや関数を登録できる

イベントが起こった時に関数が動くようにするには、ボタンなどの要素に「onclick」で指定する方法をサンプル1でやりましたが、このサンプルでは「addEventListener() 」を使ってみました。
「addEventListener() 」は、複数書いて、エレメントに何度でもイベントと関数を登録できるのが利点なのだそうです。書いた順番で実行されます。
(今回は、1つだけしか使いませんけどね)

それに比べて onclick など、HTMLのイベント属性(または script内のイベントプロパティ)は、
複数書くと、前のものを後から書いたものが上書きして、1コしか使えません。

●addEventListener() の3つの引数

サンプル2のイベントリスナー登録の部分を見てみると、

function draw2() {
    var cnvs2 = document.getElementById('canvas2');
    var ctx2 = cnvs2.getContext('2d');
    /*イベントリスナー登録_イベントはclickに*/
    cnvs2.addEventListener('click', onClick, false);
    function onClick(e) {
    /*この部分に関数「onClick()」の内容を書きます*/
    };
}

addEventListener() の () 内に、3つの引数があるのが分かります。
第1引数は「イベントタイプ」
上で紹介したイベント名を書きます。この場合は 'click' にしています。
第2引数は「イベントリスナー」
ここには呼び出す関数名を書きます。サンプルでは「onClick」という関数名を登録しています。
第3引数はとりあえず false でOK
これは「フェーズ」という、イベントが伝わって行く順番と言うか方法を決める引数だそうです。
真偽値 true か false の2択で、デフォルトは false。
trueなら「Captureフェーズ」
falseなら「Targetフェーズ」か「 Bubblingフェーズ」
だそうですが、フェーズについての理解があやふやなので、説明は割愛します(謝)。
とりあえず false(Captureフェーズにしない)でOKみたい。今回のところは。

なお、この第3引数は省略可能だそうですが、(デフォルトが false なので、省略すると falseになる)
古めのブラウザだと、第3引数が省略されているとエラーになるそうなので、ちゃんと false と書いといたほうが良さそう

さて、これで「canvas2」というID名の<canvas>要素に、
クリックで「onClick」という名前の関数を実行する準備ができました。

getBoundingClientRect() でカンバスの座標をゲットして、クリック位置を取得

これから「onClick」という名前の関数を書いて行きます。
まず、キャンバスをクリックした位置の座標をゲットしなくてはなりません。

●getBoundingClientRect() でキャンバスの座標情報をゲット

「getBoundingClientRect() 」を使うと、要素の絶対座標値を取得する事ができます。
BoundingClientRect とは、囲まれた(Bounding)領域のボックス情報...といった意味。
ボックスの「幅、高さ、ブラウザウィンドウの左上からの距離」のx,y座標を取得します。

ソースの8行目で「e.target.getBoundingClientRect();」として、
この指定のターゲットのキャンバス( ID名 canvas2 )の領域の座標を取得して、
これを var rect(変数「rect」)に代入しています。
(引数はべつに e じゃなくても何でもOK。ここではイベントの e にしました。)

    function onClick (e) {
      /*このキャンバスの絶対座標値をゲットして、マウスの位置を取得*/
      var rect = e.target.getBoundingClientRect();
      var mouseX =  Math.round(e.clientX - rect.left);
      var mouseY =  Math.round(e.clientY - rect.top);
      /*このあと、色指定を作ります*/
      };

次に、変数「mouseX」と変数「mouseY」にキャンバス上をクリックした座標を代入します。(9行目と10行目)
これには「clientX」「clientY」を利用します。

●clientX clientX からキャンバス上のクリック位置を割り出す

「client(クライアント)」とはブラウザの「ウィンドウ」のこと。
clientX は、イベントが発生したときのブラウザウィンドウのx座標、
clientY は、y座標の値を表します。

というわけで、clientX から、先ほど取得した変数rect(キャンバスの座標情報)のうち、左側の空き(rect.left)を引くことで、キャンバス上のクリックされた x座標がわかります。
同じく clientY から、キャンバスの上の空き(rect.top)を引けば、キャンバス上の y座標がゲットできるってわけです。

このままでは、x, y座標に小数点以下の数字が含まれた状態なので(ブラウザによっては小数でもOKらしいけど)、一応、Math.round() の中にぶっ込んで、四捨五入しています。

これで、ID名 canvas2 のキャンバス上をクリックしたら、その座標がゲットできるようになりました。

複数の色指定をランダムに適用する

あとは、グラフィクスの描画をするだけですが、
その前に、Math.random() を使って、色をランダムに適用するようにしてみました。
(サンプル1では「位置」がランダムでしたが、今回はその応用です)

      /*色指定を5種類作ってランダム化する*/
      var color1 = '#f00';
      var color2 = '#ffeb00';
      var color3 = '#f0f';
	  var color4 = '#0096ff';
	  var color5 = '#0f0';
      var clr = Math.floor(Math.random()*5+1);
      var thisColor;
      if (clr == 1) thisColor = color1;
      if (clr == 2) thisColor = color2;
      if (clr == 3) thisColor = color3;
	  if (clr == 4) thisColor = color4;
	  if (clr == 5) thisColor = color5;
      /*色指定と、正円の描画*/
	  ctx2.globalAlpha = 0.2;
      ctx2.fillStyle = thisColor;
      ctx2.beginPath();
      ctx2.arc(mouseX,mouseY,40,0,Math.PI*2,false);
      ctx2.fill();

変数 color1〜5 まで、5種類の色指定をします。(12〜16行目)

変数 clr に、1から5までの乱数を Math.random() で発生させます。(17行目)
乱数は0.000...から0.999...の範囲なので、5をかけて1足せば、1.000...から5.999...になります。
これを Math.floor() に入れて、小数点以下を切り捨てれば、1から5までの乱数になるわけです。

次に、変数 thisColor っていうのを決めて、もしも clr==1(clrが1と同一)だったら、変数thisColorは color1(#f00) といった具合で、5色をランダムに割り当てた thisColor を作ります。

で、描画の前の色指定で「ctx2.fillStyle = thisColor;」(26行目)とすればOK。
コレの前に「ctx2.globalAlpha = 0.2;」(25行目)として、透明度を20%にしています。

クリック位置に描画させる

上のソースの28行目を見てください。
正円の x座標, y座標を書くところに、変数 mouseX、mouseY を指定しています。
これでOK。変数 mouseX、mouseY は、キャンバス上のクリックされた座標ですから、
ここを中心点にして円が描かれます。

マウスムーブで描画する

サンプル3は、キャンバス上で「マウスを動かす(mousemove)」と描画するようにしました。サンプル1、サンプル2 の応用です。

これは画像です↓ クリックで別ウィンドウで開きます。

addEventListener() でmousemoveイベントを登録する

addEventListener() の第1引数で「mousemove」を指定すれば、マウスポインタが移動している時に関数を実行することができます。
addEventListener() についてはサンプル2で詳細を説明しています。)

ソースを見てみましょう。
5行目で、addEventListener() で「mousemove」イベントで「onMove」という関数を実行するよう指定しています。

function draw3() {
    var cnvs3 = document.getElementById('canvas3');
    var ctx3 = cnvs3.getContext('2d');
    /*イベントリスナー登録_イベントはmousemove*/
    cnvs3.addEventListener('mousemove', onMove, false);
    function onMove (m) {
      /*このキャンバスの絶対座標値をゲットして、マウスの位置を取得*/
      var rect3 = m.target.getBoundingClientRect();
      var mx =  Math.round(m.clientX - rect3.left);
      var my =  Math.round(m.clientY - rect3.top);
      /*色指定を5種類作ってランダム化*/
      var clor1 = '#f00';
      var clor2 = '#ff0';
      var clor3 = '#f0f';
      var clor4 = '#0ff';
      var clor5 = '#0f0';
      var cl = Math.floor(Math.random()*5+1);
      var tColor;
      if (cl == 1) tColor = clor1;
      if (cl == 2) tColor = clor2;
      if (cl == 3) tColor = clor3;
      if (cl == 4) tColor = clor4;
      if (cl == 5) tColor = clor5;
      /*円の半径のランダム化*/
      var hank = Math.floor(Math.random()*8+3);
      /*色指定と、正円の描画*/
      ctx3.globalAlpha = 0.4;
      ctx3.fillStyle = tColor;
      ctx3.beginPath();
      ctx3.globalCompositeOperation = 'lighter';
      ctx3.arc(mx,my,hank,0,Math.PI*2,false);
      ctx3.fill();
    };
}

それから、8,9,10行目で「getBoundingClientRect()」や「clientX」「clientY」を使って、キャンバス上のマウスポインタの位置を取得しています。

複数の半径をランダムに適用する

上のソースの25行目で、
var hank = Math.floor(Math.random()*8+3);
と指定しています。
これは、描画する正円の半径を 3px〜10px までの範囲でランダムにするための指定です。

Math.random() で発生する乱数は、0.000...から0.999...の範囲なので、
8をかけると0.000...から7.999...になります。
これに3を足して、3.000...から10.999...の範囲の乱数にして、
Math.floor() に入れて小数点以下を切り捨てているので、3から10までの半径になるわけです。
Math.random() などについての詳細はサンプル1で。)
これで、直径にすると6pxから20pxまでの正円がランダムに描かれるようになりました。

次回予告

今回のサンプル3で、ctx3.globalCompositeOperation = 'lighter' と指定しています。(30行目)
「globalCompositeOperation」は、カンバス上で複数のグラフィックスが重なった際の処理を指定するプロパティで、値は「lighter」以外にもいろいろあります。

次回はこの「globalCompositeOperation」の値を全部(11コ)試してみます。

関連記事
このエントリーをはてなブックマークに追加

やる気を保つためにランキングに参加しています。
応援してくださると すっごいやる気を出します! (笑)

初心者にも使いやすい(と思う)レンタルサーバー

「初心者ですがレンタルサーバーはどこがいい?」というご質問をよくいただきます。
自由にファイルをアップロードできる自分のサーバがあると便利ですよね。ローカル環境じゃなくサーバ上で試してみたい時がありますからね。
私が使っているのは、 スターサーバーロリポップ!です。どちらも管理画面がわかりやすく、マニュアルも充実していて、料金も安い。どちらもライトプラン以上で WordPress が使えます。
初心者が始めやすいサーバだと思います。

ちょっと料金は高いけど、さくらのレンタルサーバや、エックスサーバー は、やはり老舗なのでおすすめです。
両方とも高スペックでコスパが良く、老舗でユーザーが多いので、質問する場がたくさんあります。初心者だけど仕事でサーバが欲しい場合は、安心なのではないかと思います。

スポンサーリンク

コメントの投稿

ご注意:メールアドレスは書かないで
「コメントを送信する」ボタンを押した後の「確認画面」で、メールアドレス・URL などを入力できるようになっており、メールアドレス・URL は、そのままオートリンクになる仕様です。
当方でメールアドレスだけ削除することも、メールアドレスを非公開にすることもできません
メールアドレスは書かないでください。詳しくはこちらにまとめましたのでご覧ください。

スポンサーリンク
最新記事
Category
オススメの本
Links
Calendar
11 | 2023/12 | 01
- - - - - 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31 - - - - - -
Archive
Profile

yuki★hata

Author : yuki★hata
せめて月1回の更新をめざします~。

メールフォームはこちら

スポンサーリンク
スポンサーリンク
Copyright © ほんっとにはじめてのHTML5とCSS3 All Rights Reserved.