LogoMark.png

Canvas の変更点


#author("2021-05-20T15:09:26+09:00;2021-05-20T15:03:35+09:00","default:inoue.ko","inoue.ko")
*Canvas API
https://developer.mozilla.org/ja/docs/Web/API/Canvas_API
~
//RIGHT:
//[[WebDesign/JavaScript]]
//#clear

このページでは、HTML の <canvas> 要素に JavaScript によってグラフィックを描く Canvas API の事例を紹介します。

Canvas API は主に 2D グラフィックを対象としています。3Dを扱う場合については [[WebGL]] API の記事をご覧ください。

~

***事例1 縦横に矩形を並べて描きます。 [[→DEMO>https://design.kyusan-u.ac.jp/SampleSite/JS_Canvas/]]

CANVASを用いたインタラクティブなグラフィックスのページを作るための、基本的なサンプルです。ウインドウが読み込まれた時点で、まず画面のサイズを計測して、CANVASが画面中央に位置するように計算しています。
~

''index.html'' | ページの文書構造

 <!DOCTYPE html>
 <html lang="ja">
     <head>
         <title>Basic Sample</title>
         <script type="text/javascript" src="sample.js"></script>
         <link rel="stylesheet" type="text/css" href="sample.css">
     </head>
 
     <body>
         <canvas id="myCanvas" width="480" height="480"></canvas>
         <input type="button"  id="myButton" value="draw">
     </body>
 </html>

~

''sample.css'' | ページのビジュアル

 #myCanvas {
     position:absolute;
 }
 
 #myButton {
     position:absolute;
 }
~

''sample.js'' | ページのふるまい

 window.onload = function(){
     var mh = window.innerHeight/2 - 10;
     var mw = window.innerWidth/2;
     document.getElementById("myCanvas").style.top = String( (window.innerHeight - 480)/2 ) +'px';
     document.getElementById("myCanvas").style.left = String( (window.innerWidth - 480)/2 ) +'px';
     document.getElementById("myCanvas").style.width = String(480)+'px';
     document.getElementById("myCanvas").style.height = String(480)+'px';
     document.getElementById("myCanvas").style.background = "silver";
     document.getElementById("myButton").style.top = String( window.innerHeight/2 + 280 ) +'px';
     document.getElementById("myButton").style.left = String( window.innerWidth/2 - 20 ) +'px';
     document.getElementById("myButton").addEventListener("click", Draw, false);
 }
 
 function Draw(){
     var cnvs = document.getElementById("myCanvas");
     var dc = cnvs.getContext("2d");
     var x,y;
     for( y=0; y<480; y+=40 ){
         for( x=0; x<480; x+=40 ){
              dc.fillStyle = "#"+ Math.floor(Math.random() * 0xFFFFFF).toString(16);
              dc.fillRect( x+8, y+8, 24, 24 );		
         }
     }
 }

~
~

