失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 原生js实现Canvas实现拖拽式绘图 支持画笔 线条 箭头 三角形和圆形等等图形绘制功

原生js实现Canvas实现拖拽式绘图 支持画笔 线条 箭头 三角形和圆形等等图形绘制功

时间:2023-08-10 03:05:20

相关推荐

原生js实现Canvas实现拖拽式绘图 支持画笔 线条 箭头 三角形和圆形等等图形绘制功

前言

需要用到图形绘制,没有找到完整的图形绘制实现,所以自己实现了一个 - -

演示地址:查看演示DEMO

新版本支持IE5+(你没看错,就是某软的IE浏览器)以上任意浏览器的Canvas绘图:/eguid_1/article/details/79310269

项目维护于github:/eguid/graffitiCanvas

一、实现的功能

1、基于oop思想构建,支持坐标点、线条(由坐标点组成,包含方向)、多边形(由多个坐标点组成)、圆形(包含圆心坐标点和半径)等实体

2、原生JavaScript实现,不依赖任何第三方js库和插件

3、多图形绘制(支持画笔、线条、箭头、三角形、矩形、平行四边形、梯形以及多边形和圆形绘制)

4、拖拽式绘制(鼠标移动过程中不断进行canvas重绘)

5、图片绘制(作为背景图片时重绘会发生闪烁现象,暂时有点问题,后面继续完善)

5、清空绘制功能

6、新版本优化绘制性能(使用共享坐标变量数组,减少了大量的对象创建操作)

7、新版本支持箭头绘制功能

二、完整实现代码

