ふへー、ようやく整理完了。一つ一つ動作確認をしながらの整理は大変だ。自動テストがあればなぁ。でもテストケースを用意するのも大変だけど。
ふへー、ようやく整理完了。一つ一つ動作確認をしながらの整理は大変だ。自動テストがあればなぁ。でもテストケースを用意するのも大変だけど。
マウスの要素内座標が取れるようになったので、簡単な例を一つ。
function getElementAbsPos(elem) { var x = 0; var y = 0; while(elem){ x += elem.offsetLeft; y += elem.offsetTop; elem = elem.offsetParent; } return {x:x, y:y}; } function getMousePosOnElement(elem, ev) { if(!ev){ ev = event;}//for IE if(elem.getBoundingClientRect){ var bcr = elem.getBoundingClientRect(); var x = ev.clientX - bcr.left; var y = ev.clientY - bcr.top; return {x:x, y:y}; } else if(typeof(ev.pageX) == "number" && typeof(ev.pageY) == "number"){ var pos = getElementAbsPos(elem); return {x:ev.pageX-pos.x, y:ev.pageY-pos.y}; } else{ return {x:0, y:0}; } } var BORDER_WIDTH = 10; var cv = document.createElement("canvas"); cv.setAttribute("width", "320"); cv.setAttribute("height", "240"); cv.style.cssText = "border: "+BORDER_WIDTH+"px solid;"; document.body.appendChild(cv); var lastPoint = null; cv.onmousedown = function(ev) { lastPoint = getMousePosOnElement(cv, ev); lastPoint.x -= BORDER_WIDTH; lastPoint.y -= BORDER_WIDTH; } cv.onmousemove = function(ev) { if(lastPoint){ var currPoint = getMousePosOnElement(cv, ev); currPoint.x -= BORDER_WIDTH; currPoint.y -= BORDER_WIDTH; var ctx = cv.getContext("2d"); ctx.beginPath(); ctx.moveTo(lastPoint.x, lastPoint.y); ctx.lineTo(currPoint.x, currPoint.y); ctx.stroke(); lastPoint = currPoint; } } cv.onmouseup = function(ev) { lastPoint = null; } cv.onmouseout = function(ev) { lastPoint = null; }
例によってインタラクティブコンソールへ貼り付けて実行可能。
getMousePosOnElementより上は昨日書いたとおり。
borderを太くするとその分座標がずれてしまうので、そのあたりは適切に補正してやる必要がありました。
マウスイベントオブジェクトから得られる座標をある要素上の座標系へ変換したいのだが、これがなかなか簡単にはいかない。
色々調べてみるとgetBoundingClientRectというものがIEやFirefox、Operaでは使えるようなので、それを使うことにした。WebKitはつい最近実装したばかりのようだ。なので、getBoundingClientRectが使えればそれとclientX,clientYとの差分で求める。使えなければ、要素の絶対座標を適当に推測して、pageX,pageYとの差分で求める(IEはpageX,pageYをサポートしていないがgetBoundingClientRectの方を使うので問題ない)。
function getElementAbsPos(elem) { var x = 0; var y = 0; while(elem){ x += elem.offsetLeft; y += elem.offsetTop; elem = elem.offsetParent; } return {x:x, y:y}; } function getMousePosOnElement(elem, ev) { if(!ev){ ev = event;} if(elem.getBoundingClientRect){ var cr = elem.getBoundingClientRect(); var x = ev.clientX - cr.left; var y = ev.clientY - cr.top; return {x:x, y:y}; } else if(typeof(ev.pageX) == "number" && typeof(ev.pageY) == "number"){ var pos = getElementAbsPos(elem); return {x:ev.pageX-pos.x, y:ev.pageY-pos.y}; } else{ return {x:0, y:0}; } } function onClick(elem, ev) { var pos = getMousePosOnElement(elem, ev); alert(pos.x + "," + pos.y); } var elem = document.getElementById( ???? ); elem.onclick = function(ev) { onClick(elem, ev);}
とりあえず簡単なスクロール込みのhtmlでテストした感じでは大丈夫っぽい。CSSの指定によってはgetElementAbsPosあたりが怪しいけど。
マウスの要素内座標が簡単に求められないなんて、本当にどうかしていると思う。