最近有這樣的需求,鼠標(biāo)需要拖動(dòng)矩形的頂點(diǎn)變化大小,這就需要檢測鼠標(biāo)在畫布上的位置,接下來給大家介紹幾種情況
一般情況
一般情況下痢法,如果需要在 canvas 中獲取鼠標(biāo)指針坐標(biāo)扯键,可以通過監(jiān)聽鼠標(biāo)的 mousemove(如果只需單擊時(shí)的坐標(biāo)啦撮,可以用 click)事件未荒。 當(dāng)事件被觸發(fā)時(shí),我們可以獲取鼠標(biāo)相對于 viewport 的坐標(biāo)(event.clientX, event.clientY)秸苗。 同時(shí)浩蓉,我們可以通過 canvas.getBoundingClientRect() 來獲取 canvas 相對于 viewport 的坐標(biāo),這樣我們就可以計(jì)算出鼠標(biāo)在 canvas 中的坐標(biāo)蒸走。
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) 就是鼠標(biāo)在 canvas 單擊時(shí)的坐標(biāo)
});
設(shè)置了 border/padding
一般情況下仇奶,我們根據(jù)上面的方法獲取出來的坐標(biāo)是準(zhǔn)確的,但當(dāng)我們在 canvas 上添加了 border 或 padding 后比驻,坐標(biāo)就出現(xiàn)了偏移该溯。
這是因?yàn)樵?canvas 中,坐標(biāo)區(qū)域是 canvas 元素的 content 區(qū)域别惦,不包括 border 和 padding狈茉,而通過上面得到的坐標(biāo)原點(diǎn)在 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 后的坐標(biāo)
y -= rect.top - borderTop - paddingTop; // 去除 borderLeft paddingLeft 后的坐標(biāo)
console.log(x, y); // (x, y) 就是鼠標(biāo)在 canvas 單擊時(shí)的坐標(biāo)
});
設(shè)置了 css width/height
當(dāng)在 canvas 上設(shè)置了 css 的 width蹭秋、height,并且與 canvas 的 width堤撵、height 屬性不同時(shí)(可以非常簡單對 canvas 進(jìn)行放大或縮小仁讨,在移動(dòng)頁面上常常會(huì)使用)。從上面計(jì)算出來的坐標(biāo)在 canvas 里使用又會(huì)出現(xiàn)偏移实昨。
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; // 修正水平方向的坐標(biāo)
y *= scaleY; // 修正垂直方向的坐標(biāo)
console.log(x, y); // (x, y) canvas 里的坐標(biāo)
});
設(shè)置了 transform
如果我們在 canvas 的 style 上添加了 transform洞豁,又有可能會(huì)導(dǎo)致上面計(jì)算出來的坐標(biāo)出現(xiàn)偏移。
而且經(jīng)過 transform 后很難通過已經(jīng)的 API 來計(jì)算出準(zhǔn)確的坐標(biāo)荒给?w3c 為了解決這個(gè)問題丈挟,在 CSSOM-View 中添加了一個(gè)名為 GeometryUtils 的接口,該接口提供了一系列的 api 幫助我們對頁面上的點(diǎn)锐墙、矩形礁哄、四邊形等的坐標(biāo)進(jìn)行轉(zhuǎn)換(目前只有 Firefox 支持)。 這里我們使用其中的 convertPointFromNode 方法溪北,直接把在 viewport 的坐標(biāo) (evt.clientX, evt.clientY) 轉(zhuǎn)換成相對于 canvas 元素的坐標(biāo)桐绒。 如果 canvas 同時(shí)設(shè)置了樣式 width、height之拨、box-sizing茉继,我們可以使用 getBoxQuads 方法來獲取 canvas 經(jīng)過 transform 之前的元素的 width 和 height(雖然可以使用通過獲取 style 的相關(guān)屬性來計(jì)算,但這種方式太麻煩了)來計(jì)算出經(jīng)過 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);
});