最近作的这个小程序,因为UI设计图已经经业务方确认过,这个环形图没有商量的余地(好比用插件什么的),抱着正好玩一玩canvas的心态,自个看了点资料,画了一个。(包括原生js版和小程序版)javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<canvas id="circle" width="500" height="500"></canvas>
<script type="text/javascript">
circle('0.68');
function circle(percent) {
var canvas = document.getElementById('circle');
var imgObj = new Image();
imgObj.src = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1572866759391&di=af21821454e4acb13e6ba85150ab690d&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01708b55ed1f8e6ac7251df808d695.jpg"; // 弧形结束点的图片,若是失效了,本身随便替换一个网络图片
var ctx;
//待图片加载完后,将其显示在canvas上,避免其没法显示
imgObj.onload = function(){ //onload必须使用
ctx = canvas.getContext("2d");
/*填充文字*/
ctx.font = "24px Microsoft YaHei";
/*文字颜色*/
ctx.fillStyle = '#999';
/*文字内容*/
var insertContent = '本月任务进度';
var text = ctx.measureText(insertContent);
/*插入文字,后面两个参数为文字在画布中的坐标点*/
/*此处注意:text.width得到文字的宽度,而后就能计算出文字居中须要的x值*/
ctx.fillText(insertContent, (360 - text.width) / 2, 240);
/*填充百分比*/
ctx.font = "80px Microsoft YaHei";
ctx.fillStyle = '#222';
const percentArr = percent.split('.');
var ratioStr = percentArr[1] + '%';
var text = ctx.measureText(ratioStr);
ctx.fillText(ratioStr, (360 - text.width) / 2, 180);
/*开始圆环*/
var circleObj = {
ctx: ctx,
/*圆心坐标*/
x: 180,
y: 180,
/*半径,下方出现的150都是半径*/
radius: 150,
/*环的宽度*/
lineWidth: 24
}
/*灰色的圆环*/
/*开始的度数-从上一个结束的位置开始*/
circleObj.startAngle = 0; // 注意这里的0是3点钟方向,而非12点方向,和数学里的不同
/*结束的度数*/
circleObj.endAngle = Math.PI * 2;
circleObj.color = '#E7EFF4';
drawCircle(circleObj);
/*有色的圆环*/
/*从-90度的地方开始画*/
circleObj.startAngle = 1.5*Math.PI; //把起始点改为数学里的12点方向
/*从当前度数减去-90度*/
let holeCicle = 2 * Math.PI;
let jiaodu = percent * 360; //圆弧的角度
let x1 = 180 + (150 * Math.sin(jiaodu * Math.PI/180)) - 25; // 为啥要减去25,由于icon图标的宽度为50,而定位是以icon左上角为基准的
let y1 = 180 - 150 + (150 - 150 * Math.cos(jiaodu * Math.PI/180)) - 25;
circleObj.endAngle = 1.5*Math.PI + percent*holeCicle;
var gnt1 = ctx.createLinearGradient(0,0,x1,y1);
gnt1.addColorStop(0,'#FF8941');
gnt1.addColorStop(0.3,'#FF8935');
gnt1.addColorStop(1,'#FFC255');
// ctx.strokeStyle = gnt1;
circleObj.color = gnt1;
drawCircle(circleObj);
// 这里的this指的是imgObj,第二三个参数是它的坐标,四五个是长款
ctx.drawImage(this, x1, y1, 50, 50);
}
}
/*画曲线*/
function drawCircle(circleObj) {
var ctx = circleObj.ctx;
ctx.beginPath();
ctx.arc(circleObj.x, circleObj.y, circleObj.radius, circleObj.startAngle, circleObj.endAngle, false);
//设定曲线粗细度
ctx.lineWidth = circleObj.lineWidth;
//给曲线着色
ctx.strokeStyle = circleObj.color;
//链接处样式
ctx.lineCap = 'round';
//给环着色
ctx.stroke();
ctx.closePath();
}
</script>
</body>
</html>
复制代码
一开始我是用原生写的,他大爷的,转移到小程序发现,怎么api都长的不同!还有不少隐藏的深坑,好比拿不到图片对象啊,显示不出来啊,还好,最后顺利搞定。html
Page({
data: {
canvas: {
height: 0,
width: 0
},
canvasWidth: 100, // 这里拜托必定要给个初始宽度啊~否则iPhone6以及华为手机会偶尔没法显示,并且是锁屏后打开又莫名其妙显示的那种……
x:0,
y:0, // 圆心坐标
r:0, // 半径
},
onReady() {
let rpx = this.getRatio();
let canvasWidth = rpx * 300;
let r = rpx * 75;
let x = canvasWidth / 2;
let y = canvasWidth / 2;
this.setData({
canvasWidth,
r,
x,
y,
rpx
});
this.drawRing(68%);
},
// 获取画布宽度相对于375的比例,方便适配各类屏幕
getRatio() {
let w = 0;
wx.getSystemInfo({
success: function (res) {
w = res.windowWidth / 375;//按照750的屏宽
},
})
return w
},
drawRing(percent) {
const {r, x, y, rpx, canvasWidth} = this.data;
let url = "success.png";
var ctx;
var ctx2; // 这里也是很迷,原生js只用建立一个,这里却要2个,分别用来盛放灰色圈和彩色圈
//待图片加载完后,将其显示在canvas上
ctx = wx.createCanvasContext('circle');
ctx2 = wx.createCanvasContext('circle2');
/*填充文字*/
ctx.setFontSize(12 * rpx);
ctx2.setFontSize(40 * rpx);
/*文字颜色*/
ctx.setFillStyle('#999');
/*文字内容*/
var insertContent = '本月任务进度';
var text = ctx.measureText(insertContent);
/*插入文字,后面两个参数为文字的位置*/
/*此处注意:text.width得到文字的宽度,而后就能计算出文字居中须要的x值*/
ctx.fillText(insertContent, (canvasWidth - text.width)/2, 185 * rpx);
/*填充百分比*/
ctx2.setFillStyle('#222');
var ratioStr = this.data.options.clockRate;
var text2 = ctx2.measureText(ratioStr);
ctx2.fillText(ratioStr, (canvasWidth - text2.width) / 2, 165*rpx);
/*开始圆环*/
var circleObj = {
ctx: ctx,
/*圆心*/
x: x,
y: y,
/*半径*/
radius: r,
/*环的宽度*/
lineWidth: 12 * rpx,
startAngle: 0,
endAngle: 0,
color: ''
}
/*灰色的圆环*/
/*开始的度数-从上一个结束的位置开始*/
circleObj.startAngle = 0;
/*结束的度数*/
circleObj.endAngle = Math.PI * 2;
circleObj.color = '#E7EFF4';
this.drawCircle(circleObj);
/*有色的圆环*/
var coloredObject = {
ctx: ctx2,
/*圆心*/
x: x,
y: y,
/*半径*/
radius: r,
/*环的宽度*/
lineWidth: 12 * rpx,
startAngle: 0,
endAngle: 0,
color: ''
}
/*从-90度的地方开始画*/
coloredObject.startAngle = 1.5*Math.PI;
/*从当前度数减去-90度*/
let holeCicle = 2 * Math.PI;
const percentArr = percent.split('.');
var ratioStr = percentArr[1] + '%';
const rate = percent
let x1 = x + (r * Math.sin(Number(rate) * 360 * Math.PI/180)) - 30*rpx/2;
let y1 = y - r + (r - r * Math.cos((Number(rate) * 360) * Math.PI/180)) - 30*rpx/2;
coloredObject.endAngle = 1.5*Math.PI + Number(rate)*holeCicle;
var gnt1 = ctx2.createLinearGradient(0,0,x1,y1);
gnt1.addColorStop(0,'#FF8941');
gnt1.addColorStop(0.3,'#FF8941');
gnt1.addColorStop(1,'#FFC255');
coloredObject.color = gnt1;
this.drawCircle(coloredObject, x1, y1);
// }
},
/*画曲线*/
drawCircle(circleObj, x1, y1) {
var ctx = circleObj.ctx;
ctx.beginPath();
ctx.arc(circleObj.x, circleObj.y, circleObj.radius, circleObj.startAngle, circleObj.endAngle, false);
//设定曲线粗细度
ctx.setLineWidth(circleObj.lineWidth);
//给曲线着色
ctx.setStrokeStyle(circleObj.color);
//链接处样式
ctx.setLineCap('round');
//给环着色
ctx.stroke();
ctx.closePath();
if (x1 && y1) {
ctx.drawImage('success.png', x1, y1, 30*this.data.rpx, 30*this.data.rpx);
}
ctx.draw();
},
});
复制代码
这里也有一个坑,那就是原生js直接经过id属性获取canvas画布对象就好,可是,小程序里必定要搞个canvas-id属性……java
<canvas wx:if="{{canvasWidth}}" canvas-id="circle" id="circle" style="height: {{canvasWidth || 100}}px;width: {{canvasWidth || 100}}px;margin:0 auto;"></canvas>
<canvas wx:if="{{canvasWidth}}" canvas-id="circle2" id="circle2" style="height: {{canvasWidth}}px;width: {{canvasWidth}}px;position:absolute;top:0;left:50%;margin-left:-{{canvasWidth/2}}px;"></canvas>
复制代码
好了,到此就大功告成咯~这玩意就是有点繁琐,而后须要一丢丢三角函数(只要你想找,公式处处都有啦)canvas
由于写了有大半个月了,也不记得原生js版借鉴了哪些文章,抱歉不能列出……小程序