CANVASの画像転送・重ね書き |
今回は矢印(→)、円(○)、長方形(□)などをマウス操作で追加する方法を確認するつもりでしたが、その前にCANVAS上の画像を別のCANVASに重ね書きする方法について調べました。先回のお絵描きツールでは、連続して“mousemove”イベント発生毎にCANVAS上に直線を書き足す方法でした。
一方、矢印(→)、円(○)、長方形(□)の場合は対角等の2点を指定し形状を確定する場合、“mouseup”イベント発生時に最終形状を確定します。但し、途中の“mousemove”イベント発生時点での途中の形状が確認できる方が、編集する人が意図する形状を早く正確に描画出来ると考え、CANVASをもう一つ用意し、途中の状態を更新・表示してみようと思っています。“mouseup”イベント発生時に、追加したCANVASに描かれている形状を対象CANVASに重ね書きすることで、描画要素を追加していきます。(※ここでの“重ね書き”は、対象CANVASの元の画像を残し、追加部のみを付け足す様に書き込むという意味です。)
テストプログラム① |
下図の通りテストプログラム①を作成しました。
(→テストプログラム①にリンク)
今回は2つのCANVASを用意しています。左のCANVASはお絵描き用のCANVASでマウス操作で直接描画出来ます。右のCANVASは、マウスで直接お絵描き出来ませんが、左のCANVASのお絵描き内容を重ね書き出来ます。“CANVAS合成”ボタンをクリックすると重ね書きを実行します。テストプログラムでは転送結果を視覚的に判りやすくする為にCANVASを横並びに配置していますが、実際には2つのCANVASを上下に重ね合わせて配置し、“mouseup”イベント発生時にCANVAS合成と右側CANVASの初期化を自動的に行いますので、あたかも1つのCANVASに描画している様に感じると思います。
プログラムについてメモしておきます。行番150~151で2つのCANVASを配置します。行番124では“CANVAS合成”ボタンを配置します。“CANVAS合成”ボタンをクリックすると、行番110~116の“CVS_Transfer()”関数が実行されます。マウス操作によって描画された左のCANVASデータを行番112で画像データに変換し、行番114で右のCANVASに重ね書きします。
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>DRAWING TEST</title> <style> input { height:30px; } #select-color input { width:26px; height:36px; } </style> <script type="text/javascript" src="./js/jquery-3.3.1.min.js"></script> <script> var cvs_drw ; var ctx_drw ; var cvs_edt ; var ctx_edt ; var cvs_wdt ; var cvs_hgt ; var x_b , y_b , x_c , y_c ; var f_b ; function draw_begin(){ cvs_wdt = 325 ; cvs_hgt = 360 ; cvs_drw = document.getElementById("CANVAS_DRW") ; ctx_drw = cvs_drw.getContext("2d") ; cvs_edt = document.getElementById("CANVAS_EDT") ; ctx_edt = cvs_edt.getContext("2d") ; cvs_drw.width = cvs_wdt ; cvs_drw.height = cvs_hgt ; cvs_edt.width = cvs_wdt ; cvs_edt.height = cvs_hgt ; cvs_edt_init() ; ctx_drw.strokeRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; cvs_edt.addEventListener( "mousedown" , mouse_dn , false ) ; cvs_edt.addEventListener( "mousemove" , mouse_mv , false ) ; cvs_edt.addEventListener( "mouseup", mouse_up , false ) ; } //▼▼▼▼▼▼ マウス検知 ▼▼▼▼▼▼ function mouse_dn(event){ var l_thkn = 1 ; var t_rate = 1 ; var l_colr = "#333333" ; var rect = event.target.getBoundingClientRect() ; x_b = event.clientX-rect.left ; y_b = event.clientY-rect.top ; f_b = true ; t_rate = document.getElementById("s_transparency").value/100 ; l_colr = document.getElementById("select-color").style.backgroundColor ; l_thkn = document.getElementById("l_thickness").value ; ctx_edt.globalAlpha = t_rate ; ctx_edt.strokeStyle = l_colr ; ctx_edt.lineWidth = l_thkn ; ctx_edt.lineJoin = "round" ; ctx_edt.lineCap = "round" ; } function mouse_mv(event){ if(f_b){ var rect = event.target.getBoundingClientRect() ; x_c = event.clientX-rect.left ; y_c = event.clientY-rect.top ; draw_line() ; x_b = x_c ; y_b = y_c ; } } function mouse_up(event){ f_b=false; } //▲▲▲▲▲▲ マウス検知 ▲▲▲▲▲▲ // 自由線を引く function draw_line(){ ctx_edt.beginPath() ; ctx_edt.moveTo(x_b,y_b) ; ctx_edt.lineTo(x_c,y_c) ; ctx_edt.stroke() ; } // CVS_edt 初期化 function cvs_edt_init(){ ctx_edt.globalAlpha = 1 ; ctx_edt.lineWidth = 1 ; chng_col("#000000") ; ctx_edt.clearRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; ctx_edt.strokeRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; } // CVS_drw 初期化 function cvs_drw_init(){ ctx_drw.globalAlpha = 1 ; ctx_drw.lineWidth = 1 ; chng_col("#000000") ; ctx_drw.clearRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; ctx_drw.strokeRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; } // 描画色変更 function chng_col(gColor){ ctx_edt.strokeStyle = gColor ; document.getElementById("select-color").style.backgroundColor = gColor ; } // CANVAS 転送 function CVS_Transfer(){ var img_edt = new Image ; img_edt.src = ctx_edt.canvas.toDataURL() ; img_edt.onload = function(){ ctx_drw.drawImage(img_edt,0,0) ; } } </script> </head> <body onLoad="draw_begin()"> <TABLE><TR> <TD><input type="button" value="左消去" id="ClearCVS" onclick="cvs_edt_init()"></TD> <TD><input type="button" value="CANVAS合成" id="TransCVS" onclick="CVS_Transfer()"></TD> <TD style="width:5px;"></TD> <TD> <input type="button" style="background-color:#000000;" onclick="chng_col('#000000')"> <input type="button" style="background-color:#008000;" onclick="chng_col('#008000')"> <input type="button" style="background-color:#00ff00;" onclick="chng_col('#00ff00')"> <input type="button" style="background-color:#0000ff;" onclick="chng_col('#0000ff')"> <input type="button" style="background-color:#00ffff;" onclick="chng_col('#00ffff')"> <input type="button" style="background-color:#fffacf;" onclick="chng_col('#fffacf')"> <input type="button" style="background-color:#ffff00;" onclick="chng_col('#ffff00')"> <input type="button" style="background-color:#ffa500;" onclick="chng_col('#ffa500')"> <input type="button" style="background-color:#ff00ff;" onclick="chng_col('#ff00ff')"> <input type="button" style="background-color:#ff0000;" onclick="chng_col('#ff0000')"> </TD> <TD style="width:5px;"></TD> <TD id="select-color"> <select id="s_transparency" style="height:28px; text-align:center; font-size:16px; margin:0 3px;"> <option value="100">100</option><option value="80">80</option><option value="60">60</option> <option value="40">40</option><option value="20">20</option></select> <input type="number" id="l_thickness" min="1" max="500" value="3" style="height:23px; width:50px; text-align:center; font-size:16px; margin:0 3px;"> </TD> <TD><input type="button" value="右消去" id="ClearCVS" onclick="cvs_drw_init()"></TD> </TR></TABLE> <div id="cvs-layer"> <canvas id="CANVAS_EDT"></canvas> <canvas id="CANVAS_DRW"></canvas> </div> </body> </html>
テストプログラム② |
下図の通りテストプログラム②を作成しました。
(→テストプログラム②にリンク)
プログラム②は基本的には①と同じですが、2つのCANVASを上下に重ね合わせ、“mouseup”イベント発生時にCANVAS合成・初期化を自動実行しますので、CANVASが複数ある様には見えません。
プログラム①と異なるポイントのみですが、行番9~10により、二つのCANVASを上下に重ね合わせます。行番74で“mouseup”イベント発生時にCANVAS合成・初期化を自動的に実行します。
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>DRAWING TEST</title> <style> #cvs-layer { position:relative ; } #cvs-layer canvas { position:absolute ; top:0 ; left:0 ; } input { height:30px; } #select-color input { width:26px; height:36px; } </style> <script type="text/javascript" src="./js/jquery-3.3.1.min.js"></script> <script> var cvs_drw ; var ctx_drw ; var cvs_edt ; var ctx_edt ; var cvs_wdt ; var cvs_hgt ; var x_b , y_b , x_c , y_c ; var f_b ; function draw_begin(){ cvs_wdt = 325 ; cvs_hgt = 360 ; cvs_drw = document.getElementById("CANVAS_DRW") ; ctx_drw = cvs_drw.getContext("2d") ; cvs_edt = document.getElementById("CANVAS_EDT") ; ctx_edt = cvs_edt.getContext("2d") ; cvs_drw.width = cvs_wdt ; cvs_drw.height = cvs_hgt ; cvs_edt.width = cvs_wdt ; cvs_edt.height = cvs_hgt ; cvs_edt_init() ; chng_col("#000000") ; ctx_drw.strokeRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; cvs_edt.addEventListener( "mousedown" , mouse_dn , false ) ; cvs_edt.addEventListener( "mousemove" , mouse_mv , false ) ; cvs_edt.addEventListener( "mouseup", mouse_up , false ) ; } //▼▼▼▼▼▼ マウス検知 ▼▼▼▼▼▼ function mouse_dn(event){ var l_thkn = 1 ; var t_rate = 1 ; var l_colr = "#333333" ; var rect = event.target.getBoundingClientRect() ; x_b = event.clientX-rect.left ; y_b = event.clientY-rect.top ; f_b = true ; t_rate = document.getElementById("s_transparency").value/100 ; l_colr = document.getElementById("select-color").style.backgroundColor ; l_thkn = document.getElementById("l_thickness").value ; ctx_edt.globalAlpha = t_rate ; ctx_edt.strokeStyle = l_colr ; ctx_edt.lineWidth = l_thkn ; ctx_edt.lineJoin = "round" ; ctx_edt.lineCap = "round" ; } function mouse_mv(event){ if(f_b){ var rect = event.target.getBoundingClientRect() ; x_c = event.clientX-rect.left ; y_c = event.clientY-rect.top ; draw_line() ; x_b = x_c ; y_b = y_c ; } } function mouse_up(event){ f_b=false; CVS_Transfer() ; } //▲▲▲▲▲▲ マウス検知 ▲▲▲▲▲▲ // 自由線を引く function draw_line(){ ctx_edt.beginPath() ; ctx_edt.moveTo(x_b,y_b) ; ctx_edt.lineTo(x_c,y_c) ; ctx_edt.stroke() ; } // CVS_edt 初期化 function cvs_edt_init(){ ctx_edt.globalAlpha = 1 ; ctx_edt.lineWidth = 1 ; ctx_edt.strokeStyle = "#000000" ; ctx_edt.clearRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; ctx_edt.strokeRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; } // CVS_drw 初期化 function cvs_drw_init(){ ctx_drw.globalAlpha = 1 ; ctx_drw.lineWidth = 1 ; ctx_drw.clearRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; ctx_drw.strokeRect( 0 , 0 , cvs_wdt , cvs_hgt ) ; } // 描画色変更 function chng_col(gColor){ ctx_edt.strokeStyle = gColor ; document.getElementById("select-color").style.backgroundColor = gColor ; } // CANVAS 転送 function CVS_Transfer(){ var img_edt = new Image ; img_edt.src = ctx_edt.canvas.toDataURL() ; img_edt.onload = function(){ ctx_drw.drawImage(img_edt,0,0) ; cvs_edt_init(); } } </script> </head> <body onLoad="draw_begin()"> <TABLE><TR> <TD style="width:5px;"></TD> <TD> <input type="button" style="background-color:#000000;" onclick="chng_col('#000000')"> <input type="button" style="background-color:#008000;" onclick="chng_col('#008000')"> <input type="button" style="background-color:#00ff00;" onclick="chng_col('#00ff00')"> <input type="button" style="background-color:#0000ff;" onclick="chng_col('#0000ff')"> <input type="button" style="background-color:#00ffff;" onclick="chng_col('#00ffff')"> <input type="button" style="background-color:#fffacf;" onclick="chng_col('#fffacf')"> <input type="button" style="background-color:#ffff00;" onclick="chng_col('#ffff00')"> <input type="button" style="background-color:#ffa500;" onclick="chng_col('#ffa500')"> <input type="button" style="background-color:#ff00ff;" onclick="chng_col('#ff00ff')"> <input type="button" style="background-color:#ff0000;" onclick="chng_col('#ff0000')"> </TD> <TD style="width:5px;"></TD> <TD id="select-color"> <select id="s_transparency" style="height:28px; text-align:center; font-size:16px; margin:0 3px;"> <option value="100">100</option><option value="80">80</option><option value="60">60</option> <option value="40">40</option><option value="20">20</option></select> <input type="number" id="l_thickness" min="1" max="500" value="3" style="height:23px; width:50px; text-align:center; font-size:16px; margin:0 3px;"> </TD> <TD><input type="button" value="右消去" id="ClearCVS" onclick="cvs_drw_init()"></TD> </TR></TABLE> <div id="cvs-layer"> <canvas id="CANVAS_DRW"></canvas> <canvas id="CANVAS_EDT"></canvas> </div> </body> </html>
まとめ |
今回の内容では、自分でもなぜ2つのCANVASを用意して、合成させる必要があるのか、目的が判りにくい様にも感じます。でも恐らく、次回投稿では理解頂けるのではないかと思います。
たぶん。