鼠標(biāo)在canvas上的位置計(jì)算

最近有這樣的需求,鼠標(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);
});
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烁竭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吉挣,更是在濱河造成了極大的恐慌派撕,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睬魂,死亡現(xiàn)場離奇詭異终吼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)氯哮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門际跪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喉钢,你說我怎么就攤上這事姆打。” “怎么了肠虽?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵幔戏,是天一觀的道長。 經(jīng)常有香客問我税课,道長评抚,這世上最難降的妖魔是什么豹缀? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮慨代,結(jié)果婚禮上邢笙,老公的妹妹穿的比我還像新娘。我一直安慰自己侍匙,他們只是感情好氮惯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著想暗,像睡著了一般妇汗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上说莫,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天杨箭,我揣著相機(jī)與錄音,去河邊找鬼储狭。 笑死互婿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辽狈。 我是一名探鬼主播慈参,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼刮萌!你這毒婦竟也來了驮配?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤着茸,失蹤者是張志新(化名)和其女友劉穎壮锻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涮阔,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猜绣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了澎语。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片途事。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡验懊,死狀恐怖擅羞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情义图,我是刑警寧澤减俏,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站碱工,受9級特大地震影響娃承,放射性物質(zhì)發(fā)生泄漏奏夫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一历筝、第九天 我趴在偏房一處隱蔽的房頂上張望酗昼。 院中可真熱鬧,春花似錦梳猪、人聲如沸麻削。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呛哟。三九已至,卻和暖如春匿沛,著一層夾襖步出監(jiān)牢的瞬間扫责,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工逃呼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鳖孤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓蜘渣,卻偏偏與公主長得像淌铐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子蔫缸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350