最近在项目中用到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

参数类型必填说明
ctxObjectcanvas 2d绘图环境
xNumber圆心x坐标
yNumber圆心y坐标
roundObject圆的基本参数

参数:round

参数类型必填说明
starAngleNumber起始角度
endAngleNumber结束角度
anticlockwiseBooleantrue(顺时针),false(逆时针)
radiusNumber圆半径
borderArray[0]:是线条宽度值,[1]:线条颜色值
bgColorString背景色
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

参数类型必填说明
ctxObjectcanvas 2d绘图环境
xNumber圆心x坐标
yNumber圆心y坐标
dashroundObject基本参数

参数:dashround

参数类型必填说明
stepNumber虚线间距(值越大,间距越大)
starAngleNumber起始角度
endAngleNumber结束角度
anticlockwiseBooleantrue(顺时针),false(逆时针)
radiusNumber圆半径
borderArray[0]:线条宽度,[1]:线条颜色值
bgColorString背景色
innerBorderArray[0]:内环线条宽度,[1]:内环线条颜色值 
innerBgColorString内环背景色
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

参数类型必填说明
ctxObjectcanvas 2d绘图环境
xNumber圆心x坐标
yNumber圆心y坐标
lineObject基本参数

参数:line

参数类型必填说明
widthNumber线条长度
borderArray[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)

参数类型必填说明
ctxObjectcanvas 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

参数类型必填说明
ctxObjectcanvas 2d绘图环境
canvasWidthNumber画布宽度
canvasHeightNumber画布高度
screenObject基本参数
sDegNumber起始弧度
eDegNumber结束弧度

参数:screen

参数类型必填说明
radiusNumber圆半径
borderArray[0]:弧宽度,[1]:弧颜色值
bgColorString背景色
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

参数类型必填说明
ctxObjectcanvas 2d绘图环境
xNumber圆心x坐标
yNumber圆心y坐标
smallRoundObject基本参数

参数:smallRound

参数类型必填说明
starAngleNumber起始角度
endAngleNumber结束角度
radiusNumber圆半径
fatherRadiusNumber位于父圆上的半径
borderArrray[0]:线条宽度,[1]:线条颜色值
noBorderArray[0]:未被选中线条宽度,[1]:未被选中线条颜色值
bgColorString背景色
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

参数类型必填说明
ctxObjectcanvas 2d绘图环境
xNumber起始坐标x
yNumber起始坐标y
widthNumber矩形宽度
heightNumber矩形高度
BWNumber边框线条宽度值
BWString边框线条颜色值
fillColorString矩形填充色
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

参数类型必填说明
ctxObjectcanvas 2d绘图环境
xNumber圆心x坐标
yNumber圆心y坐标
textObject基本参数

参数:text

参数类型必填说明
radiusNumber在外圆文字
fontString字体
colorString字体颜色
noColorString未被选中字体颜色
offsetPositionObjectx:向’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;		
}