一般情況
一般情況下,如果需要在 canvas 中獲取鼠標指針坐標,可以通過監聽鼠標的 mousemove
(如果只需單擊時的坐標,可以用 click
)事件。 當事件被觸發時,我們可以獲取鼠標相對於 viewport 的坐標(event.clientX
, event.clientY
)。 同時,我們可以通過 canvas.getBoundingClientRect() 來獲取 canvas 相對於 viewport 的坐標,這樣我們就可以計算出鼠標在 canvas 中的坐標。
canvas.addEventListener("click", function __handler__(evt) { var x = evt.clientX; var y = evt.clientY; var rect = canvas.getBoundingClientRect(); x -= rect.left; y -= rect.top; console.log(x, y); // (x, y) 就是鼠標在 canvas 單擊時的坐標 });
** DEMO-1 **
設置了 border/padding
一般情況下,我們根據上面的方法獲取出來的坐標是准確的,但當我們在 canvas 上添加了 border 或 padding 后,坐標就出現了偏移。
** DEMO-2-0 **
這是因為在 canvas 中,坐標區域是 canvas 元素的 content 區域,不包括 border 和 padding,而通過上面得到的坐標原點在 canvas 的 border 開始的。因此,這里還需要減去 border 和 padding。
var style = window.getComputedStyle(canvas, null); var borderLeft = parseFloat(style["border-left-width"]); var borderTop = parseFloat(style["border-top-width"]); var paddingLeft = parseFloat(style["padding-left"]); var paddingTop = parseFloat(style["padding-top"]); canvas.addEventListener("click", function __handler__(evt) { var x = evt.clientX; var y = evt.clientY; var rect = canvas.getBoundingClientRect(); x -= rect.left - borderLeft - paddingLeft; // 去除 borderLeft paddingLeft 后的坐標 y -= rect.top - borderTop - paddingTop; // 去除 borderLeft paddingLeft 后的坐標 console.log(x, y); // (x, y) 就是鼠標在 canvas 單擊時的坐標 });
** DEMO-2-1 **
設置了 css width/height
當在 canvas 上設置了 css 的 width、height,並且與 canvas 的 width、height 屬性不同時(可以非常簡單對 canvas 進行放大或縮小,在移動頁面上常常會使用)。從上面計算出來的坐標在 canvas 里使用又會出現偏移。
** DEMO-3-0 **
這里就需要對坐標進行修正:
var style = window.getComputedStyle(canvas, null); var cssWidth = parseFloat(style["width"]); var cssHeight = parseFloat(style["height"]); var scaleX = canvas.width / cssWidth; // 水平方向的縮放因子 var scaleY = canvas.height / cssHeight; // 垂直方向的縮放因子 canvas.addEventListener("click", function __handler__(evt) { var x = evt.clientX; var y = evt.clientY; var rect = canvas.getBoundingClientRect(); x -= rect.left; y -= rect.top; x *= scaleX; // 修正水平方向的坐標 y *= scaleY; // 修正垂直方向的坐標 console.log(x, y); // (x, y) canvas 里的坐標 });
** DEMO-3-1 **
設置了 transform
如果我們在 canvas 的 style 上添加了 transform,又有可能會導致上面計算出來的坐標出現偏移。
** DEMO-4-0 **
而且經過 transform 后很難通過已經的 API 來計算出准確的坐標?w3c 為了解決這個問題,在 CSSOM-View 中添加了一個名為 GeometryUtils 的接口,該接口提供了一系列的 api 幫助我們對頁面上的點、矩形、四邊形等的坐標進行轉換(目前只有 Firefox 支持)。 這里我們使用其中的 convertPointFromNode
方法,直接把在 viewport 的坐標 (evt.clientX, evt.clientY)
轉換成相對於 canvas 元素的坐標。 如果 canvas 同時設置了樣式 width、height、box-sizing
,我們可以使用 getBoxQuads
方法來獲取 canvas 經過 transform 之前的元素的 width
和 height
(雖然可以使用通過獲取 style 的相關屬性來計算,但這種方式太麻煩了)來計算出經過 css 縮放的因子。
var quads = canvas.getBoxQuads({ box: "content", relativeTo: canvas }); var bounds = quads[0]; var scaleX = canvas.width / bounds.width; var scaleY = canvas.height / bounds.height; canvas.addEventListener("click", function __handler__(evt) { var {x, y} = canvas.convertPointFromNode({ x: evt.clientX, y: evt.clientY }, document, { toBox: "content" }); x *= scaleX; y *= scaleY; console.log(x, y); });
** DEMO-4-1 **
在文章的最后,貼上另一種方法的解決方案:
** DEMO-4-2 **