最近作的這個小程序,因爲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版借鑑了哪些文章,抱歉不能列出……小程序