美文网首页让前端飞Web前端之路
canvas绘制基础图形图像

canvas绘制基础图形图像

作者: jdkwky | 来源:发表于2020-04-12 17:45 被阅读0次

    趁着清明放假的空闲,将之前写过的代码整理了一下,发现了一个比较有意思的项目,该项目其实也比较简单,就是利用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绘制基础图形图像

        本文链接:https://www.haomeiwen.com/subject/frjbmhtx.html