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

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

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

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

JavaScript

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

Q&A

解決済

1回答

2597閲覧

canvasでのタッチイベント、clientXでエラーが出ます

kihokutarou

総合スコア59

canvas

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

JavaScript

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

0グッド

1クリップ

投稿2018/03/07 14:59

https://teratail.com/questions/116264#
にて質問した同一内容になります。上記は本件が解決したら解決済みとしようと思っています。
回答者様より様々アドバイスを受けまして、mauseイベントで成功しているものにtouchイベントを追加しようと苦戦しています。
以下のscriptの「//ここからタッチ試行」よりが質問の中心で、上記質問時はタッチによる座標がNaN状態でした。
今回書き換えを行い、コンソールにtouchmove状態の座標もすべて獲得できていることを確認しました。しかしその後consoleでエラーが出ます。
Uncaught TypeError: cannot read property'clientX' of undefined 206行目
clientXというプロパティは読めませんよ、と大雑把にはそういう意味なんでしょうが、そもそもclientXはscreenX、screenY、pageX、pageYと同列のプロパティーなのでは?と混乱中です。

煮詰まっているものがもう少しで解決しそうなので、どなたかご指摘などいただけないでしょうか。

javascript

1var layer1 = document.getElementById("layer1"); 2var layer1Ctx = layer1.getContext("2d"); 3 4var layer2 = document.getElementById("layer2"); 5var layer2Ctx = layer2.getContext("2d"); 6 7var display = document.getElementById("upload_canvas"); 8var displayCtx = display.getContext("2d"); 9 10var base = document.getElementById("base"); 11var baseCtx = base.getContext("2d"); 12 13var render = document.getElementById("render"); 14var renderCtx = render.getContext("2d"); 15 16var layers = [layer1, layer2]; 17 18var is_down = false; 19var trimming_begin_pos = { x: null, y: null }; 20var trimming_end_pos = { x: null, y: null }; 21 22var loaded_file = null; 23var max_canvas_size = { width: 900, height: 600 }; 24 25$(function() { 26 // load image file 27 $("#upload_file").change(function() { 28 29 var file = this.files[0]; 30 if (!file.type.match(/^image/(png|jpeg|gif)$/)) return; 31 32 var image = new Image(); 33 var reader = new FileReader(); 34 35 reader.onload = function(e) { 36 image.onload = function() { 37 38 var min_width = Math.min(this.width, max_canvas_size.width); 39 var min_height = Math.min(this.height, max_canvas_size.height); 40 var scale = Math.min(min_width / this.width, min_height / this.height); 41 var size = {width: this.width * scale, height: this.height * scale}; 42 43 resizeCanvas(size.width, size.height); 44 layer1Ctx.drawImage(image, 0, 0, size.width, size.height); 45 updateCanvas(); 46 47 $("#upload_button").attr('filename', file.name); 48 $("#upload_button").show(); 49 50 // load file on base buffer 51 base.width = this.width/3; 52 base.height = this.height/3; 53 baseCtx.drawImage(image, 0, 0,this.width/3,this.height/3); 54 } 55 image.src = e.target.result; 56 } 57 58 reader.readAsDataURL(file); 59 }); 60 61 // upload image 62 $("#upload_button").click(function(){ 63 64 // get blob data from canvas 65 var canvas = $('#render'); 66 var type = 'image/jpeg'; 67 var dataurl = canvas[0].toDataURL(type); 68 var bin = atob(dataurl.split(',')[1]); 69 var buffer = new Uint8Array(bin.length); 70 for (var i = 0; i < bin.length; i++) { 71 buffer[i] = bin.charCodeAt(i); 72 } 73 var blob = new Blob([buffer.buffer], {type: type}); 74 75 // upload 76 var fd = new FormData(); 77 fd.append('filename', $(this).attr('filename')); 78 fd.append('data', blob); 79 $.ajax({ 80 type: 'POST', 81 url: 'http://yoursite', 82 data: fd, 83 processData: false, 84 contentType: false 85 }).done(function(data) { 86 // done 87 }); 88 }); 89 90 // canvas controll 91 function resizeCanvas(width, height) { 92 for(var i = 0; i < layers.length; i++) { 93 layers[i].width = width; 94 layers[i].height = height; 95 } 96 display.width = width; 97 display.height = height; 98 } 99 100 function updateCanvas() { 101 displayCtx.drawImage(layer1, 0, 0, display.width, display.height); 102 displayCtx.drawImage(layer2, 0, 0, display.width, display.height); 103 } 104 105 106 display.addEventListener("mousemove", function(e){}, false); 107 display.addEventListener("mouseout", function(e){}, false); 108 $('#upload_canvas').on("mousemove", function(e) { 109 if (is_down) { 110 var mouse_pos = getMousePos(e); 111 updateSelectArea(mouse_pos); 112 } 113 }); 114 $('#upload_canvas').on("mouseout", function(e) { 115 }); 116 $('#upload_canvas').on("mousedown", function(e) { 117 if (is_down == true) return; 118 is_down = true; 119 trimming_begin_pos = getMousePos(e); 120 }); 121 $('#upload_canvas').on("mouseup", function(e) { 122 is_down = false; 123 trimming_end_pos = getMousePos(e); 124 125 var begin_pos = trimming_begin_pos; 126 var end_pos = trimming_end_pos; 127 128 if (begin_pos.x == end_pos.x && begin_pos.y == end_pos.y) return; 129 var begin = {x: 0, y:0}; 130 var end = {x:0, y:0}; 131 begin.x = Math.min(begin_pos.x, end_pos.x); 132 begin.y = Math.min(begin_pos.y, end_pos.y); 133 end.x = Math.max(begin_pos.x, end_pos.x); 134 end.y = Math.max(begin_pos.y, end_pos.y); 135 136 highlightTrimmingArea(begin, end); 137 clip(begin, end); 138 console.log(trimming_begin_pos.x); 139 console.log(trimming_begin_pos.y); 140 }); 141 142 function getMousePos(e) { 143 var rect = display.getBoundingClientRect(); 144 return { 145 x: e.clientX - rect.left, 146 y: e.clientY - rect.top}; 147 } 148 149 function updateSelectArea(mouse_pos) { 150 clear(layer2); 151 drawRect(layer2, 152 trimming_begin_pos.x, 153 trimming_begin_pos.y, 154 mouse_pos.x - trimming_begin_pos.x, 155 mouse_pos.y - trimming_begin_pos.y, 156 3, 'red'); 157 updateCanvas(); 158 console.log(trimming_begin_pos.x); 159 console.log(trimming_begin_pos.y); 160 } 161 162 //ここからタッチ試行 163 var touch; 164 display.addEventListener("touchstart", function(e){}, false); 165 display.addEventListener("touchmove", function(e){}, false); 166 display.addEventListener("touchend", function(e){}, false); 167 display.addEventListener("touchcancel", function(e){}, false); 168 169 170 171 $('#upload_canvas').on("touchmove", function(e) { 172 if (this.touch) { 173 var touch_pos = getTouchPos(e); 174 updateSelectArea2(touch_pos); 175 } 176 }); 177 $('#upload_canvas').on("touchend", function(e) { 178 }); 179 $('#upload_canvas').on("touchstart", function(e) { 180 if (this.touch == true) return; 181 this.touch = true; 182 trimming_begin_pos = getTouchPos(e); 183 }); 184 $('#upload_canvas').on("touchend", function(e) { 185 this.touch = false; 186 trimming_end_pos = getTouchPos(e); 187 188 var begin_pos = trimming_begin_pos; 189 var end_pos = trimming_end_pos; 190 191 if (begin_pos.x == end_pos.x && begin_pos.y == end_pos.y) return; 192 var begin = {x: 0, y:0}; 193 var end = {x:0, y:0}; 194 begin.x = Math.min(begin_pos.x, end_pos.x); 195 begin.y = Math.min(begin_pos.y, end_pos.y); 196 end.x = Math.max(begin_pos.x, end_pos.x); 197 end.y = Math.max(begin_pos.y, end_pos.y); 198 199 highlightTrimmingArea(begin, end); 200 clip(begin, end); 201 }); 202 203 function getTouchPos(e) { 204 var rect = display.getBoundingClientRect(); 205 return { 206 x : event.touches[0].clientX - rect.left, 207 y : event.touches[0].clientY - rect.top}; 208} 209 210 211 function updateSelectArea2(touch_pos) { 212 clear(layer2); 213 event.preventDefault() ; 214 drawRect(layer2, 215 trimming_begin_pos.x, 216 trimming_begin_pos.y, 217 touch_pos.x - trimming_begin_pos.x, 218 touch_pos.y - trimming_begin_pos.y, 219 3, 'red'); 220 updateCanvas(); 221 console.log(trimming_begin_pos.x); 222 console.log(trimming_begin_pos.y); 223 } 224 225 226 //ここまでタッチ試行 227 228 229 230 231 function highlightTrimmingArea(begin, end) { 232 clear(layer2); 233 var fill = "rgba(0, 0, 0, 0.5)"; 234 fillRect(layer2, 0, 0, layer2.width, begin.y, fill); // top 235 fillRect(layer2, 0, begin.y, begin.x, end.y - begin.y, fill); // left 236 fillRect(layer2, end.x, begin.y, layer2.width - begin.x, end.y - begin.y, fill); // right 237 fillRect(layer2, 0, end.y, layer2.width, layer2.height - end.y, fill); // bottom 238 updateCanvas(); 239 } 240 241 function clear(canvas) { 242 var ctx = canvas.getContext("2d"); 243 ctx.clearRect(0, 0, canvas.width, canvas.height); 244 } 245 246 function drawRect(canvas, x, y, width, height, line, style) { 247 var ctx = canvas.getContext("2d"); 248 ctx.beginPath(); 249 ctx.rect(x, y, width, height); 250 ctx.lineWidth = line; 251 ctx.strokeStyle = style; 252 ctx.stroke(); 253 } 254 255 function fillRect(canvas, x, y, width, height, style) { 256 var ctx = canvas.getContext("2d"); 257 ctx.beginPath(); 258 ctx.rect(x, y, width, height); 259 ctx.fillStyle = style; 260 ctx.fill(); 261 } 262 263 function clip(begin, end) { 264 var scale = base.width / display.width; // display scale 265 var x = begin.x * scale; 266 var y = begin.y * scale; 267 var width = (end.x - begin.x) * scale; 268 var height = (end.y - begin.y) * scale; 269 270 render.width = width; 271 render.height = height; 272 renderCtx.drawImage( base, x, y, width, height, 0, 0, width, height); 273 } 274 275})

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

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

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

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

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

guest

回答1

0

ベストアンサー

この問題を解決するには、getTouchPosを以下のように修正します。

javascript

1function getTouchPos(e) { 2 var rect = display.getBoundingClientRect(); 3 var touch = e.touches[0] || e.changedTouches[0]; 4 return { 5 x : touch.clientX - rect.left, 6 y : touch.clientY - rect.top 7 }; 8}

Uncaught TypeError: cannot read property 'clientX' of undefined 206行目

clientXというプロパティは読めませんよ、と大雑把にはそういう意味なんでしょうが、そもそもclientXはscreenX、screenY、pageX、pageYと同列のプロパティーなのでは?と混乱中です。

undefined の 'clientX' は読めません」という意味ですので、問題は「clientX がない」ではなく「touches[0] がない(から touches[0].clientX もない)」です。screenX, screenY, pageX, pageY も同列のプロパティですから、それらを使ったとしても、同じエラーになったでしょう。

コードをみると touchstart, touchmove, touchend で getTouchPos を呼んでいますが、touchend ではうまくいきません。
event.touchesはそのイベントがおきたときに画面に触れている指の数だけ中身が入っています。

  • touchstart は指が画面に触れたときのイベントなので、touches[0] はあります。
  • touchmove は画面に触れた指が移動したときのイベントなので、touches[0] はあります。
  • touchend は指が画面から離れたときのイベントで、それが画面に触れていた最後の指だった場合、touches は空になります。そのため、getTouchPosでエラーになります。

そこで、touchesの代わりにchangedTouchesを使います。event.changedTouchesには、そのイベントを起こしたtouchが入っています。

https://codepen.io/hoo-chan/full/bvbRWp/
こちら↑に、touchイベントを見るためのアプリを作りました。指を離したときのtouchesとchangedTouchesの違いを確認してください。

投稿2018/03/07 22:12

編集2018/03/07 22:49
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

kihokutarou

2018/03/08 05:26

ご回答ありがとうございます。 var touch = e.touches[0] || e.changedTouches[0]; を var touch = event.touches[0] || event.changedTouches[0]; と修正しましたら期待通りの動きができました。 ご説明してくださったこと、もやもやが晴れてすっきりしました。質問でのエラー排出時のタイミングが、まさにtouchendのタイミング、言われてみればその判定は指が離れた直後であって離れた時(直前)の座標はとれていませんよね。本当に勉強になりました。 ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問