質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%

Q&A

解決済

2回答

784閲覧

ペン描画と要素操作をon/offで動作できるようにしたい

teru-

総合スコア13

0グッド

0クリップ

投稿2022/07/01 09:03

編集2022/07/07 17:05

前提

vanillaJS(ES6)を利用してサッカーボードのようなアプリを作成しているのですが、canvasへのペン描画と要素の追加および操作を分離することができません。

実現したいこと

動作イメージとしては、フラグがオンの場合は要素追加および操作のみ、
フラグがオフの場合はペン描画のみといったものです。]
現状偶数/奇数でフラグを分類しておりますが、フラグそのものは何でもよく、個々の処理を別々で動かしたいというのが本旨でございます。

発生している問題

現状、画像をクリックするとイベント発火し、変数yが偶数の場合は要素追加メソッドを呼び出し、奇数の場合はペン描画メソッドを呼び出しで動作を分離しようとしています。
ただ、この場合呼び出し先のイベントが残ったままとなるので、
y=<2で要素追加とペン描画のイベントが重なり、
下記のようにペン描画後に要素追加が重なってしまいます。
イメージ説明
そこで、コールバック関数listnenerのclickを処理するbreakの直前にremoveEventListener("click", listener);を入れてみましたが、
そうすると、要素追加が1度きりになってしまいます。(当然と言えば当然だとは思いますが)

該当のソースコード

index.htmlでmain.jsを呼び出し、ペン描画処理のDrawPen.jsと要素操作のElement.jsはクラス化しており、モジュールとしてmain.jsにインポートしています。

html:index.html

1<body> 2 <div class="container"> 3 <main> 4 <canvas id="draw_area" width="500px" height="500px" style="border: 1px solid #000000;"> 5 </canvas> 6 </main> 7 <div class="playerElem" id="player" data-elemid="0" style="top: 220px; left: 267px;">たいとる</div> 8 </div> 9 10 <!-- ペンの設定を変更する要素 --> 11 <div id="change_pen"> 12 <img src="img/pen.png" alt=""> 13 <div class="select_color"> 14 <ul> 15 <li data-color="black"> 16 <p>ペンの色:黒</p> 17 </li> 18 <li data-color="red"> 19 <p>ペンの色:赤</p> 20 </li> 21 <li data-color="blue"> 22 <p>ペンの色:青</p> 23 </li> 24 <li data-color="white"> 25 <p>消しゴム</p> 26 </li> 27 </ul> 28 </div> 29 </div> 30 <script src="js/main.js" type="module"></script> 31 <!-- テスト用スクリプト --> 32 <script> 33 </script> 34</body>

js:main.js

1"use strict"; 2import { DrawPen } from './DrawPen.js'; 3import { Element } from './Element.js'; 4 5const draw = new DrawPen(); 6draw.setCanvasSize(); 7// draw.drawPen(); 8// draw.changePenColor(); 9 10const elem = new Element(); 11// elem.moveElement(1); 12// elem.moveElement(2); 13 14// switch_btn押下で要素追加とペン描画を切り替える 15const switch_btn = document.querySelector("#change_pen > img"); 16let y = 0; 17switch_btn.addEventListener("click", () => { 18 y++; 19 if (y % 2 === 0) { 20 console.log(true); 21 elem.moveElement(); 22 } else if (y % 2 !== 0) { 23 console.log(false); 24 draw.drawPen(); 25 draw.changePenColor(); 26 } 27});

js:DrawPen.js

