环图
canvas制作圆形所用的方法是
ctx.arc(x,y,r,0,Math,PI*2,false)
其中的6个参数分别是圆心横坐标,圆心纵坐标,半径,起始角度,结束角度,false顺势针/true逆时针
而圆环也是用的这个方法,只不过是ctx.fill()填充,替换成了ctx.stroke()描边,利用描边我们就可以绘制出任何大小及弧度的圆环了,通过ctx.linewidth可以设置描边的粗细,也就是圆环的宽窄。
上图中其实是个统计用的图表,展示的是四个数据在这个圆环中的占比情况。由于canvas采用的是帧绘制,所以在有轮流高亮的情况下,就是定时的清除画布然后重绘,清除画布的方法是
ctx.clearRect(起始x,起始y,结束x,结束y)
图一
图一JS
function gague(arr, index) {
var canvas = document.querySelector('#cav')
var cav = canvas.getContext('2d');
cav.clearRect(0, 0, 300, 300);
var wholeCircle = Math.PI * 2
var wholeAngle = wholeCircle * 3 / 4 //圆环的全部角度是3/4个圆
var sum = eval(arr.join('+')) //对传入数据的处理求和
var startAngle = wholeCircle * 3 / 8 + wholeCircle / 200 //设置圆环的初始角度
var endAngle = startAngle + wholeAngle * (arr[0] / sum) - wholeCircle / 100 //根据数据占比设置第一个弧长的结束角度
//绘制起始的小分割圆弧
cav.beginPath()
cav.lineWidth = 40
cav.strokeStyle = 'rgb(56,64,129)';
cav.arc(150, 150, 90, startAngle - wholeCircle / 200, startAngle, false)
cav.stroke()
for (let i = 0; i < arr.length; i++) {
//getPoint是自定义的一个方法,通过传入圆弧的起始角度,结束角度,半径和圆心的横纵坐标来获得圆弧的起始和结束点坐标,返回的是一个对象集合
var PointAssemble = getPoint(startAngle, endAngle, 110, 150, 150)
//cav.createLinearGradient()是canvas设置渐变色的方法,这是径向渐变传入的参数是渐变起始点横纵坐标和结束点横纵坐标
var normalColor = cav.createLinearGradient(PointAssemble.startPointX, PointAssemble.startPointY, PointAssemble
.endPointX, PointAssemble.endPointY);
normalColor.addColorStop(0, "rgba(68,102,244,0.2)");
normalColor.addColorStop(1, 'rgba(68,102,244,0.4)');
var highlightColor = cav.createLinearGradient(PointAssemble.startPointX, PointAssemble.startPointY,
PointAssemble.endPointX, PointAssemble.endPointY);
highlightColor.addColorStop(0, "rgba(56,64,129,0.2)");
highlightColor.addColorStop(1, 'rgba(255,15,110,0.6)');
//开始绘制圆弧
cav.beginPath()
cav.lineWidth = 40
cav.strokeStyle = index == i ? highlightColor : normalColor;
cav.arc(150, 150, 90, startAngle, endAngle, false)
cav.stroke()
//绘制每个圆弧之间的分割圆弧
cav.beginPath()
cav.lineWidth = 40
cav.strokeStyle = index == i ? 'rgb(255,15,110)' : 'rgb(68,102,244)';
cav.arc(150, 150, 90, endAngle, endAngle + wholeCircle / 100, false)
cav.stroke()
startAngle = startAngle + wholeAngle * (arr[i] / sum)
endAngle = startAngle + wholeAngle * (arr[i + 1] / sum) - wholeCircle / 100
}
}
图二
上图的高亮处小圆点是个图片,要注意的是,如果canvas中出现图片,要先获取图片,等图片加载完毕之后再调用canvas的绘图方法
var img = new Image()
img.src='图片路径'
img.onload =()=>{
//这里调用你的绘图函数
}
图二JS
function halfCircle(arr, index) {
var canvas = document.querySelector('#cav4')
var cav = canvas.getContext('2d');
cav.clearRect(0, 0, 300, 300);
//跟上面的环图一样,首先先设置的是初始值,比如第一个环的起始和结束角度以及整个圆弧的角度
var wholeCircle = Math.PI * 2
var wholeAngle = wholeCircle / 2
var sum = eval(arr.join('+'))
var startAngle = wholeCircle / 2
var endAngle = startAngle + wholeAngle * (arr[0] / sum)
for (let i = 0; i < arr.length; i++) {
//这里依然需要获取到每一个圆弧的起始和结束坐标点用来定位图片的绘制位置
var PointAssemble = getPoint(startAngle, endAngle, 90, 150, 150)
var normalColor = cav.createLinearGradient(PointAssemble.startPointX, PointAssemble.startPointY, PointAssemble
.endPointX, PointAssemble.endPointY);
normalColor.addColorStop(0, "rgba(9, 9, 24,0)");
normalColor.addColorStop(1, 'rgba(42, 81, 248,1)');
var highlightColor = cav.createLinearGradient(PointAssemble.startPointX, PointAssemble.startPointY,
PointAssemble.endPointX, PointAssemble.endPointY);
highlightColor.addColorStop(0, "rgba(29, 11, 35,0)");
highlightColor.addColorStop(1, 'rgba(228, 90, 147,1)');
cav.beginPath()
cav.lineWidth = 10
cav.strokeStyle = index == i ? highlightColor : normalColor;
cav.lineCap = 'round'
cav.arc(150, 150, 90, startAngle, endAngle, false)
cav.stroke()
if (i == index) {
cav.drawImage(imgYuan, PointAssemble.endPointX - imgYuan.width / 2, PointAssemble.endPointY - imgYuan
.height / 2)
}
startAngle = startAngle + wholeAngle * (arr[i] / sum)
endAngle = startAngle + wholeAngle * (arr[i + 1] / sum)
}
}
值得注意的一点是,当你的圆环半径确定的时候,不管你的圆环宽度有多宽,圆环的半径始终是从圆心到二分之一圆环宽度的距离,而不是从圆心到圆环外边或者到圆环内边的距离,所以你的圆环越宽,他的实际面积会越大
自定义获取坐标点函数
function getPoint(startAngle, endAngle, r, circleX, circleY) {
var startPointX, startPointY, endPointX, endPointY
if (startAngle < Math.PI) {
startPointX = circleX - Math.sin(startAngle - Math.PI / 2) * r
startPointY = circleY + Math.cos(startAngle - Math.PI / 2) * r
} else if (startAngle < Math.PI * 3 / 2) {
startPointX = circleX - Math.cos(startAngle - Math.PI) * r
startPointY = circleY - Math.sin(startAngle - Math.PI) * r
} else if (startAngle < Math.PI * 2) {
startPointX = circleX + Math.sin(startAngle - Math.PI * 3 / 2) * r
startPointY = circleY - Math.cos(startAngle - Math.PI * 3 / 2) * r
} else {
startPointX = circleX + Math.cos(startAngle - Math.PI * 2) * r
startPointY = circleY + Math.sin(startAngle - Math.PI * 2) * r
}
if (endAngle < Math.PI) {
endPointX = circleX - Math.sin(endAngle - Math.PI / 2) * r
endPointY = circleY + Math.cos(endAngle - Math.PI / 2) * r
} else if (endAngle < Math.PI * 3 / 2) {
endPointX = circleX - Math.cos(endAngle - Math.PI) * r
endPointY = circleY - Math.sin(endAngle - Math.PI) * r
} else if (endAngle < Math.PI * 2) {
endPointX = circleX + Math.sin(endAngle - Math.PI * 3 / 2) * r
endPointY = circleY - Math.cos(endAngle - Math.PI * 3 / 2) * r
} else {
endPointX = circleX + Math.cos(endAngle - Math.PI * 2) * r
endPointY = circleY + Math.sin(endAngle - Math.PI * 2) * r
}
return {
startPointX,
startPointY,
endPointX,
endPointY
}
}