Canvas绘制不规则图形,实现可拖动,编辑

Stella981
• 阅读 1417

目前的工作在做在线的标注工具,接触canvas一年了,各种绘制,基本上图像的交互canvas都可以完成,也写了几篇关于canvas的文章,遇到的问题也写博客上了,对于canvas有问题的朋友可以去看看。一直想写一个关于canvas系列的东西,也没时间。正好最近再捣鼓canvas,有时间就写一点,一个功能一个功能的写,争取写一个系列。

以前都是绘制矩形,今天写一个新鲜的,绘制多边形可拖动编辑的多边形。见下图(截取自工程的一部分):

Canvas绘制不规则图形,实现可拖动,编辑

(太大的GIF图传不上来,只能截取一小部分,找个时间把完整的功能录一个视频放在网盘上)。

鼠标点击绘制点,并且自动绘制曲线,(当曲线闭合的时候,弹出名称边界框--不重要),绘制完毕后,点击点可以改变形状,点击曲线内部可以拖动位置。

这篇文章就先写绘制不规则图像与拖动点吧,移动图像值得另起写一篇文章。

正文

写了这么多的canvas代码,我的经验就是一定要去写,一边写边看效果,做的过程中会有新的想法。对于canvas各种绘制的效果,还是得去动手去修改才能出效果。

写之前整理下思路:

1.鼠标点击绘制点-->绘制圆的方法,鼠标点为中心点。

2.每点击一次,绘制线段。--Lineto方法。

3.判断是否闭合。

4.判断是否点击了绘制过的点上--拖动改变形状。

5.重绘。

画布上每一次的更改都是要重新绘制画布的,所以要把绘制的点保存起来。这个思路和画矩形的思路大体相近,可以看下我的绘制矩形的文章

 1 //线段的点的集合
 2 var points=[];
 3 //可拖动圆圈的点的集合
 4 var circles=[];
 5 //每一个点的对象
 6 function Point(x, y) {
 7     this.x = x;
 8     this.y = y;
 9   }
10 //圆圈对象
11 function Circle(x, y) {
12      this.x = x;
13      this.y = y;
14      this.radius = 10;
15      this.color = "blue";
16      //拖拽点的标记
17      this.isSelected = false;
18   }
19 /*每一次的点都看作一个对象,然后把点放在数组里保存起来
20 这样circles和points就会是这种形式
21 points=[{(x0,y0},{x1,y1},{x2,y2}..]
22 circles=[{x0,y0,10,blue,false}...]*/

上边的points和circles数组其实xy坐标是一样的,为啥创建两个呢?

如果项目小,就实现一个拖拽的图像就行了,那可以随便设置数组个数,随着标注工具增加,各种重绘,还是各干各的比较好,不要高耦合。

绘制:

canvas.onmousedown=function(e){
     var clickX = e.pageX - canvas.offsetLeft;
     var clickY = e.pageY - canvas.offsetTop;
     //判断当前点击点是否在已经绘制的圆圈上,如果是执行相关操作,并return,不进入画线的代码
     for(var i=1; i<circles.length; i++) {
        var circle = circles[i];
        //使用勾股定理计算这个点与圆心之间的距离
        var distanceFromCenter = Math.sqrt(Math.pow(circle.x - clickX, 2)
            + Math.pow(circle.y - clickY, 2));

        // 如果是其他的点,则设置可以拖动
        if (distanceFromCenter <= circle.radius) {
          // 清除之前选择的圆圈
          index=i;
          isDragging=true;
          //停止搜索
          return;
        }
      }
    //如果点击新的位置,则进入下面的代码,绘制点
    context.clearRect(0,0,canvas.width,canvas.height);
    //遍历数组画圆
     var circle=new Circle(clickX,clickY);
     circles.push(circle);
     circles[0].color="green";
     for(var i=0; i<circles.length; i++) {
        var circle = circles[i];
        // 绘制圆圈
        context.globalAlpha = 0.85;
        context.beginPath();
        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI*2);
        context.fillStyle = circle.color;
        context.strokeStyle = "black";
        context.fill();
        context.stroke();
      }
      // 画线
     var point=new Point(clickX,clickY);
     points.push(point);
     context.beginPath();
     context.lineWidth = 4;
     //从起始点开始绘制
     context.moveTo(points[0].x,points[0].y);
     for (var i = 0; i < points.length; i++) {
       context.lineTo(points[i].x, points[i].y);
     }
     context.fillStyle="rgb(2,100,30)";
     context.fill();
     context.strokeStyle="#9d4dca";
     context.stroke();
   };

    每一次点击都要判断一下,如果点在曾经绘制的点上,那么就设置变量isDragging=true可以拖拽,return。如果没有,说明创建的是新的点,那么开始绘制并且加入数组。(代码部分的声明就不写了,直接出关键的思路代码,具体的还要自己动手去写)

