最近在项目中用到canvas绘图,有点心得,在此小记。
原本打算用百度echarts,百度echarts有饼状图,但后来没有用。主要有两方面原因。
1.小菜的技术有限,不能直接拿来用。
2.如果要用echarts那就得熟读echarts的api文档,小菜时间有限。
按照小菜的尿性,先把demo放出来 
备用演示地址http://walidream.com/example/canvas/pieChart/
1.绘实线圆
方法:drawSolidRound(ctx,x,y,round)
参数:ctx x y round
| 参数 | 类型 | 必填 | 说明 | 
|---|
| ctx | Object | 是 | canvas 2d绘图环境 | 
| x | Number | 是 | 圆心x坐标 | 
| y | Number | 是 | 圆心y坐标 | 
| round | Object | 是 | 圆的基本参数 | 
参数:round
| 参数 | 类型 | 必填 | 说明 | 
|---|
| starAngle | Number | 是 | 起始角度 | 
| endAngle | Number | 是 | 结束角度 | 
| anticlockwise | Boolean | 是 | true(顺时针),false(逆时针) | 
| radius | Number | 是 | 圆半径 | 
| border | Array | 是 | [0]:是线条宽度值,[1]:线条颜色值 | 
| bgColor | String | 是 | 背景色 | 
drawSolidRound(ctx,x,y,round){
     ctx.beginPath()
     ctx.lineWidth = round.border[0];   
     ctx.strokeStyle = round.border[1]; 
     ctx.closePath();
     ctx.arc(x, y, round.radius, this.getRads(round.starAngle),this.getRads(round.endAngle),round.anticlockwise); 
     ctx.fillStyle = round.bgColor;
     ctx.fill();
     ctx.stroke();
}
2.绘虚线圆
方法:drawDashRound (ctx, x, y, dashround)
参数:ctx x y dashround
| 参数 | 类型 | 必填 | 说明 | 
|---|
| ctx | Object | 是 | canvas 2d绘图环境 | 
| x | Number | 是 | 圆心x坐标 | 
| y | Number | 是 | 圆心y坐标 | 
| dashround | Object | 是 | 基本参数 | 
参数:dashround
| 参数 | 类型 | 必填 | 说明 | 
|---|
| step | Number | 是 | 虚线间距(值越大,间距越大) | 
| starAngle | Number | 是 | 起始角度 | 
| endAngle | Number | 是 | 结束角度 | 
| anticlockwise | Boolean | 是 | true(顺时针),false(逆时针) | 
| radius | Number | 是 | 圆半径 | 
| border | Array | 是 | [0]:线条宽度,[1]:线条颜色值 | 
| bgColor | String | 是 | 背景色 | 
| innerBorder | Array | [0]:内环线条宽度,[1]:内环线条颜色值 |   | 
| innerBgColor | String | 是 | 内环背景色 | 
drawDashRound (ctx, x, y, dashround){    
     let step = dashround.step / 180 * 2 * Math.PI;
     for (let b = 0, e = step / 2; e <= dashround.endAngle; b += step, e += step) {
       ctx.beginPath()
       ctx.lineWidth = dashround.border[0];
       ctx.strokeStyle = dashround.border[1];
       ctx.closePath();
       ctx.arc(x, y, dashround.radius, b, e);
       ctx.stroke();
     }
     //填充内圈
     ctx.beginPath()
     ctx.lineWidth = dashround.innerBorder[0];
     ctx.strokeStyle= dashround.innerBorder[1];
     ctx.closePath();
     ctx.arc(x, y, dashround.radius - dashround.border[0], this.getRads(dashround.starAngle), this.getRads(dashround.endAngle));
     ctx.fillStyle= dashround.innerBgColor;
     ctx.fill();
     ctx.stroke();
	 
}
3.画等份线
方法:drawAngleSolid(ctx,x,y,line)
参数:ctx x y line
| 参数 | 类型 | 必填 | 说明 | 
|---|
| ctx | Object | 是 | canvas 2d绘图环境 | 
| x | Number | 是 | 圆心x坐标 | 
| y | Number | 是 | 圆心y坐标 | 
| line | Object | 是 | 基本参数 | 
参数:line
| 参数 | 类型 | 必填 | 说明 | 
|---|
| width | Number | 是 | 线条长度 | 
| border | Array | 是 | [0]:是线条宽度值,[1]:线条颜色值 | 
drawAngleSolid(ctx,x,y,line){
     let s = {
     	x:x,
     	y:y,
     	r:line.width
     }
     let num = this.data.allTagValues.length;   //获取等份
     let sang = this.getsolidPointAngle(num);   //获取等份角度	
     let spoint = this.getCircle(s,sang);		//获取等份角度点坐标		
     
     ctx.lineWidth = line.border[0];
     ctx.strokeStyle = line.border[1];
     
     let nleng = spoint.length/2;
     for(let i=0;i<spoint.length/2;i++){
     	ctx.beginPath();
     	ctx.moveTo(spoint[i].x,spoint[i].y);
     	ctx.lineTo(spoint[i+nleng].x,spoint[i+nleng].y);
     	ctx.stroke();
     	ctx.closePath();
     }
	
}
4.画多弧
方法:drawMoreScreen(ctx)
| 参数 | 类型 | 必填 | 说明 | 
|---|
| ctx | Object | 是 | canvas 2d绘图环境 | 
drawMoreScreen(ctx){
    let self = this;
    let screenArr = this.data.tagValueUUIDs;
    let allScreenArr = this.data.allTagValusUUIDs;
    let angle = 360 / allScreenArr.length;
    let angleArr = this.getsolidPointAngle(allScreenArr.length);
    let tmpArr = [];
    screenArr.forEach( val => {
    	let tmpobj = this.isArrayValue(val,allScreenArr);
    	if(tmpobj.flag){
    		let ang = angleArr[tmpobj.ind];
    		tmpArr.push(ang);
    	}
    });				
    tmpArr.forEach( val =>{
    	self.drawScreen(ctx,canvas.width,canvas.height,this.activeStyle.screen,this.getRads(val),this.getRads(val+angle));
    });
}
5.画单弧
方法:drawScreen (ctx,canvasWidth,canvasHeight,screen,sDeg,eDeg)
参数:ctx canvasWidth canvasHeight screen sDeg eDeg
| 参数 | 类型 | 必填 | 说明 | 
|---|
| ctx | Object | 是 | canvas 2d绘图环境 | 
| canvasWidth | Number | 是 | 画布宽度 | 
| canvasHeight | Number | 是 | 画布高度 | 
| screen | Object | 是 | 基本参数 | 
| sDeg | Number | 是 | 起始弧度 | 
| eDeg | Number | 是 | 结束弧度 | 
参数:screen
| 参数 | 类型 | 必填 | 说明 | 
|---|
| radius | Number | 是 | 圆半径 | 
| border | Array | 是 | [0]:弧宽度,[1]:弧颜色值 | 
| bgColor | String | 是 | 背景色 | 
drawScreen (ctx,canvasWidth,canvasHeight,screen,sDeg,eDeg) { 
let arc = { 
        x: canvasWidth / 2,
    	y: canvasHeight / 2,
    	r: screen.radius 
    }, 
    w = canvasWidth, 
    h = canvasHeight; 
    ctx.save(); 
    ctx.lineWidth = screen.border[0]; 	
    ctx.strokeStyle = screen.border[1];			
    ctx.fillStyle = screen.bgColor; 
    ctx.beginPath(); // 起始点设置在圆心处 
    ctx.moveTo(arc.x, arc.y);
    ctx.arc(arc.x, arc.y, arc.r,sDeg,eDeg); // 闭合路径
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
}
6.画小圈圈
方法:drawSmallCircle(ctx,x,y,smallRound)
参数:ctx x y smallRound
| 参数 | 类型 | 必填 | 说明 | 
|---|
| ctx | Object | 是 | canvas 2d绘图环境 | 
| x | Number | 是 | 圆心x坐标 | 
| y | Number | 是 | 圆心y坐标 | 
| smallRound | Object | 是 | 基本参数 | 
参数:smallRound
| 参数 | 类型 | 必填 | 说明 | 
|---|
| starAngle | Number | 是 | 起始角度 | 
| endAngle | Number | 是 | 结束角度 | 
| radius | Number | 是 | 圆半径 | 
| fatherRadius | Number | 是 | 位于父圆上的半径 | 
| border | Arrray | 是 | [0]:线条宽度,[1]:线条颜色值 | 
| noBorder | Array | 是 | [0]:未被选中线条宽度,[1]:未被选中线条颜色值 | 
| bgColor | String | 是 | 背景色 | 
drawSmallCircle(ctx,x,y,smallRound){
    let self = this;
    let c ={
    	x:x,
    	y:y,
    	r:smallRound.fatherRadius
    }
    let num = this.data.allTagValues.length; //获取等分
    let angles = this.getAngles(num);	
    let quan = this.getCircle(c,angles);  //画小圈圈	
    
    quan.forEach(function(val){
    	self.drawSolidRound(ctx,val.x,val.y,smallRound);
    });
}
7.绘制矩形
方法:drawRect(cxt,x,y,width,height,BW,BC,fillColor)
参数:cxt x y width height BW BC fillColor
| 参数 | 类型 | 必填 | 说明 | 
|---|
| ctx | Object | 是 | canvas 2d绘图环境 | 
| x | Number | 是 | 起始坐标x | 
| y | Number | 是 | 起始坐标y | 
| width | Number | 是 | 矩形宽度 | 
| height | Number | 是 | 矩形高度 | 
| BW | Number | 是 | 边框线条宽度值 | 
| BW | String | 是 | 边框线条颜色值 | 
| fillColor | String | 是 | 矩形填充色 | 
drawRect(cxt,x,y,width,height,BW,BC,fillColor){
    cxt.beginPath();
    cxt.moveTo(x,y);
    cxt.lineTo(x+width,y);
    cxt.lineTo(x+width,y+height);
    cxt.lineTo(x,y+height);
    cxt.closePath();
    
    cxt.lineWidth=BW;
    cxt.fillStyle=fillColor;
    cxt.strokeStyle=BC;
    
    cxt.fill();
    cxt.stroke();
}	
8.绘文字
方法:drwaText(ctx,x,y,text)
参数:ctx x y text
| 参数 | 类型 | 必填 | 说明 | 
|---|
| ctx | Object | 是 | canvas 2d绘图环境 | 
| x | Number | 是 | 圆心x坐标 | 
| y | Number | 是 | 圆心y坐标 | 
| text | Object | 是 | 基本参数 | 
参数:text
| 参数 | 类型 | 必填 | 说明 | 
|---|
| radius | Number | 是 | 在外圆文字 | 
| font | String | 是 | 字体 | 
| color | String | 是 | 字体颜色 | 
| noColor | String | 是 | 未被选中字体颜色 | 
| offsetPosition | Object | 是 | x:向’x’轴偏移x,y:向’y’轴偏移y | 
drwaText(ctx,x,y,text){	
    let t = {
    	x:x,
    	y:y,
    	r:text.radius
    }
    let divide = this.data.allTagValues.length;  //等分
    let angles = this.bbbb(divide);
    
    let points = this.getCircle(t,angles);
    
    //this.drawSmallCircle(ctx,2,points)
    //this.drawSolidRound(ctx,circle.x,circle.y,circle.r,0,360,{width:1,bgColor:'#000'});
    
    let tmpArr =  this.dealTextData(ctx,points,this.data.allTagValues,text.offsetPosition); //获取处理好的数据
    
    ctx.font = text.font;
    ctx.fillStyle = text.color;
    
    tmpArr.forEach(function(val){
    	ctx.fillText(val.text, val.point.x, val.point.y);	
    });
	
}
9.工具方法
//1.角度转弧度
getRads (degrees) { return (Math.PI * degrees) / 180; } 
//2.弧度转角度
getDegrees (rads) { return (rads * 180) / Math.PI; }
//3.获取角度等分
getAngles(num){
	let angle = 360 / num;
	let tmpArr = [];
	for(let i=1;i<=num;i++){
		tmpArr.push(angle * i);
	}
	return tmpArr;
}	
//4.获取圆上任意一点坐标
getCircle(circle,circleArr){		
	let tmpArr = [];			
	tmpArr = circleArr.map(function(val){
		let obj = {};
		obj.x = circle.x + circle.r * Math.cos(val * Math.PI / 180);
		obj.y = circle.y + circle.r * Math.sin(val * Math.PI / 180);
		return obj;
	});
	return tmpArr;
}
//5.获取等分线角度
getsolidPointAngle(num){
	let angle = 360 / num;
	let tmpArr = [];
	for(let i=0;i<num;i++){
		tmpArr.push(i * angle - 90);
	}
	return tmpArr;	
}
//6.随便起的名字
bbbb(num){
	let angle = 360 / num;
	let tmpArr = [];
	for(let i=0;i<num;i++){
		tmpArr.push(angle * i - 90 + angle/2 );
	}
	return tmpArr;
}
//7.判读数组是否存在某个值
isArrayValue(val,arr){
  let obj = {
	  flag:false,
	  ind:null
  }		
  arr.forEach((v,i) =>{
	if(val == v) {
		obj.flag = true;
		obj.ind = i;
	}
  });
  return obj;
}
//8.处理文字数据,返回文字起始坐标
dealTextData(ctx,points,textArr,offsetPosition){
	let tmpArr = [];
	textArr.forEach(function(val,ind){
		let obj = {};
		let textWidth = ctx.measureText(val).width;  //获取文本长度
		if(ind < textArr.length / 2){
			let point = {
					x: points[ind].x + offsetPosition.x,
					y: points[ind].y + offsetPosition.y
				};
			obj.text = val; 
			obj.point = point;
		}else{
			let point = {
					x: points[ind].x + offsetPosition.x - textWidth -20,
					y: points[ind].y + offsetPosition.y
				};
			obj.text = val; 
			obj.point = point;
		}
		tmpArr.push(obj);		
	});
	return tmpArr;		
}