前些天接到一个需求要将桌面软件的度盘界面在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>