1"use strict"; 2export class DrawPen { 3 4 canvas = document.getElementById("draw_area"); 5 context = this.canvas.getContext("2d"); 6 7 // this減らしてもっと簡潔に書けないだろうか? 8 drawPen() { 9 this.canvas.addEventListener("mousemove", event => draw(event.layerX, event.layerY)); 10 this.canvas.addEventListener("touchmove", event => draw(event.layerX, event.layerY)); 11 //パソコンでクリックしてる間だけ描けるようにした機能 12 this.canvas.addEventListener("mousedown", () => { 13 this.context.beginPath(); 14 isDrag = true; 15 }); 16 this.canvas.addEventListener("mouseup", () => { 17 this.context.closePath(); 18 isDrag = false; 19 }); 20 //スマホで描けるようにする機能 21 this.canvas.addEventListener("touchstart", () => { 22 this.context.beginPath(); 23 isDrag = true; 24 }); 25 this.canvas.addEventListener("touchend", () => { 26 this.context.closePath(); 27 isDrag = false; 28 }); 29 30 let isDrag = false; 31 //線をかく機能 32 const draw = (x, y) => { 33 if (!isDrag) return; 34 this.context.lineTo(x, y); 35 this.context.stroke(); 36 } 37 } 38 39 // ペンの色を変えるメソッド 40 changePenColor() { 41 const selectList = document.querySelectorAll(".select_color > ul > li"); 42 const colorList = ["black", "red", "blue", "white"]; 43 for (let i = 0, len = selectList.length; i < len; i++) { 44 selectList[i].addEventListener("click", () => { 45 const color = selectList[i].getAttribute("data-color"); 46 // 下記はもっと簡潔に書く方法はないか…? 47 if (color === "black") { 48 this.context.globalCompositeOperation = "source-over"; 49 this.context.strokeStyle = "black"; 50 this.context.lineWidth =5; 51 } else if (color === "red") { 52 this.context.globalCompositeOperation = "source-over"; 53 this.context.strokeStyle = "red"; 54 this.context.lineWidth =5; 55 } else if (color === "blue") { 56 this.context.globalCompositeOperation = "source-over"; 57 this.context.strokeStyle = "blue"; 58 this.context.lineWidth =5; 59 } else { 60 this.context.globalCompositeOperation = 'destination-out'; 61 this.context.lineWidth =50; 62 } 63 }) 64 } 65 } 66 // canvasのサイズをmain要素に合わせる処理 67 setCanvasSize() { 68 const main = document.querySelector("main"); 69 const mainWidth = main.clientWidth; 70 const mainHeight = main.clientHeight; 71 console.log(mainWidth); 72 console.log(mainHeight); 73 74 this.canvas.setAttribute("width", mainWidth); 75 this.canvas.setAttribute("height", mainHeight); 76 } 77}

js:Element.js

1"use strict"; 2export class Element { 3 moveElement() { 4 const elements = document.getElementsByClassName("playerElem"); 5 const container = document.querySelector(".container"); 6 7 // 要素がクリックされた座標を取得する変数 8 let x; 9 let y; 10 11 const listener = (event) => { 12 console.log("イベント起動") 13 switch (event.type) { 14 case 'mousedown': 15 console.log("mousedown起動"); 16 this.lastMousedownElement = event.target.id; 17 let elem = event.target; 18 elem.classList.add("drag"); 19 20 if (event.type !== "mousedown") { 21 var event = event.changedTouches[0]; 22 } 23 24 x = event.pageX - elem.offsetLeft; 25 y = event.pageY - elem.offsetTop; 26 document.body.addEventListener("mousemove", mmove, false); 27 break; 28 case 'mouseup': 29 console.log("mouseup起動"); 30 this.lastMouseupElement = event.target.id; 31 let mouseup_elem = event.target; 32 mouseup_elem.classList.remove("drag"); 33 break; 34 case 'click': 35 console.log("click起動"); 36 console.log(this.lastMousedownElement); 37 console.log(this.lastMouseupElement); 38 if (this.lastMousedownElement === this.lastMouseupElement) return; 39 console.log("alert:要素を追加しました"); 40 alert("要素を追加しました"); 41 42 // 追加する要素を定義 43 const playerElem = document.createElement("div"); 44 playerElem.className = "playerElem"; 45 const text = document.createTextNode("たいとる"); 46 playerElem.setAttribute("id", "player"); 47 playerElem.appendChild(text); 48 container.appendChild(playerElem); 49 50 // クリックした座標位置に要素を配置 51 const drag = document.querySelectorAll(".playerElem"); 52 const elemId = drag.length - 1; 53 drag[elemId].setAttribute("data-elemId", elemId); 54 55 // 下記の20はマジックナンバーなので、他に算出方法あるかも 56 drag[elemId].style.top = event.pageY - 20 + "px"; 57 drag[elemId].style.left = event.pageX - 20 + "px"; 58 59 container.removeEventListener("click", listener); 60 break; 61 } 62 63 for (let dragElm of document.getElementsByClassName("playerElem")) { 64 dragElm.addEventListener('mousedown', listener, false); 65 document.addEventListener('mouseup', listener, false); 66 } 67 } 68 69 function mmove(event) { 70 const drag = document.getElementsByClassName("drag")[0]; 71 if (event.type !== "mousemove") { 72 let event = event.changedTouches[0]; 73 } 74 event.preventDefault(); 75 76 if (drag) { 77 drag.style.top = event.pageY - y + "px"; 78 drag.style.left = event.pageX - x + "px"; 79 } 80 } 81 container.addEventListener('click', listener, false); 82 container.addEventListener('mouseup', listener, false); 83 } 84}

かなり長文となってしまい申し訳ございません。
情報が不足しているあるいは、あいまいな表現で伝わらない部分がございましたら、ご指摘いただけますと幸いです。
何卒宜しくお願い致します。

追記になります。
※すみません。。②はmousedownではなくmouseupです。
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2022/07/01 11:18 編集

ふーむ、なるほど?イマイチやってる事がまだわからないから、要望を整理するね サッカーフィールドを模した画像の上で スルーパスみたいな時間軸の表現を共有したいという目的があって その手段としてcanvas要素の上で低機能なペイントツールを実装しようとしてるわけだね。 質問文の問題としては ペンや消しゴムの選択アイコンがキャンバスに被っているから 消しゴム等を選択するためにクリックすると、キャンバス上にクリック情報が伝播してしまって 「選択したペン・消しゴムをそのままキャンパスへ押し付けてしまう」のに困ってるのかな? もし合ってたらstopPropagationやイベントの伝播等で調査してみてね https://developer.mozilla.org/ja/docs/Web/API/Event/stopPropagation この記事の中の「イベントの伝播」リンク先にサンプルコードも載ってる
miyabi-sun

2022/07/01 11:25

ああ、理解した。 canvas要素は左上から等間隔で並んだ画素に対して 「お前の色は#000だ」みたいな指定をしてるだけで オブジェクトや線という概念が消えてしまうんだよ。 だからその用途を実現する為にはsvgを使ったほうが適切だと思うよ。 サッカーコートくらいはそのままcanvasでも良いかもね。 CSSでこんな感じにしてやれば全部重ねられるよ。 ``` main { position: relative; } canvas, 他の要素 { position: absolute; top: 0; left: 0; width: 600px; // キャンバスのサイズ height: 320px; // キャンバスのサイズ } ```
teru-

2022/07/01 13:43

ご回答ありがとうございます! すみません自分の質問がわかりにくく、、 まとめると、下記が解決したい事項になります! 大まかにはcanvas上で2つ機能がある想定です ・マウスを動かしてペンを使って描く(DrawPen.js) ・クリックで要素を追加する、その要素をマウスを使って動かす(Element.js) 今困っているのは、mousedown/mouseupでペン描画することとmousedown/mouseup/clickで要素を追加および操作することが同時に発生してしまうということです。 同時に発生してしまうと、ペン描画後(mousedown/mouseup)のclickがトリガーになってしまい、 要素の追加が意図せず発生してしまっています。 ここのペン描画と要素操作の処理をフラグ等?を用意してペン描画したいときはペン描画だけ。 要素を追加および動かしたい場合は要素の操作だけといった処理にしたいです。
guest

回答2

0

自己解決

自己解決しました。
ご協力いただきありがとうございました。

投稿2022/07/07 08:05

teru-

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

消しゴムは背景画像も消してしまうのでレイヤー的な処理が必要になるので割愛
とりあえずjQueryで

javascript

1<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> 2<script src="https://cdnjs.cloudflare.com/ajax/libs/jSignature/2.1.3/jSignature.min.js"></script> 3<script> 4$(function(){ 5 var w="480"; 6 var h="300"; 7 var ctx=$("#canvas").jSignature({width:w,height:h}).find('canvas').get(0).getContext('2d'); 8 $("#clear").on('click',function() { 9 $("#canvas").jSignature("reset"); 10 $('<img>').prop('src','sample.jpg').on('load',function(){ 11 ctx.drawImage($(this).get(0), 0, 0); 12 }); 13 }).trigger('click'); 14 $("#download-image").on('click',function() { 15 var a=$('<a>'); 16 a.attr({'href':$("#canvas").jSignature("getData"),download:"test.png"}).appendTo($('body')).end().get(0).click(); 17 a.remove(); 18 }); 19 $('[data-color]').on('click',function(){ 20 var data=$("#canvas").jSignature("getData"); 21 $("#canvas canvas").remove(); 22 ctx=$("#canvas").jSignature({width:w,height:h,color:$(this).data('color')}).find('canvas').get(0).getContext('2d'); 23 $("#canvas").jSignature("importData",data); 24 }); 25}); 26</script> 27<div id="canvas" style="background-color:#ffffee;"></div> 28<button data-color="black"></button> 29<button data-color="red"></button> 30<button data-color="blue"></button> 31<button data-color="green"></button> 32<button id="clear">クリア</button> 33<button id="download-image">画像ダウンロード</button>

投稿2022/07/01 11:29

yambejp

総合スコア114585

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teru-

2022/07/01 14:01

ご回答いただきありがとうございます! 内容確認させていただきました。 いただいたサンプルコードを例にしますと、 ペン描画できるcanvas上にclickイベントで要素を追加する処理が加わり、 ペン描画とクリックイベントをそれぞれ個別に起動させるといった処理がしたいです。 質問の意図がわかりにくく申し訳ないです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問