使用canvas绘制较复杂的类似极坐标的图形

前些天接到一个需求要将桌面软件的度盘界面在web页面上画出来,找了一写前端的图形组件库echarts等,都没有可以达到这种效果的,所以只能用canvas自己来绘制了。用到的都是canvas里面的最基本的画直线、圆弧、绘制路径、填充这些函数,在这里记录一下。

效果图:

思路:

1. 先绘制内圈,包括刻度和刻度值,中心位置文字。

2. 绘制7个外圈的圆形,判断为最后一个圈时画半圆。

3. 绘制外圈的刻度线和分割线。

4. 根据弧度值填充环的颜色。

注意的问题:
1. canvas标签的宽和高设置,直接使用width和height属性。使用样式设置缩放页面会有问题。

2. 同一个位置有时回有重叠显示的问题,可以先清除该区域的内容在绘制。

3. 每次开始绘制前最好开启新的路径。

4. 上一次对画笔的设置在下一次使用时不会自动清空,需要重新设置覆盖。

5. 绘制线条是回有锯齿状,可以根据屏幕分辨率去调整。

代码如下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>dees度盘</title>
		<style type="text/css">
			.mycanvas{
				border: 1px solid #C7C7CC;
				background-color: #000000;
			}
		</style>
	</head>
	<body>
		<canvas class="mycanvas" width="800" height="800"></canvas>
		
		<script type="text/javascript">
			// 构造函数-度盘图对象
			var DialChart = function(ctx) {
				var canvas = document.querySelector('canvas');
				//获取绘图工具
				this.ctx = ctx || canvas.getContext('2d');
				// canvas的宽度和高度
				this.canvasWidth = this.ctx.canvas.width;
				this.canvasHeight = this.ctx.canvas.height;
				// 画布中心点
				this.x0 = this.canvasWidth;
				this.y0 = this.canvasHeight;
				// 内圆半径
				this.innerR = 100;
				// 刻度长度
				this.shortScale = 5;
				this.middleScale = 15;
				this.longScale = 50;
				// 外环宽度
				this.ringWidth = 50;
				// 每30个角度一个分割线
				this.sepAngle = Math.PI / 6;
				
				if (window.devicePixelRatio) {
					console.log('---',window.devicePixelRatio);
					canvas.style.width = this.canvasWidth + "px";
					canvas.style.height = this.canvasHeight + "px";
					canvas.height = this.canvasHeight * window.devicePixelRatio;
					canvas.width = this.canvasHeight * window.devicePixelRatio;
				}
			};
			
			// 初始化方法
			DialChart.prototype.init = function() {
				this.drawInnerCircle();
				this.drawOurterCircle();
				this.fillColor();
			};
			
			// 绘制内圈方法
			DialChart.prototype.drawInnerCircle = function() {
				this.ctx.beginPath();
				this.ctx.arc(this.x0, this.y0, this.innerR, 0, Math.PI*2);
				this.ctx.strokeStyle = '#fff';
				this.ctx.stroke();
				
				var angle = Math.PI / 36;
				// 绘制刻度
				for (var i = 0; i < 72; i++) {
					this.ctx.beginPath();
					var nowAngle = angle * i;
					var startX = this.x0 + this.innerR * Math.sin(nowAngle);
					var startY = this.y0 - this.innerR * Math.cos(nowAngle);
					var endX = this.x0 + (this.innerR - this.shortScale) * Math.sin(nowAngle);
					var endY = this.y0 - (this.innerR - this.shortScale) * Math.cos(nowAngle);
					this.ctx.moveTo(startX, startY);
					this.ctx.lineTo(endX, endY);
					this.ctx.stroke();
					
					if (nowAngle === 0) {
						console.log('--0--');
						this.ctx.beginPath();
						this.ctx.textAlign = 'center';
						this.ctx.strokeText('0',endX,endY+10);
					}
					else if(nowAngle === Math.PI / 2) {
						this.ctx.beginPath();
						this.ctx.textAlign = 'center';
						this.ctx.textBaseline = 'middle';
						this.ctx.strokeText('90',endX-10,endY);
					}
					else if(nowAngle === Math.PI ) {
						this.ctx.beginPath();
						this.ctx.textAlign = 'center';
						this.ctx.strokeText('180',endX,endY-7);
					}
					else if(nowAngle === Math.PI * 1.5) {
						this.ctx.beginPath();
						this.ctx.textAlign = 'center';	
						this.ctx.textBaseline = 'middle';
						this.ctx.strokeText('270',endX+12,endY);
					}
				}
				
				this.ctx.font = '16px SimSun, Songti SC';
				this.ctx.fillStyle = '#fff';
				this.ctx.fillText('重工面',this.x0,this.y0-25);
				this.ctx.fillStyle = 'red';
				this.ctx.textAlign = 'center';
				this.ctx.font = '20px SimSun, Songti SC';
				this.ctx.fillText('112.5',this.x0,this.y0);
				this.ctx.fillStyle = '#fff';
				this.ctx.font = '16px SimSun, Songti SC';
				this.ctx.fillText('11:03:00',this.x0,this.y0+20);
			};
			
			// 绘制外圈
			DialChart.prototype.drawOurterCircle = function() {
				// 绘制的圈数
				var circleNum = 7;
				
				for (var i = 1; i <= circleNum; i++) {
					this.ctx.beginPath();
					if (i === 7) {
						this.ctx.arc(this.x0, this.y0, this.innerR + i*this.ringWidth, Math.PI/2, Math.PI*3/2);
					} else{
						this.ctx.arc(this.x0, this.y0, this.innerR + i*this.ringWidth, 0, Math.PI*2);
					}					
					this.ctx.strokeStyle = '#fff';
					this.ctx.stroke();
				}	
				
				this.drawSepline();
				this.drawOurterScale();
			};
			
			// 绘制12份 分割线
			DialChart.prototype.drawSepline = function() {
				for (var i = 0; i < 12; i++) {
					this.ctx.beginPath();
					var startX = this.x0 + this.innerR * Math.sin(this.sepAngle * i);
					var startY = this.y0 - this.innerR * Math.cos(this.sepAngle * i);
					var endX = this.x0 + (this.innerR + this.ringWidth * 5) * Math.sin(this.sepAngle * i);
					var endY = this.y0 - (this.innerR + this.ringWidth * 5) * Math.cos(this.sepAngle * i);
					
					this.ctx.moveTo(startX, startY);
					this.ctx.lineTo(endX, endY);
					this.ctx.strokeStyle = '#fff';
					this.ctx.lineWidth = 1;
					this.ctx.setLineDash([3,3]);
					// this.ctx.fillStyle = '#fff';
					this.ctx.stroke();
				}
			};
			
			// 绘制外圈刻度
			DialChart.prototype.drawOurterScale = function() {
				console.log('--drawOurterScale--');
				var angle = Math.PI / 36;
				var flag = 0;
				// 绘制刻度
				for (var i = 0; i <= 72; i++) {
					this.ctx.beginPath();
					var nowAngle = angle * i;
					var nowRadius = this.innerR + this.ringWidth*6;
					var startX = this.x0 + nowRadius * Math.sin(nowAngle);
					var startY = this.y0 - nowRadius * Math.cos(nowAngle);
					
					var str = (i/6) * 30;
					if(i%6 === 0) {
						var endX = this.x0 + (nowRadius + this.longScale) * Math.sin(nowAngle);
						var endY = this.y0 - (nowRadius + this.longScale) * Math.cos(nowAngle);
						this.ctx.beginPath();
						this.ctx.textAlign = 'center';
						this.ctx.textBaseline = 'middle';
						this.ctx.font = '14px SimSun, Songti SC';
						this.ctx.strokeText(str+'',this.x0 + (nowRadius-this.ringWidth/2) * Math.sin(nowAngle), this.y0 - (nowRadius-this.ringWidth/2) * Math.cos(nowAngle));
						if (str >= 180) {
							this.ctx.strokeText(30*flag,this.x0 + (nowRadius+this.ringWidth*2-this.ringWidth/2) * Math.sin(nowAngle), this.y0 - (nowRadius+this.ringWidth*2-this.ringWidth/2) * Math.cos(nowAngle));
							flag = flag + 1;
						} 
					}
					else if(i%2 === 0) {
						var endX = this.x0 + (nowRadius + this.middleScale) * Math.sin(nowAngle);
						var endY = this.y0 - (nowRadius + this.middleScale) * Math.cos(nowAngle);
					}
					else {
						var endX = this.x0 + (nowRadius + this.shortScale) * Math.sin(nowAngle);
						var endY = this.y0 - (nowRadius + this.shortScale) * Math.cos(nowAngle);
					}
					
					this.ctx.clearRect(this.x0-16,this.y0 - this.innerR - this.ringWidth * 5.5-8,28,15);
					this.ctx.strokeText('0',this.x0, this.y0 - this.innerR - this.ringWidth * 5.5);
					
					this.ctx.moveTo(startX, startY);
					this.ctx.lineTo(endX, endY);
					this.ctx.setLineDash([1,0]);
					this.ctx.stroke();	
														
				}	
				console.log('--drawOurterScale end--');				
				
			};	
			
			// 颜色填充
			DialChart.prototype.fillColor = function() {
				this.ctx.beginPath();
				this.ctx.strokeStyle = '#2AC845';
				this.ctx.lineWidth = this.ringWidth-1;
				this.ctx.arc(this.x0,this.y0,this.innerR + this.ringWidth*1.5,-Math.PI/2,Math.PI/2);
				this.ctx.stroke();
								
			}					
									
			// 实例化对象
			var dialChart = new DialChart();
			dialChart.init();			
			
		</script>
	</body>
</html>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值