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

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

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

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

1109閲覧

canvasでの戻る機能が実装できません

kihokutarou

総合スコア59

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2018/01/25 08:11

タブレットでの使用目的で、「自由に字や絵が描ける」「画像を呼び出してそれに絵画できる」「あらかじめおいておいたカードなどを動かせる(本件とは無関係)」などのプログラムをHTMLで書いています。

上記やりたいことはできるようになったのですが、絵画をした際、「戻る」機能の実装ができません。おそらくはその時点でのcanvasの情報を5個程度保存し、それを「戻る」プッシュで呼び出す、という感じだと思うのですが、素人なりに様々試しましたがまったくうまくいきません。どなたかご教示ください。
5回~10回程度の過去情報に「戻る」ができればよいと思います。

同階層に
imgフォルダ(動かすカードなどを格納)
a.jpg 初期状態のcanvasの背景画像
jquery.min.js
jquery.ui.touch-punch.js
jquery-ui-1.8.12.custom.min.js
move.js(動かす用のjs)
wboard.css
index.html(下記)
wboard.js(下記)
と入れております。

html

1<!DOCTYPE html> 2<html lang="ja"> 3<head> 4 <meta charset="UTF-8"> 5 <title>汎用お絵かき試作品</title> 6 <link rel="stylesheet" type="text/css" href="wboard.css"> 7</head> 8<body> 9 10<div class="wrapper"> 11<form> 12<p> 13&nbsp;&nbsp;&nbsp; 14<input type="button" class="b1" style="background: #000000;" value=" " onclick="change_black()"> 15<input type="button" class="b1" style="background: #ff0000;" value=" " onclick="change_red()"> 16<input type="button" class="b1" style="background: #00ff00;" value=" " onclick="change_green()"> 17<input type="button" class="b1" style="background: #0000ff;" value=" " onclick="change_blue()"> 18<input type="button" class="b1" style="background: #ffffff;" value=" " onclick="change_white()"> 19&nbsp;&nbsp;線幅&nbsp; 20<input type="button" class="b2" value="8px" onclick="change_8()"> 21<input type="button" class="b2" value="16px" onclick="change_16()"> 22&nbsp;&nbsp; 23<input type="button" class="b2" value="全面" onclick="spread()"> 24<input type="button" class="b2" value="消去" onclick="clr()"> 25<font size="5" color="#ff0000"><a href="javascript:location.reload();">最初から</a></font> 26&nbsp;&nbsp;画像&nbsp; 27<input id="ufile" name="ufile" type="file" accept="image/jpeg,image/png"> 28<input type="button" value="戻る" id="backBtn"> 29<input type="button" value="進む" id="forwardBtn"> 30</form> 31 32 33<canvas id="drawArea"></canvas> 34 35 36 37<p><font size="6" color="#ff0000"> 38<a href="#" onClick="toggle_view1();return false;">カードの表示</a> 39<a href="#" onClick="toggle_hidden1();return false;">カードの非表示</a> 40</font> 41</p> 42<div id="card" style="display: none;"> 43<table><tr> 44<td><p id="move1"><img src="img/1.JPG" width="150" height="120"></p></td> 45 46<td><p id="move2"><img src="img/2.JPG" width="150" height="120"></p></td> 47 48<td><p id="move3"><img src="img/3.JPG" width="150" height="120"></p></td> 49 50<td><p id="move4"><img src="img/4.JPG" width="150" height="120"></p></td> 51 52<td><p id="move5"><img src="img/5.JPG" width="150" height="120"></p></td> 53 54<td><p id="move6"><img src="img/6.JPG" width="150" height="120"></p></td> 55 56<td><p id="move7"><img src="img/7.JPG" width="150" height="120"></p></td> 57 58<td><p id="move8"><img src="img/8.png" width="80" height="80"></p></td> 59 60</tr></table> 61</div> 62 63 64 65<script src="wboard.js"></script> 66<script src="jquery.min.js"></script> 67<script type="text/javascript" src="jquery-ui-1.8.12.custom.min.js"></script> 68<script type="text/javascript" src="jquery.ui.touch-punch.js"></script> 69<script type="text/javascript" src="move.js"></script> 70 71 <script> 72 $(function(){ 73 74 // id="ufile"の変化でコールバック 75 $("#ufile").change(function(){ 76 // 選択ファイルの有無をチェック 77 if (!this.files.length) { 78 alert('ファイルが選択されていません'); 79 return; 80 } 81 82 // Formからファイルを取得 83 var file = this.files[0]; 84 85 // (1) HTMLのCanvas要素の取得 86 var canvas = $("#drawArea"); 87 88 // (2) getContext()メソッドで描画機能を有効にする 89 var ctx = canvas[0].getContext('2d'); 90 91 // 描画イメージインスタンス化 92 var image = new Image(); 93 94 // File API FileReader Objectでローカルファイルにアクセス 95 var fr = new FileReader(); 96 97 // ファイル読み込み読み込み完了後に実行 [非同期処理] 98 fr.onload = function(evt) { 99 100 // 画像がロードされた後にcanvasに描画を行う [非同期処理] 101 image.onload = function() { 102 // (3) プレビュー(Cnavas)のサイズを指定 103 var cnvsH = ctx.canvas.height; 104 var cnvsW = image.naturalWidth*cnvsH/image.naturalHeight; 105 // (4) Cnavasにサイズアトリビュートを設定する 106 canvas.attr('width', cnvsW); 107 canvas.attr('height', cnvsH); 108 // (5) 描画 109 ctx.drawImage(image, 0, 0, cnvsW, cnvsH); 110 } 111 // 読み込んだ画像をimageのソースに設定 112 image.src = evt.target.result; 113 } 114 115 // fileを読み込む データはBase64エンコードされる 116 fr.readAsDataURL(file); 117 }) 118 }) 119</script> 120<script language="JavaScript" type="text/javascript"> 121<!-- 122var elem1 = document.getElementById("card"); 123function toggle_view1() { 124 elem1.style.display = ""; 125} 126function toggle_hidden1() { 127 elem1.style.display = "none"; 128} 129--> 130</script> 131</body> 132</html> 133

js

1onload = function() { 2 draw(); 3}; 4function draw() { 5 var canvas = document.getElementById("drawArea"); 6 if ( ! canvas || ! canvas.getContext ) { return false; } 7 var ctx = canvas.getContext("2d"); 8 /* Imageオブジェクトを生成 */ 9 var img = new Image(); 10 img.src = "a.jpg?" + new Date().getTime(); 11 /* 画像が読み込まれるのを待ってから処理を続行 */ 12 img.onload = function() { 13 ctx.drawImage(img, 0, 0,ctx.canvas.width, ctx.canvas.height); 14 } 15} 16 17var canvas = document.getElementById("drawArea"); 18var ctx = canvas.getContext("2d"); 19var width = window.innerWidth; 20var height = window.innerHeight; 21var x = 0, y = 0; 22var color = "#000000"; 23var line = 13; 24canvas.width = width; 25canvas.height = height - 70; 26 27canvas.addEventListener("mousedown", touchStartHandler, false); 28canvas.addEventListener("mouseup", touchEndHandler, false); 29 30canvas.addEventListener("touchstart", ttouchStartHandler, false); 31canvas.addEventListener("touchend", ttouchEndHandler, false); 32 33function touchStartHandler(e) { 34 e.preventDefault(); 35 getTouchPoint(e); 36 ctx.beginPath(); 37 ctx.lineCap = "round"; 38 ctx.lineJoin = "round"; 39 ctx.moveTo(x, y); 40 canvas.addEventListener("mousemove", touchMoveHandler, false); 41} 42 43function touchMoveHandler(e) { 44 e.preventDefault(); 45 getTouchPoint(e); 46 ctx.lineWidth = line; //線の太さ 47 ctx.strokeStyle = color; //線の色 48 ctx.lineTo(x, y); 49 ctx.stroke(); 50} 51 52function touchEndHandler(e) { 53 e.preventDefault(); 54 ctx.closePath(); 55 canvas.removeEventListener("mousemove", touchMoveHandler, false); 56} 57 58function getTouchPoint(e) { 59 x = e.clientX - canvas.offsetLeft; 60 y = e.clientY - canvas.offsetTop; 61} 62 63function ttouchStartHandler(e) { 64 e.preventDefault(); 65 getTTouchPoint(e); 66 ctx.beginPath(); 67 ctx.lineCap = "round"; 68 ctx.lineJoin = "round"; 69 ctx.moveTo(x, y); 70 canvas.addEventListener("touchmove", ttouchMoveHandler, false); 71} 72 73function ttouchMoveHandler(e) { 74 e.preventDefault(); 75 getTTouchPoint(e); 76 ctx.lineWidth = line; //線の太さ 77 ctx.strokeStyle = color; //線の色 78 ctx.lineTo(x, y); 79 ctx.stroke(); 80} 81 82function ttouchEndHandler(e) { 83 e.preventDefault(); 84 ctx.closePath(); 85 canvas.removeEventListener("touchmove", ttouchMoveHandler, false); 86} 87 88function getTTouchPoint(e) { 89 var touch = e.touches[0]; 90 x = touch.pageX - canvas.offsetLeft; 91 y = touch.pageY - canvas.offsetTop; 92} 93 94function spread() { 95 ctx.fillStyle = color; 96 ctx.fillRect(0, 0, canvas.width, canvas.height); 97 document.getElementById("ClickSound").play(); 98} 99 100function clr() { 101 cntx = canvas.getContext("2d"); 102 cntx.clearRect(0, 0, canvas.width, canvas.height); 103 document.getElementById("ClickSound").play(); 104} 105 106function change_black() { 107 color = "#000000"; 108 document.getElementById("ClickSound").play(); 109} 110function change_red() { 111 color = "#ff0000"; 112 document.getElementById("ClickSound").play(); 113} 114function change_green() { 115 color = "#00ff00"; 116 document.getElementById("ClickSound").play(); 117} 118function change_blue() { 119 color = "#0000ff"; 120 document.getElementById("ClickSound").play(); 121} 122function change_white() { 123 color = "#ffffff"; 124 document.getElementById("ClickSound").play(); 125} 126function change_8() { 127 line = 8; 128 document.getElementById("ClickSound").play(); 129} 130function change_16() { 131 line = 16; 132 document.getElementById("ClickSound").play(); 133} 134

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

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

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

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

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

guest

回答1

0

ベストアンサー

最も単純な方法としては, canvas要素の描画状態をgetImageDataメソッドを用いてImageDataオブジェクト化する方法でしょう. このImageDataオブジェクトを保管しておきたい履歴数分, 配列にとっておき, putImageDataメソッドを使ってcanvas要素に書き戻すのです. 後は一般的なundo/redoの実装に帰着します.

簡単なサンプルのみを提示しますので, どのように組み込むかはご自分でお考え下さい.

JavaScript

1const canvas = document.querySelector("canvas"); 2const ctx = canvas.getContext("2d"); 3 4//履歴を保管する配列 5const history = []; 6//履歴の上限数 7const limit = 5; 8 9//履歴取得 10history.push(ctx.getImageData(0, 0, canvas.width, canvas.height)); 11if(history.length > limit){ 12 history.shift(); 13} 14 15//履歴書き戻し 16const id = history.pop(); 17if(id){ 18 ctx.putImageData(id, 0, 0); 19}

NOTE:
描画状態のオブジェクト化にはImageDataオブジェクトの他にも次のものが利用できます

  • CanvasPatternオブジェクト
  • HTMLCanvasElementオブジェクト(canvas要素)
  • toDataURL(dataURIスキーム形式)/toBlobメソッド

投稿2018/01/25 08:33

編集2018/01/25 08:40
defghi1977

総合スコア4756

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

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

kihokutarou

2018/01/25 09:06

ご回答ありがとうございます。ところが本当に申し訳ないことにずぶの素人でいきなり行き詰まってしまいました。  ご提示の記述を、私のwboard.jsに記載したいのですが、おそらく「履歴取得」をfunction ttouchEndHandler(e) に記載するのだろうと予想します。タッチが離れた瞬間に履歴をとるのですよね? 書き出しも例えばfunction undoとなり名前を付けて紐づけする感じでしょうか? 後だしで本当に申し訳ないのですが、VBAくらいしかわからず基本もまったくありません。非常に申し訳ない。。。
defghi1977

2018/01/25 09:26

> タッチが離れた瞬間に履歴をとる それだとシステムに対する負荷が高すぎます. 1履歴とる毎に多量のメモリ確保・開放が発生するので, 最悪ブラウザがクラッシュします. それより例えば一時保存ボタンを用意して, それがクリック・タップされたタイミングで履歴をとるといったふうにしたほうが良かろうと思います.
kihokutarou

2018/01/25 14:45

なるほど・・・・。 あきらめきれないのが、 https://www.kabanoki.net/1823 様のサイトで紹介されている方法を見ると、さほど負荷があるようにも見えずに・・・。ただこちらはchrome(タブレット)に反応せず流用できませんでした。 先ほどから様々試してみるのですがうまくいきません・・・。
defghi1977

2018/01/25 18:32

負荷の有無はシステムのメモリ容量と画像のサイズ及び画像データの取得頻度で決定します. が, ブラウザによってはこのメモリ負荷に極端に弱いもの(例えばChrome)があります. ※もちろんこれは個人的な用途であれば許容できるリスクですが, 不特定多数が利用するWEBページでは無視できない問題です. >  ご提示の記述を、私のwboard.jsに記載したいのですが、おそらく「履歴取得」をfunction ttouchEndHandler(e) に記載するのだろうと予想します。タッチが離れた瞬間に履歴をとるのですよね? 書き出しも例えばfunction undoとなり名前を付けて紐づけする感じでしょうか? 多分そうなるであろうと(ただ, マルチタッチ絡みの制御が気にはなります).しかし別の質問でありましたとおり, Chromeではローカル環境でのtoDataURL/toBlob/getImageDataは許可されていませんので, エラーとなる場合があります.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問