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中了解~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 1. 矢量减法 设二维矢量 P = (x1,y1) ,Q = (x2,y2) 则矢量减法定义为: P - Q = ...
    潭潭_180阅读 2,222评论 0 1
  • 基于学生学习共同体培育语文生态课堂文化的研究 近年来,随着现代教育理念的不断深入与...
    火车头123阅读 1,974评论 0 8
  • 在Canvas中,线段也是路径中的一种,被称之为线性路径。在Canvas中绘制线性路径主要用到moveTo(x,y...
    王叮叮当当响阅读 2,854评论 0 2
  • 原文地址:canvas图形编辑器[https://jeff_zhong.gitee.io/blog/2017/11...
    jeffzhong阅读 3,374评论 1 8
  • 回顾:七月任务: 1,组织四次读书会,形式多样。完成 2,组织两次亲子活动。 未完成 3,和家人每月至少10次电话...
    陈雪云2021阅读 98评论 0 1