拖拽点移动:

canvas.onmousemove=function(e){
   // 判断圆圈是否开始拖拽
   if (isDragging == true) {
     // 判断拖拽对象是否存在
       // 取得鼠标位置
       var x1 = e.pageX - canvas.offsetLeft;
       var y1 = e.pageY - canvas.offsetTop;
       context.clearRect(0,0,canvas.width,canvas.height);
       //根据上文得到的index设置index点位置随鼠标改变
       circles[index].x=x1;
       circles[index].y=y1;
       points[index].x=x1;
       points[index].y=y1;
       for(var i=0; i<circles.length; i++) {
          var circle = circles[i];
          // 绘制圆圈
          context.globalAlpha = 0.85;
          context.beginPath();
          context.arc(circle.x, circle.y, circle.radius, 0, Math.PI*2);
          context.fillStyle = circle.color;
          context.strokeStyle = "black";
          context.fill();
          context.stroke();
        }
       context.beginPath();
       context.moveTo(points[0].x,points[0].y);
       for (var i = 0; i < points.length; i++) {
         context.lineTo(points[i].x, points[i].y);
       }
       context.lineTo(points[0].x,points[0].y);
       // context.fillStyle="#831f68";
       context.fillStyle="rgb(2,100,30)";
       context.fill();
       context.strokeStyle="#9d4dca";
       context.stroke();
     }
   };

   canvas.onmouseup=function(){
     isDragging=false;
   };

   canvas.onmouseout=function(){
     isDragging=false;
   };

其实实现拖拽并不难, 有一个地方就是如何判断鼠标点在了已经绘制的点上。循环所有点,只要鼠标点到圆心的距离小于半径,那么一定点在了这个点上。

这样一个简单的拖拽就实现了,后面的文章我还会继续增加新的功能,慢慢丰富代码。欢迎关注。

Canvas绘制不规则图形,实现可拖动,编辑

更新:一个V1.0工程的视频,百度网盘:链接: https://pan.baidu.com/s/1qFF3yF4T9oE9quiUx7hfoA 密码: md6g

1.0版本,后续版本慢慢更新,包括canvas的绘制和Vue的一些东东。可绘制、拖拽、编辑、下载坐标信息,对于一些数据集的标注工作可以辅助标记。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Python进阶者 Python进阶者
2年前
手把手教你使用CanvasAPI打造一款拼图游戏
一、canvas简介1.canvas是HTML5提供的一种新标签,双标签;2.HTML5 canvas标签元素用于图形的绘制,通过脚本(通常是JavaScript)来完成;3.canvas标签只是图形容器,必须使用脚本来绘制图形;Canvas是一个矩形区域的画布,可以用JavaScript在上面绘画;二、案例目标我们今天的目标是使用HTML5
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
View绘制系列(10)
Canvas基础变换前面学习了Canvas相关的一些绘制方法,不知道大家发现没?我们都是根据左上角(0,0)点算出来新的坐标,然后再绘制,这样明显不符合我们平常基于坐标原点绘制的习惯,那么我们能不能直接在原点绘制图形,然后通过操作Canvas来实现移动到目标位置呢?答案是肯定的,这就需要用到Canvas变换方法了。Canvas变
Wesley13 Wesley13
3年前
HTML5实现绘制几何图形
HTML5新增了一个<canvas.../属性。该元素自身并不绘制图形,只是相当于一张空画布。如果开发者需要向<canvas.../上绘制图形则必须使用JavaScript脚本进行绘制。为了向<canvas.../元素上绘图,必须经过如下3步。获取<canvas.../元素对应的DOM对象,这是一个Canvas对象。调用Canvas对象
Stella981 Stella981
3年前
ES6与canvas实现鼠标小球跟随效果
        最近闲来无聊,看了下ES6的语法,结合canvas实现了动画特效——随着鼠标的移动,会有小球跟随且自动消失的动画。首先,html部分,目前就一个canvas标签。1<canvasid"canvas"2当前浏览器不支持!3</canvas其次,css部分,没有考
Wesley13 Wesley13
3年前
Unity性能优化整理
纹理优化SpritePacker通过细分导入到Unity的纹理,利用自带的SpritePacker将时间段内需要同时绘制在屏幕上的纹理合并到一个图集中,可以减少Drawcall的开销UGUI优化Canvas1._分离Canvas_画布(Canvas)是U
Wesley13 Wesley13
3年前
12、开源游戏
在前面中我们初始化了游戏的资源,这次我们来说下地图的绘制和游戏主循环设计。地图绘制    以前说过地图是用tiled画好,导出为图片形式的,所以地图的绘制,就是把这个图片绘制到canvas的过程。这样绘制地图就简单了,使用drawImage方法绘制即可。    这里有个2问题,1是地图的大小一般肯定是大于canvas的,所以我们只是把地图