canvas繪制基礎(chǔ)圖形圖像

趁著清明放假的空閑,將之前寫(xiě)過(guò)的代碼整理了一下,發(fā)現(xiàn)了一個(gè)比較有意思的項(xiàng)目,該項(xiàng)目其實(shí)也比較簡(jiǎn)單,就是利用Canvas的各種原生API在圖像中繪制一些基礎(chǔ)圖形,以及一些圖形的更改操作。順便借此項(xiàng)目復(fù)習(xí)一下Canvas基礎(chǔ)。

目前實(shí)現(xiàn)功能
基本實(shí)現(xiàn)功能
  1. 圖片的放大縮小和拖拽
  2. 繪制多邊形并修改
  3. 繪制矩形并修改
  4. 繪制線段(暫無(wú)修改)
  5. 繪制箭頭(暫無(wú)修改)
圖片的放大和縮小
  • drawImage() 圖片的繪制
// 繪制圖片
    drawImage = () => {
        if (this.$imageDom) {
            try {
                this._context.drawImage(
                    this.$imageDom,  // 圖片元素
                    0, // 開(kāi)始剪切的 x 坐標(biāo)位置
                    0, // 開(kāi)始剪切的 y 坐標(biāo)位置
                    this.imageOriginWidth,  //被剪切圖像的寬度
                    this.imageOriginHeight, //被剪切圖像的寬度
                    this.offsetX, // 在畫(huà)布上放置圖像的 x 坐標(biāo)位置
                    this.offsetY, //在畫(huà)布上放置圖像的 y 坐標(biāo)位置
                    this.imageOriginWidth * this.currentRatio, //要使用的圖像的寬度
                    this.imageOriginHeight * this.currentRatio //要使用的圖像的高度
                );

                return Promise.resolve();

            } catch (e) {
                console.log(e)
            }
        }
    }
  • 計(jì)算畫(huà)布上放置圖像的坐標(biāo)位置
按照上圖方式去計(jì)算要放置的圖像的點(diǎn)坐標(biāo)
getOffset = (pointX, pointY, scale, ratio, dir) => {
        if (pointX && pointY) {
            // 獲取圖片
            const width = this.imageOriginWidth * (scale - ratio * dir);
            const height = this.imageOriginHeight * (scale - ratio * dir);
            const x = this.offsetX;
            const y = this.offsetY;
            if ((pointX < x) && (pointY >= y && pointY <= y + height)
            ) {
                // 1
                this.offsetY = pointY - (pointY - this.offsetY) / (scale - ratio * dir) * scale;

            } else if ((pointX < x) && pointY >= y + height) {
                // 2
                this.offsetX = x;
                this.offsetY = (ratio * dir * this.imageOriginHeight - this.offsetY) * (-1);
            } else if (pointX > x + width && (pointY >= y && pointY <= y + height)) {
                // 5
                this.offsetY = pointY - (pointY - this.offsetY) / (scale - ratio * dir) * scale;
                this.offsetX = (ratio * dir * this.imageOriginWidth - this.offsetX) * (-1);
            } else if ((pointX >= x && pointX <= x + width) && (pointY > y + height)) {
                // 3
                this.offsetX = pointX - (pointX - this.offsetX) / (scale - ratio * dir) * scale;
                this.offsetY = (ratio * dir * this.imageOriginHeight - this.offsetY) * (-1);
            } else if (pointY < y && (pointX >= x && pointX <= x + width)) {
                // 7
                this.offsetX = pointX - (pointX - this.offsetX) / (scale - ratio * dir) * scale;
                this.offsetY = y;
            } else if (pointX > x + width && (pointY > y + height)) {
                // 4
                this.offsetX = (ratio * dir * this.imageOriginWidth - this.offsetX) * (-1);
                this.offsetY = (ratio * dir * this.imageOriginHeight - this.offsetY) * (-1);
            } else if (pointX > x + width && pointY < y) {
                // 6
                this.offsetY = y;
                this.offsetX = (ratio * dir * this.imageOriginWidth - this.offsetX) * (-1);
            } else if (pointX < x && pointY < y) {
                //  8 
                this.offsetX = x;
                this.offsetY = y;
            } else {
                // 9 
                this.offsetX = pointX - (pointX - this.offsetX) / (scale - ratio * dir) * scale;
                this.offsetY = pointY - (pointY - this.offsetY) / (scale - ratio * dir) * scale;

            }
        }
    }

