栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > Web开发 > JavaScript

原生JS使用Canvas实现拖拽式绘图功能

JavaScript 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

原生JS使用Canvas实现拖拽式绘图功能

一、实现的功能

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,//线条宽度,默认1
 strokeStyle:'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();
 
      }
      
      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);
   //浏览器是否支持Canvas
   if (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);

总结

以上所述是小编给大家介绍的原生JS使用Canvas实现拖拽式绘图功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对考高分网网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/75322.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号