原生js實現Canvas實現拖拽式繪圖,支持畫筆、線條、箭頭、三角形和圓形等等圖形繪製功能,有實例Demo

前言

須要用到圖形繪製,沒有找到完整的圖形繪製實現,因此本身實現了一個 - -javascript

演示地址:查看演示DEMOhtml

新版本支持IE5+(你沒看錯,就是某軟的IE瀏覽器)以上任意瀏覽器的Canvas繪圖:http://blog.csdn.net/eguid_1/article/details/79310269
java

1、實現的功能

一、基於oop思想構建,支持座標點、線條(由座標點組成,包含方向)、多邊形(由多個座標點組成)、圓形(包含圓心座標點和半徑)等實體
canvas

二、原生JavaScript實現,不依賴任何第三方js庫和插件數組

三、多圖形繪製(支持畫筆、線條、箭頭、三角形、矩形、平行四邊形、梯形以及多邊形和圓形繪製)瀏覽器

四、拖拽式繪製(鼠標移動過程當中不斷進行canvas重繪)ssh

五、圖片繪製(做爲背景圖片時重繪會發生閃爍現象,暫時有點問題,後面繼續完善)異步

五、清空繪製功能ide

六、新版本優化繪製性能(使用共享座標變量數組,減小了大量的對象建立操做)工具

七、新版本支持箭頭繪製功能

2、完整實現代碼


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();
		/*
		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);
			//瀏覽器是否支持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);//切換鼠標樣式
		}
	}
})




3、使用方式

一、圖形類型

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};

二、初始化以及使用背景圖片和畫筆選擇

var drawUtil=new DrawingTools();
//初始化,(若是瀏覽器不支持H5,會初始化失敗,返回false)
if(drawUtil.init({'id':'calibrationCanvas'})){
	//加載圖片
	var imgsrc='圖片地址';
	if(!drawUtil.isNull(imgsrc)){
		drawUtil.setBgPic(imgsrc,true);//設置背景圖片(異步加載圖片)
	}
}
drawUtil.begin(1);//選擇畫筆

二、繪製箭頭

drawUtil.begin(21);



4、演示demo

點擊這裏跳轉demo

相關文章
相關標籤/搜索