多邊形的繪制(線的繪制、箭頭的繪制)
  • moveTo()
  • lineTo()
  • closePath()

根據(jù)上述原生API繪制線,多邊形的繪制即為坐標(biāo)點(diǎn)大于2的路徑的閉合曲線。

判斷點(diǎn)是否在多邊形內(nèi)
  • isPointInPath

繪制當(dāng)前閉合路徑,根據(jù)該函數(shù)判斷點(diǎn)是否在路徑內(nèi)。

判斷點(diǎn)是否在線上

由于上述方法是判斷點(diǎn)是否在路徑內(nèi),就無(wú)法判斷點(diǎn)是否在線上了,我采用的方法如下:

  1. 先判斷點(diǎn)的坐標(biāo)是否在線的坐標(biāo)范圍內(nèi),如果不在則點(diǎn)不在線上
  2. 如果1滿足,則根據(jù)直線公式 y = kx + b 通過(guò)線段已知兩點(diǎn)坐標(biāo)求出斜率k和偏移值b;
  3. 根據(jù)線段的斜率和垂直線的斜率 k * k1 = -1,求出垂直線斜率,再根據(jù)當(dāng)前點(diǎn)計(jì)算出通過(guò)該點(diǎn)的垂直線公式 y = (-1/k)x + m;
  4. 根據(jù)垂直相交線公式求出交點(diǎn)坐標(biāo),根據(jù)兩點(diǎn)(當(dāng)前點(diǎn)和交點(diǎn)坐標(biāo))求出線段距離,如果該距離小于誤差范圍值,則認(rèn)為點(diǎn)在線上,反之則認(rèn)為不在線上。
判斷點(diǎn)是否在線上
function isPointInLinePath(line, dot, threshold) {
    const x2 = dot[0] ? dot[0] : 0;
    const y2 = dot[1] ? dot[1] : 0;
    const p1 = line[0];
    const p2 = line[1];
    const [p1X, p1Y] = p1;
    const [p2X, p2Y] = p2;
    let l = threshold + 1;
    let x = 0;
    let y = 0;
    if (
        ((p1X <= x2 && x2 <= p2X) || (p2X <= x2 && x2 <= p1X)) &&
        ((p1Y <= y2 && y2 <= p2Y) || (p2Y <= y2 && y2 <= p1Y))
    ) {
        const slop = _calSlop(p1, p2); // 計(jì)算斜率
        const verSlop = _calVerticalSlop(slop); // 計(jì)算垂直斜率

        const x1 = p1[0] || 0;
        const y1 = p1[1] || 0;

        if (slop != 0 && verSlop != 0) {
            if (y2 == slop * x2 + y1 - slop * y1) {
                // 點(diǎn)在當(dāng)前直線上
                x = x2;
                y = y2;
            } else {
                x = parseFloat(
                    (y2 - y1 + slop * x1 - verSlop * x2) / (slop - verSlop)
                );
                y = parseFloat(slop * x + y1 - slop * x1);
            }
        } else {
            // 垂直于x軸或平行于x軸
            if (x1 == p2X) {
                // 平行于y軸
                x = x1;
                y = y2;
            } else if (y1 == p2Y) {
                // 平行于x軸
                x = x2;
                y = y1;
            }
        }

        if (
            (p1X <= x && x <= p2X) ||
            (p2X <= x && x <= p1X && (p1Y <= y && y <= p2Y)) ||
            (p2Y <= y && y <= p1Y)
        ) {
            l = parseInt(Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2 - y, 2)));
        }
    }

    if (l < threshold) {
        // 說(shuō)明是點(diǎn)在線上
        return true;
    }
    return false;
}
繪制矩形
  • rect()

根據(jù)原生API繪制圖形,修改時(shí)的判斷方式同多邊形的判斷。

實(shí)現(xiàn)原理就介紹到這里,更多詳細(xì)信息請(qǐng)去https://github.com/jdkwky/my-vue-example/tree/master/src/view/canvas中了解~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容