***事例2 Canvas上でのアニメーション [[→DEMO>https://design.kyusan-u.ac.jp/SampleSite/JS_Animate/]]

アニメーションの基本は「古い絵を消して、新しい場所に描く」を目に止まらない速さで繰り返す・・・ということです。
//[[ダブルバッファリング>Google:HTML5 canvas ダブルバッファリング]]という技法を使えば、さらに画面のチラツキがなくなるようですが、ここでは基本を理解する目的で、シンプルな記述をしています。

''index.html''
 <!DOCTYPE html>
 <html lang="ja">
         <head>
                 <meta charset="UTF-8">
                 <link rel="stylesheet" type="text/css"  href="style.css">
                 <script type="text/javascript" src="sample.js"></script>
                 <title>Sample Page</title>
         </head>
         <body>
                 <h1>Sample Graphics</h1>
                 <canvas id="myCanvas" width="640" height="480" ></canvas>
                 <p>JavaScriptによるCANVASへの描画</p>
         </body>
 </html>
~

''style.css''
 body {
     background-color: silver;
     color: white;
     text-align:center;
 }
 h1{
     font-size: 24pt;
 }
 canvas{
     background-color:black;
 }
~


''sample.js''
 var cnvs,dc;
 var X=Y=200;
 var vx=vy=10.0;
 
 window.onload = function(){
     cnvs = document.getElementById("myCanvas");
     dc = cnvs.getContext("2d");
     Draw();
 }
 
 function Draw(){
 
     requestAnimationFrame( Draw );
 
     dc.fillStyle = "black";
     dc.fillRect(0, 0, 640, 480); 
 
     X += vx;
     Y += vy;
 
     if(X < 0 || X > 640){
          vx *= -1;
     }
     if(Y < 0 || Y > 480){
          vy *= -1;
     }
 
     dc.beginPath();
     dc.arc(X, Y, 10, 0, 2 * Math.PI, false);
     dc.fillStyle = 'red';
     dc.fill();
 }

ボールが画面内で反射しながら動きます。
Draw()全体で1フレーム分、これを毎秒約60回(60fps)実行します。
黒でcanvas全体を消して、座標()を更新して、円を描く・・
という極めて単純な作業の繰り返しです。

Draw関数の中に書かれた以下の記述が、Draw自体を繰り返し実行させます。
 requestAnimationFrame( Draw );

動かす=描画する座標を更新していく ということで、
これは、以下の記述で実現しています。
 X += vx; (Y方向も同様)  
これは X = X+vx; と同じ意味で、
現在のX座標にvxを足した値を、新たな座標Xに設定します。
例えば、vxが1だったとすると、この式を実行する度に、Xは1ずつ増える・・・
ということになります。

壁面(境界)での方向転換は、以下の式で実現しています。
 vx *= -1; (Y方向も同様)
これは vx = vx*(-1); と同じ意味で、現在が+ならーへ、現在がーなら+へと
反転します。

''補足''
アニメーションをストップさせるには、cancelAnimationFrame()。
例えば以下のような書き方をすれば、100フレーム目でストップします。
 count = count+1;
 if(count<100){
     requestAnimationFrame( Draw );
 }else{
     cancelAnimationFrame( Draw );
 }
~
~

***事例3 マウスイベント [[→DEMO>https://design.kyusan-u.ac.jp/SampleSite/JS_MouseEvent]]

HTMLとCSSは、事例2と同じなので省略します。
JavaScriptの部分だけ、以下のものに差し替えてみて下さい。
canvas上でクリックする都度、画面上にランダムな色と大きさの
ボールが追加されます。

''sample.js''
 var cnvs,dc;
 var N = 0;
 var R = new Array(1000);
 var C = new Array(1000);
 var X = new Array(1000);
 var Y = new Array(1000);
 var vx= new Array(1000);
 var vy= new Array(1000);
 
 window.onload = function(){
     cnvs = document.getElementById("myCanvas");
     dc = cnvs.getContext("2d");
     cnvs.addEventListener("click", AddObject, false);
     Draw();
 }
 
 function AddObject(event){
 
     if( N >= 999 ) return;
  
     var rect = event.target.getBoundingClientRect();
  
     X[N] = event.clientX - rect.left;
     Y[N] = event.clientY - rect.top;
     vx[N] = Math.floor ( Math.random()*15 - 7 );
     vy[N] = Math.floor ( Math.random()*15 - 7 );
  	
     R[N] = Math.floor ( Math.random()*15 + 5 );
 
     var r = Math.floor ( Math.random()*256 );
     var g = Math.floor ( Math.random()*256 );
     var b = Math.floor ( Math.random()*256 );
     C[N] = 'rgb('+ r + ',' + g + ','+ b +')';
 
  N++;
 }
 
 function Draw(){
 
     requestAnimationFrame( Draw );
 
     dc.fillStyle = "black";
     dc.fillRect(0, 0, 640, 480); 
     
     for(i=0; i<N; i++){
  
         X[i] += vx[i];
         Y[i] += vy[i];
     
         if(X[i] < 0 || X[i] > 640){
             vx[i] *= -1;
         }
 
         if(Y[i] < 0 || Y[i] > 480){
             vy[i] *= -1;
         }
     
         dc.beginPath();
         dc.arc(X[i], Y[i], R[i], 0, 2 * Math.PI, false);
         dc.fillStyle = C[i];
         dc.fill();
     }
 }
ここでは、複数のボールを制御するために、配列変数を使っています。
 var X = new Array(1000);
と書くことで、X[0] 〜 X[999]までの配列が確保されます。

X[i] : i 番目のボールのX座標
C[i] : i 番目のボールの色(r,g,bの3つの値で定義されています)
R[i] : i 番目のボールの大きさ(半径)  ・・・といった感じです。

window.onload時点で、canvas上でのクリックイベントに対して、
AddObjectという関数を実行するように指示しています。
以下のような記述です。クリックイベントに対する「御用聞き」という意味で
イベント・リスナーと呼ばれます。
 cnvs.addEventListener("click", AddObject, false);
~
~

***事例4 キーボードイベント [[→DEMO>https://design.kyusan-u.ac.jp/SampleSite/JS_KeyEvent]]

HTMLとCSSは事例2のままでOKですが、HTMLに記載されたキャンバスのサイズは縦長に変更する方がいいでしょう。 
 <canvas id="myCanvas" width="480" height="640" ></canvas>
JavaScriptの部分だけ、以下のものに差し替えてみて下さい。
Enterキーでボールが出ます。左右カーソルでラケットが動きます。
キーボードでラケットが動きにくい場合は、マウスで([[DEMO2>https://design.kyusan-u.ac.jp/SampleSite/JS_KeyEvent2]])

''sample.js''
 var cnvs,dc;
 var N = 0;
 var BX = new Array(1000);
 var BY = new Array(1000);
 var vx= new Array(1000);
 var vy= new Array(1000);
 var R = new Array(1000);
 var C = new Array(1000);
 var RX,RY,RWIDTH,RHEIGHT;
 var message = "Game Over";
 var audioHit = new Audio("hit.wav");
 var audioMiss = new Audio("miss.wav");
 
 window.onload = function(){
     cnvs = document.getElementById("myCanvas");
     dc = cnvs.getContext("2d");
     RWIDTH = cnvs.width/5;
     RHEIGHT = cnvs.height/32;
     RX = cnvs.width/2 - RWIDTH/2;
     RY = cnvs.height - 80;
     window.addEventListener('keydown',keyEventFunc,true);
     Draw();
 } 
 
 function keyEventFunc(event){
     if( event.keyCode == 13 ) AddObject();
     if( event.keyCode == 37 ) MoveRacket(-20);
     if( event.keyCode == 39 ) MoveRacket(+20);
 }
 
 function AddObject(){
     if( N >= 999 ) return;
     message = ""; // Game Start 
     BX[N] = cnvs.width/2; 
     BY[N] = cnvs.height/5;
     vx[N] = Math.floor ( Math.random()*13 - 6 );
     vy[N] = Math.floor ( Math.random()*5 + 5 ) * (-1);
     R[N] = cnvs.width/64;
     var r = Math.floor ( Math.random()*256 );
     var g = Math.floor ( Math.random()*256 );
     var b = Math.floor ( Math.random()*256 );
     C[N] = 'rgb('+ r + ',' + g + ','+ b +')';
     N++;
 }
 
 function MoveRacket( v ){
     if( v < 0 && 0 < RX ) RX += v ;
     if( 0 < v && RX+RWIDTH < cnvs.width ) RX += v;
 }
 
 function Draw(){
 
     requestAnimationFrame( Draw );
 
     dc.fillStyle = "black";
     dc.fillRect(0, 0, cnvs.width, cnvs.height ); 
     
     for(i=0; i<N; i++){
          BX[i] += vx[i];
          BY[i] += vy[i];
          if(BX[i] < 0 || BX[i] > cnvs.width){ //wall
               vx[i] *= -1;
          }
          if(BY[i] < 0){ //roof
               vy[i] *= -1;
          }
          if( BY[i] > cnvs.height ) { //miss
               N = 0;
               message = "Game Over";
               audioMiss.play();
               break;
          }    
          if( ( RX < BX[i]  &&  BX[i] < RX+RWIDTH  )
               && ( RY < BY[i]+R[i]  &&  BY[i]-R[i] < RY ) ) { // racket
               vy[i] *= -1;
               audioHit.play();
          }
          dc.beginPath();
          dc.arc(BX[i], BY[i], R[i], 0, 2 * Math.PI, false);
          dc.fillStyle = C[i];
          dc.fill();
     }
    
     dc.fillStyle = "white";
     dc.fillRect(RX, RY, RWIDTH, RHEIGHT);
 
     dc.fillStyle = "white";
     dc.font = "20pt Arial";
     dc.fillText( message, cnvs.width/2 - 70, cnvs.height*0.45); 
 
 }
~
~


***事例5 マウスで簡単なお絵かき [[→DEMO>https://design.kyusan-u.ac.jp/SampleSite/JS_SimplePaint]]
マウスの位置情報を用いたシンプルなお絵描きです。index.html、style.css、sample.js の3つのソースコードをそれぞれテキストエディタにコピー&ペーストして、見出しにあるとおりのファイル名で同じフォルダに保存すれば、ローカルで動かすことができます。
注) Web標準ブラウザ:Firefox、Chrome、Safari等で動作確認しています。

''index.html''
 <!DOCTYPE html>
 <html lang="ja">
     <head>
         <meta charset="UTF-8">
         <link rel="stylesheet" type="text/css"  href="style.css">
         <script type="text/javascript" src="sample.js"></script>
         <title>Sample Page</title>
     </head>
     <body>
         <h1>Sample Graphics</h1>
         <canvas id="myCanvas" width="640" height="480" ></canvas>
         <p>JavaScriptによるCANVASへの描画</p>
     </body>
 </html>
~

''style.css''
 body {
     background-color: silver;
     color: white;
     text-align: center;
 }
 h1{
     font-size: 24pt;
 }
 canvas{
     background-color:black;
 }
~

''sample.js''
 var flag = false;
 var oldX = 0;
 var oldY = 0;
 
 window.onload = function(){
 	var cnvs = document.getElementById("myCanvas");
 	cnvs.addEventListener("mousemove", draw, true);
 	cnvs.addEventListener("mousedown", start, false);
 	cnvs.addEventListener("mouseup", stop,false);
 }
 
 function draw(event){
 	if (!flag) return;
 	var rect = event.target.getBoundingClientRect();
 	var x = event.clientX - rect.left;
 	var y = event.clientY - rect.top;
 	var cnvs = document.getElementById("myCanvas");
 	var dc = cnvs.getContext("2d");
 	dc.strokeStyle = "rgba(128,128,128,1)";
 	dc.lineWidth = 1;
 	dc.beginPath();
 	dc.moveTo(oldX, oldY);
 	dc.lineTo(x, y);
 	dc.stroke();
 	dc.closePath();
 	oldX = x;
 	oldY = y;
 }
 
 function start(event){
     flag = true;
     var rect = event.target.getBoundingClientRect();
     oldX = event.clientX - rect.left;
     oldY = event.clientY - rect.top;
 }
 
 function stop(){
     flag = false;
 }
~

***事例6 色やサイズを選んだお絵描き [[→DEMO>https://design.kyusan-u.ac.jp/SampleSite/JS_Paint/]]

少し煩雑なので、このページでのソースの掲載は省略しますが、
Firefoxなどで、[ファイル]>[名前をつけてページを保存] すれば、HTML、CSS、JavaScript すべて一括でDLして見ることができますので、
その方法でご覧下さい。

~
~

~