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

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

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

解決済

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

teru-
teru-

総合スコア11

2回答

0評価

0クリップ

114閲覧

投稿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

<body> <div class="container"> <main> <canvas id="draw_area" width="500px" height="500px" style="border: 1px solid #000000;"> </canvas> </main> <div class="playerElem" id="player" data-elemid="0" style="top: 220px; left: 267px;">たいとる</div> </div> <!-- ペンの設定を変更する要素 --> <div id="change_pen"> <img src="img/pen.png" alt=""> <div class="select_color"> <ul> <li data-color="black"> <p>ペンの色:黒</p> </li> <li data-color="red"> <p>ペンの色:赤</p> </li> <li data-color="blue"> <p>ペンの色:青</p> </li> <li data-color="white"> <p>消しゴム</p> </li> </ul> </div> </div> <script src="js/main.js" type="module"></script> <!-- テスト用スクリプト --> <script> </script> </body>

js:main.js

"use strict"; import { DrawPen } from './DrawPen.js'; import { Element } from './Element.js'; const draw = new DrawPen(); draw.setCanvasSize(); // draw.drawPen(); // draw.changePenColor(); const elem = new Element(); // elem.moveElement(1); // elem.moveElement(2); // switch_btn押下で要素追加とペン描画を切り替える const switch_btn = document.querySelector("#change_pen > img"); let y = 0; switch_btn.addEventListener("click", () => { y++; if (y % 2 === 0) { console.log(true); elem.moveElement(); } else if (y % 2 !== 0) { console.log(false); draw.drawPen(); draw.changePenColor(); } });

js:DrawPen.js

"use strict"; export class DrawPen { canvas = document.getElementById("draw_area"); context = this.canvas.getContext("2d"); // this減らしてもっと簡潔に書けないだろうか? drawPen() { this.canvas.addEventListener("mousemove", event => draw(event.layerX, event.layerY)); this.canvas.addEventListener("touchmove", event => draw(event.layerX, event.layerY)); //パソコンでクリックしてる間だけ描けるようにした機能 this.canvas.addEventListener("mousedown", () => { this.context.beginPath(); isDrag = true; }); this.canvas.addEventListener("mouseup", () => { this.context.closePath(); isDrag = false; }); //スマホで描けるようにする機能 this.canvas.addEventListener("touchstart", () => { this.context.beginPath(); isDrag = true; }); this.canvas.addEventListener("touchend", () => { this.context.closePath(); isDrag = false; }); let isDrag = false; //線をかく機能 const draw = (x, y) => { if (!isDrag) return; this.context.lineTo(x, y); this.context.stroke(); } } // ペンの色を変えるメソッド changePenColor() { const selectList = document.querySelectorAll(".select_color > ul > li"); const colorList = ["black", "red", "blue", "white"]; for (let i = 0, len = selectList.length; i < len; i++) { selectList[i].addEventListener("click", () => { const color = selectList[i].getAttribute("data-color"); // 下記はもっと簡潔に書く方法はないか…? if (color === "black") { this.context.globalCompositeOperation = "source-over"; this.context.strokeStyle = "black"; this.context.lineWidth =5; } else if (color === "red") { this.context.globalCompositeOperation = "source-over"; this.context.strokeStyle = "red"; this.context.lineWidth =5; } else if (color === "blue") { this.context.globalCompositeOperation = "source-over"; this.context.strokeStyle = "blue"; this.context.lineWidth =5; } else { this.context.globalCompositeOperation = 'destination-out'; this.context.lineWidth =50; } }) } } // canvasのサイズをmain要素に合わせる処理 setCanvasSize() { const main = document.querySelector("main"); const mainWidth = main.clientWidth; const mainHeight = main.clientHeight; console.log(mainWidth); console.log(mainHeight); this.canvas.setAttribute("width", mainWidth); this.canvas.setAttribute("height", mainHeight); } }

js:Element.js

"use strict"; export class Element { moveElement() { const elements = document.getElementsByClassName("playerElem"); const container = document.querySelector(".container"); // 要素がクリックされた座標を取得する変数 let x; let y; const listener = (event) => { console.log("イベント起動") switch (event.type) { case 'mousedown': console.log("mousedown起動"); this.lastMousedownElement = event.target.id; let elem = event.target; elem.classList.add("drag"); if (event.type !== "mousedown") { var event = event.changedTouches[0]; } x = event.pageX - elem.offsetLeft; y = event.pageY - elem.offsetTop; document.body.addEventListener("mousemove", mmove, false); break; case 'mouseup': console.log("mouseup起動"); this.lastMouseupElement = event.target.id; let mouseup_elem = event.target; mouseup_elem.classList.remove("drag"); break; case 'click': console.log("click起動"); console.log(this.lastMousedownElement); console.log(this.lastMouseupElement); if (this.lastMousedownElement === this.lastMouseupElement) return; console.log("alert:要素を追加しました"); alert("要素を追加しました"); // 追加する要素を定義 const playerElem = document.createElement("div"); playerElem.className = "playerElem"; const text = document.createTextNode("たいとる"); playerElem.setAttribute("id", "player"); playerElem.appendChild(text); container.appendChild(playerElem); // クリックした座標位置に要素を配置 const drag = document.querySelectorAll(".playerElem"); const elemId = drag.length - 1; drag[elemId].setAttribute("data-elemId", elemId); // 下記の20はマジックナンバーなので、他に算出方法あるかも drag[elemId].style.top = event.pageY - 20 + "px"; drag[elemId].style.left = event.pageX - 20 + "px"; container.removeEventListener("click", listener); break; } for (let dragElm of document.getElementsByClassName("playerElem")) { dragElm.addEventListener('mousedown', listener, false); document.addEventListener('mouseup', listener, false); } } function mmove(event) { const drag = document.getElementsByClassName("drag")[0]; if (event.type !== "mousemove") { let event = event.changedTouches[0]; } event.preventDefault(); if (drag) { drag.style.top = event.pageY - y + "px"; drag.style.left = event.pageX - x + "px"; } } container.addEventListener('click', listener, false); container.addEventListener('mouseup', listener, false); } }

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

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

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

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がトリガーになってしまい、 要素の追加が意図せず発生してしまっています。 ここのペン描画と要素操作の処理をフラグ等?を用意してペン描画したいときはペン描画だけ。 要素を追加および動かしたい場合は要素の操作だけといった処理にしたいです。

まだ回答がついていません

会員登録して回答してみよう

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

ただいまの回答率
87.20%

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

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

質問する

関連した質問

同じタグがついた質問を見る