お絵描き②(CANVAS画像転送)

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を用意して、合成させる必要があるのか、目的が判りにくい様にも感じます。でも恐らく、次回投稿では理解頂けるのではないかと思います。
たぶん。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です