canvas绘制基础图形图像

趁着清明放假的空闲,将之前写过的代码整理了一下,发现了一个比较有意思的项目,该项目其实也比较简单,就是利用Canvas的各种原生API在图像中绘制一些基础图形,以及一些图形的更改操作。顺便借此项目复习一下Canvas基础。

目前实现功能
基本实现功能
  1. 图片的放大缩小和拖拽
  2. 绘制多边形并修改
  3. 绘制矩形并修改
  4. 绘制线段(暂无修改)
  5. 绘制箭头(暂无修改)
图片的放大和缩小
  • drawImage() 图片的绘制
// 绘制图片
    drawImage = () => {
        if (this.$imageDom) {
            try {
                this._context.drawImage(
                    this.$imageDom,  // 图片元素
                    0, // 开始剪切的 x 坐标位置
                    0, // 开始剪切的 y 坐标位置
                    this.imageOriginWidth,  //被剪切图像的宽度
                    this.imageOriginHeight, //被剪切图像的宽度
                    this.offsetX, // 在画布上放置图像的 x 坐标位置
                    this.offsetY, //在画布上放置图像的 y 坐标位置
                    this.imageOriginWidth * this.currentRatio, //要使用的图像的宽度
                    this.imageOriginHeight * this.currentRatio //要使用的图像的高度
                );

                return Promise.resolve();

            } catch (e) {
                console.log(e)
            }
        }
    }
  • 计算画布上放置图像的坐标位置
按照上图方式去计算要放置的图像的点坐标
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()

根据上述原生API绘制线,多边形的绘制即为坐标点大于2的路径的闭合曲线。

判断点是否在多边形内
  • isPointInPath

绘制当前闭合路径,根据该函数判断点是否在路径内。

判断点是否在线上

由于上述方法是判断点是否在路径内,就无法判断点是否在线上了,我采用的方法如下:

  1. 先判断点的坐标是否在线的坐标范围内,如果不在则点不在线上
  2. 如果1满足,则根据直线公式 y = kx + b 通过线段已知两点坐标求出斜率k和偏移值b;
  3. 根据线段的斜率和垂直线的斜率 k * k1 = -1,求出垂直线斜率,再根据当前点计算出通过该点的垂直线公式 y = (-1/k)x + m;
  4. 根据垂直相交线公式求出交点坐标,根据两点(当前点和交点坐标)求出线段距离,如果该距离小于误差范围值,则认为点在线上,反之则认为不在线上。
判断点是否在线上
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); // 计算斜率
        const verSlop = _calVerticalSlop(slop); // 计算垂直斜率

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

        if (slop != 0 && verSlop != 0) {
            if (y2 == slop * x2 + y1 - slop * y1) {
                // 点在当前直线上
                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) {
        // 说明是点在线上
        return true;
    }
    return false;
}
绘制矩形
  • rect()

根据原生API绘制图形,修改时的判断方式同多边形的判断。

实现原理就介绍到这里,更多详细信息请去https://github.com/jdkwky/my-vue-example/tree/master/src/view/canvas中了解~

你可能感兴趣的:(canvas绘制基础图形图像)