DrawingTools =(function(){//公共方法var getDom=function(id){return document.getElementById(id)};var isNull=function(s){return s==undefined||typeof(s)=='undefined'||s==null||s=='null'||s==''||s.length<1};var hideDefRM=function(){document.oncontextmenu=function(){return false}};//屏蔽浏览器默认鼠标事件/**绘图容器*/var cbtCanvas;/**绘图对象*/var cxt;/**绘制的图形列表*/var shapes=new Array();var graphkind={'cursor':0,'pen':1,'line':2,'trian':3,'rect':4,'poly':5,'circle':6,'arrow':21,'parallel':41,'trapezoid':42};//背景图片绘制配置var bgPictureConfig={pic:null,//背景图片地址或路径repaint:true,//是否作为永久背景图,每次清除时会进行重绘};//加载并绘制图片(src:图片路径或地址),默认重绘背景图var loadPicture=function(src){if(isNull(bgPictureConfig.repaint)||bgPictureConfig.repaint){bgPictureConfig.pic=src}var img = new Image();img.onload = function(){cxt.drawImage(img,0,0)}img.src =src;}//绘图基础配置var paintConfig={lineWidth:1,//线条宽度,默认1strokeStyle:'red',//画笔颜色,默认红色fillStyle:'red',//填充色lineJoin:"round",//线条交角样式,默认圆角lineCap:"round",//线条结束样式,默认圆角};//重新载入绘制样式var resetStyle=function(){cxt.strokeStyle=paintConfig.strokeStyle;cxt.lineWidth=paintConfig.lineWidth;cxt.lineJoin=paintConfig.lineJoin;cxt.lineCap=paintConfig.lineCap;cxt.fillStyle=paintConfig.fillStyle;}//鼠标图形var cursors=['crosshair','pointer'];/** 切换鼠标样式*/var switchCorser=function(b){cbtCanvas.style.cursor=((isNull(b)?isDrawing():b)?cursors[0]:cursors[1]);}//操作控制变量组var ctrlConfig={kind:0,//当前绘画分类isPainting:false,//是否开始绘制startPoint:null,//起始点cuGraph:null,//当前绘制的图像cuPoint:null,//当前临时坐标点,确定一个坐标点后重新构建cuAngle:null,//当前箭头角度vertex:[],//坐标点}/**获取当前坐标点*/var getCuPoint=function(i){return ctrlConfig.cuPoint[i];}/**设置当前坐标点*/var setCuPoint=function(p,i){return ctrlConfig.cuPoint[i]=p;}/**设置当前临时坐标点值*/var setCuPointXY=function(x,y,i){if(isNull(ctrlConfig.cuPoint)){var arr=new Array();arr[i]=new Point(x,y);ctrlConfig.cuPoint=arr;}else if(isNull(ctrlConfig.cuPoint[i])){setCuPoint(new Point(x,y),i);}else{ctrlConfig.cuPoint[i].setXY(x,y);}return getCuPoint(i);}/**是否正在绘制*/var isDrawing=function (){return ctrlConfig.isPainting;}/**开始绘制状态*/var beginDrawing=function(){ctrlConfig.isPainting=true;}/**结束绘制状态*/var stopDrawing=function(){ctrlConfig.isPainting=false;}/**是否有开始坐标点*/var hasStartPoint=function(){return !isNull(ctrlConfig.startPoint);}/**设置当前绘制的图形*/var setCuGraph=function(g){ctrlConfig.cuGraph=g;}/**获取当前绘制的图形*/var getCuGraph=function(){return ctrlConfig.cuGraph;}/**设置开始坐标点(线条的起始点,三角形的顶点,圆形的圆心,四边形的左上角或右下角,多边形的起始点)*/var setStartPoint=function(p){ctrlConfig.startPoint=p;}/**获取开始坐标点*/var getStartPoint=function(){return ctrlConfig.startPoint;}/**清空全部*/var clearAll=function(){cxt.clearRect(0,0,cbtCanvas.width,cbtCanvas.height);}/**重绘*/var repaint=function(){clearAll();/*if(bgPictureConfig.repaint){loadPicture(bgPictureConfig.pic);}*/}/**点(坐标,绘图的基本要素,包含x,y坐标)*/var Point=(function(x1,y1){var x=x1,y=y1;return{set:function(p){x=p.x,y=p.y;},setXY:function(x2,y2){x=x2;y=y2;},setX:function(x3){x=x3;},setY:function(y3){y=y3;},getX:function(){return x;},getY:function(){return y;}}});/**多角形(三角形、矩形、多边形),由多个点组成*/var Poly=(function(ps1){var ps=isNull(ps1)?new Array():ps1;var size=ps.length;return{set:function(ps2){ps=ps2;},getSize:function(){return size;},setPoint:function(p,i){if(isNull(p)&&isNaN(i)){return;}ps[i]=p;},setStart:function(p1){if(isNull(ps)){ps=new Array();return ps.push(p1);}else{ps[0]=p1;}},add:function(p){if(isNull(ps)){ps=new Array();}return ps.push(p);},pop:function(){if(isNull(ps)){return;}return ps.pop();},shift:function(){if(isNull(ps)){return;}return ps.shift;},get:function(){if(isNull(ps)){return null;}return ps;},draw:function(){cxt.beginPath();for(i in ps){if(i==0){cxt.moveTo(ps[i].getX(),ps[i].getY());}else{cxt.lineTo(ps[i].getX(),ps[i].getY());}}cxt.closePath();cxt.stroke();}}});/*线条(由两个点组成,包含方向)*/var Line=(function(p1,p2,al){var start=p1,end=p2,angle=al;var drawLine=function(){cxt.beginPath();cxt.moveTo(p1.getX(),p1.getY());cxt.lineTo(p2.getX(),p2.getY());cxt.stroke();}//画箭头var drawArrow=function() {var vertex =ctrlConfig.vertex;var x1=p1.getX(),y1=p1.getY(),x2=p2.getX(),y2=p2.getY();var el=50,al=15; //计算箭头底边两个点(开始点,结束点,两边角度,箭头角度)vertex[0] = x1,vertex[1] = y1, vertex[6] = x2,vertex[7] = y2;//计算起点坐标与X轴之间的夹角角度值var angle = Math.atan2(y2 - y1, x2 - x1) / Math.PI * 180;var x = x2 - x1,y = y2 - y1,length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));if (length < 250) {el/=2,al/2;}else if(length<500){el*=length/500,al*=length/500;}vertex[8] = x2 - el * Math.cos(Math.PI / 180 * (angle + al));vertex[9] = y2- el * Math.sin(Math.PI / 180 * (angle + al));vertex[4] = x2- el* Math.cos(Math.PI / 180 * (angle - al));vertex[5] = y2 - el * Math.sin(Math.PI / 180 * (angle - al));//获取另外两个顶点坐标x=(vertex[4]+vertex[8])/2,y=(vertex[5]+vertex[9])/2;vertex[2] = (vertex[4] + x) / 2;vertex[3] = (vertex[5] + y) / 2;vertex[10] = (vertex[8] +x) / 2;vertex[11] = (vertex[9] +y) / 2;//计算完成,开始绘制cxt.beginPath();cxt.moveTo(vertex[0], vertex[1]);cxt.lineTo(vertex[2], vertex[3]);cxt.lineTo(vertex[4], vertex[5]);cxt.lineTo(vertex[6], vertex[7]);cxt.lineTo(vertex[8], vertex[9]);cxt.lineTo(vertex[10], vertex[11]);cxt.closePath();cxt.fill();cxt.stroke();}return{setStart:function(s){start=s;},setEnd:function(e){end=e;},getStart:function(){return start;},getEnd:function(){return end;},draw:function(){if(angle){drawArrow();}else{drawLine();}}}});/**圆形(包含圆心点和半径)*/var Circle=(function(arr){//包含起始点(圆心)和结束点,以及圆半径var startPoint=arr.start,endPoint=arr.end,radius=arr.radius;/*绘制圆*/var drawCircle=function(){cxt.beginPath();var x=startPoint.getX();var y=startPoint.getY();if(isNull(radius)){radius=calculateRadius(startPoint,endPoint);}//x,y,半径,开始点,结束点,顺时针/逆时针cxt.arc(x,y,radius,0,Math.PI*2,false); // 绘制圆cxt.stroke();}//计算圆半径var calculateRadius=function(p1,p2){var width=p2.getX()-p1.getX();var height=p2.getY()-p1.getY();//如果是负数if(width<0||height<0){width=Math.abs(width);}//计算两点距离=平方根(width^2+height^2)c=Math.sqrt(Math.pow(width,2)+Math.pow(height,2));return c;}return{set:function(params){startPoint=params.start;endPoint=params.end;radius=params.radius;},setPoint:function(p1){p=p1;},getPoint:function(){return p;},setRadius:function(r1){radius=r1;},getRadius:function(){return radius;},calcRadius:calculateRadius,//绘制draw:drawCircle,}});/**绘制线条工具方法*/var drawLine=function(p){cxt.beginPath();cxt.moveTo(startPosition.getX(),startPosition.getY());cxt.lineTo(p.getX(),p.getY());cxt.stroke();}/**绘制三角形工具方法*/var drawTrian=function(ps){cxt.beginPath();var a=ps.get();cxt.moveTo(a[0].getX(),a[0].getY());cxt.lineTo(a[1].getX(),a[1].getY());cxt.lineTo(a[2].getX(),a[2].getY());cxt.closePath();cxt.stroke();}/**绘制矩形工具方法*/var drawRect=function(p2){var p=getStartPoint();var width=p.getX()-p2.getX();var height=p.getY()-p2.getY();cxt.beginPath();cxt.strokeRect(x,y,width,height);//绘制矩形}/*绘制多边形工具方法*/var drawpolygon=function(ps){if(ps.length>1){//保证只有两个坐标点才是矩形cxt.beginPath();var p=ctrlConfig.startPoint;var x=p.getX();var y=p.getY();cxt.moveTo(x,y);for(p1 in ps){cxt.lineTo(p1.getX(),p1.getY());}cxt.stroke();}}/*绘制圆角矩形工具方法*/var drawRoundedRect=function(x,y,width,height,radius){cxt.beginPath();cxt.moveTo(x,y+radius);cxt.lineTo(x,y+height-radius);cxt.quadraticCurveTo(x,y+height,x+radius,y+height);cxt.lineTo(x+width-radius,y+height);cxt.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);cxt.lineTo(x+width,y+radius);cxt.quadraticCurveTo(x+width,y,x+width-radius,y);cxt.lineTo(x+radius,y);cxt.quadraticCurveTo(x,y,x,y+radius);cxt.stroke();}/*绘制圆工具方法*/var drawCircle=function(c){var p=c.getPoint();//坐标点var x=p.getX();var y=p.getY();var r=c.getRadius();cxt.beginPath();//x,y,半径,开始点,结束点,顺时针/逆时针cxt.arc(x,y,r,0,Math.PI*2,false); // 绘制圆cxt.stroke();}//计算圆半径工具方法var calculateRadius=function(p1,p2){var width=p2.getX()-p1.getX();var height=p2.getY()-p1.getY();//如果是负数if(width<0||height<0){width=Math.abs(width);}//计算两点距离=平方根(width^2+height^2)c=Math.sqrt(Math.pow(width,2)+Math.pow(height,2));return c;}//鼠标按键点击(首次点击确定开始坐标点,拖动鼠标不断进行图形重绘)var mouseDown = function(e){var btnNum = e.button;if(btnNum==0){console.log("选择:"+ctrlConfig.kind);//设置起始点switch(ctrlConfig.kind){case graphkind.pen://画笔(不松开鼠标按键一直画)beginDrawing();//开始绘制cxt.beginPath();cxt.moveTo(e.offsetX,e.offsetY);break;case graphkind.poly://多边形var p=new Point(e.offsetX,e.offsetY);if(isDrawing()){getCuGraph().add(p);//添加到}else{//第一次确定开始坐标beginDrawing();//开始绘制setStartPoint(p);var poly=new Poly();poly.add(p);setCuGraph(poly);//设置当前绘制图形}break;case graphkind.line://线条case graphkind.arrow://方向case graphkind.trian://三角形case graphkind.rect://矩形case graphkind.parallel://平行四边形case graphkind.trapezoid://梯形beginDrawing();//开始绘制var p=new Point(e.offsetX,e.offsetY);setStartPoint(p);var poly=new Poly();poly.add(p);setCuGraph(poly);//设置当前绘制图形break;case graphkind.circle://圆console.log("确定图形绘制开始坐标点:"+e.offsetX+","+e.offsetY);//点击确定图形的开始坐标点beginDrawing();//开始绘制var p=new Point(e.offsetX,e.offsetY);setStartPoint(p);var circle= new Circle({'start':p});setCuGraph(circle);break;case ctrlConfig.cursor: //手型鼠标default://默认是手型鼠标,不允许绘制}}else if(btnNum==2){console.log("右键由于结束多边形绘制");if(isDrawing()){if(ctrlConfig.kind==graphkind.poly){repaint();getCuGraph().draw();stopDrawing();//结束绘制}}}hideDefRM();//屏蔽浏览器默认事件}//鼠标移动(拖动,根据鼠标移动的位置不断重绘图形)var mouseMove = function(e){if(isDrawing()&&hasStartPoint()){//检查是否开始绘制,检查是否有开始坐标点//画笔不需要重绘if(ctrlConfig.kind>1){repaint();//重绘}var p=setCuPointXY(e.offsetX,e.offsetY,0);//设置共享的临时坐标点,用于防止重复创建对象switch(ctrlConfig.kind){case graphkind.pen://画笔(一直画)cxt.lineTo(e.offsetX,e.offsetY);cxt.stroke();break;case graphkind.poly://多边形var poly=getCuGraph(poly);var size=poly.getSize();poly.setPoint(p,(size-1));poly.draw();break;case graphkind.line://线条var line=new Line(getStartPoint(),p,false);ctrlConfig.cuGraph=line;line.draw();break;case graphkind.arrow://方向var line=new Line(getStartPoint(),p,true);ctrlConfig.cuGraph=line;line.draw();break;case graphkind.trian://三角形var lu=getStartPoint();var x2=p.getX();var x1=lu.getX();//三角形左边的点坐标计算方法:(x1-(x2-x1),y2)var x3=x1-(x2-x1);var l=setCuPointXY(x3,p.getY(),1);//设置共享的临时坐标点,用于防止重复创建对象var poly=getCuGraph();//获取当前图形poly.set([lu,p,l]);poly.draw();//即时绘制break;case graphkind.parallel://平行四边形var lu=getStartPoint();var x3=p.getX();var x1=lu.getX();//平行四边形两个未知坐标点计算方法:(x1-(x3-x1),y3),(x1+(x3-x1),y1)var x2=x3+(x3-x1);var x4=x1-(x3-x1);var ld=setCuPointXY(x2,lu.getY(),1);//设置共享的临时坐标点,用于防止重复创建对象var ru=setCuPointXY(x4,p.getY(),2);//设置共享的临时坐标点,用于防止重复创建对象var poly=getCuGraph();//获取当前图形poly.set([lu,ru,p,ld]);poly.draw();//即时绘制break;case graphkind.trapezoid://梯形var lu=getStartPoint();var x3=p.getX();var x1=lu.getX();//梯形两个未知坐标点计算方法:(x3-(x3-x1)/2,y1),(x1-(x3-x1)/2,y3)var x2=x3-(x3-x1)/2;var x4=x1-(x3-x1)/2;var ld=setCuPointXY(x2,lu.getY(),1);var ru=setCuPointXY(x4,p.getY(),2);var poly=getCuGraph();poly.set([lu,ru,p,ld]);poly.draw();break;case graphkind.rect://矩形var lu=getStartPoint();//矩形右上角和左上角坐标计算方法var ld=setCuPointXY(lu.getX(),p.getY(),1);var ru=setCuPointXY(p.getX(),lu.getY(),2);var poly=getCuGraph();poly.set([lu,ru,p,ld]);poly.draw();break;case graphkind.circle://圆var circle=getCuGraph();//获取当前图形circle.set({'start':getStartPoint(),'end':p});circle.draw();//即时绘制break;}}}//鼠标按键松开var mouseUp = function(e){if(isDrawing()){//console.log("松开鼠标按键:"+e.offsetX+","+e.offsetY);//画笔不需要重绘if(ctrlConfig.kind>1){repaint();getCuGraph().draw();}if(ctrlConfig.kind!=graphkind.poly){//多边形绘制鼠标按键松开不结束绘制,多边形只有右键点击才能结束绘制stopDrawing();//结束绘制}}} //鼠标移出var mouseOut = function(e){console.log("鼠标移出绘制区域"+e.offsetX+","+e.offsetY);if(isDrawing()){console.log("停止绘制");if(ctrlConfig.kind>1){repaint();getCuGraph().draw();}stopDrawing();//停止绘制}}return{isNull:isNull,getDom:getDom,clear:function(){stopDrawing();//停止绘制repaint();},/**初始化*/init:function(params){cbtCanvas=getDom(params.id);//浏览器是否支持Canvasif (cbtCanvas.getContext){/**绘图对象*/cxt=cbtCanvas.getContext("2d");cbtCanvas.onmousedown = mouseDown; cbtCanvas.onmouseup = mouseUp; cbtCanvas.onmousemove = mouseMove; cbtCanvas.onmouseout = mouseOut;resetStyle();//载入样式return true;}else{return false;}},/**设置背景图片*/setBgPic:loadPicture,/**选择图形类型*/begin:function(k){console.log("选择绘制图形:"+k);if(isNaN(k)){//如果不是数字,先转换为对应字符ctrlConfig.kind=kind[k];}else{ctrlConfig.kind=k;}switchCorser(true);//切换鼠标样式},/*手型,并停止绘图*/hand:function(){ctrlConfig.kind=0;stopDrawing();//停止绘制switchCorser(false);//切换鼠标样式}}})

三、使用方式

1、图形类型

0:鼠标,1:画笔,2:线条,3:三角形,4:矩形,5:多边形,6:圆形,21:箭头,41:平行四边形,42:梯形

var graphkind={'cursor':0,'pen':1,'line':2,'trian':3,'rect':4,'poly':5,'circle':6,'arrow':21,'parallel':41,'trapezoid':42};

2、初始化以及使用背景图片和画笔选择

var drawUtil=new DrawingTools();//初始化,(如果浏览器不支持H5,会初始化失败,返回false)if(drawUtil.init({'id':'calibrationCanvas'})){//加载图片var imgsrc='图片地址';if(!drawUtil.isNull(imgsrc)){drawUtil.setBgPic(imgsrc,true);//设置背景图片(异步加载图片)}}drawUtil.begin(1);//选择画笔

2、绘制箭头

drawUtil.begin(21);

四、演示demo

点击这里跳转demo

原生js实现Canvas实现拖拽式绘图 支持画笔 线条 箭头 三角形和圆形等等图形绘制功能 有实例Demo

如果觉得《原生js实现Canvas实现拖拽式绘图 支持画笔 线条 箭头 三角形和圆形等等图形绘制功》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。