小程序canvas自定义圆弧进度条
2023-04-21 本文已影响0人
hao_developer
效果图
image.png思路
考虑到背景是不变的,把背景和会动的分为两个canvas来进行画
image.png
image.png
wxss
page{
background: #fd7895;
}
.component-slider {
width: 90%;
position: relative;
margin: 0rpx auto 0rpx;
padding: 70rpx 0 70rpx;
}
/* 盒子 */
.slider-box {
width: 88%;
margin: 0 auto;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
/* 未选中区线 */
.slider-line {
width: 100%;
height: 10rpx;
background: rgba(91, 150, 246, 0.1);
margin: 0 auto;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
/* 选中区线 */
.slider-line-active {
position: absolute;
left: 0;
top: 50%;
transform: translate(0, -50%);
height: 10rpx;
background: #5B96F6;
}
/* slider按钮 */
.slider-btn {
width: 70rpx;
height: 35rpx;
background: #5B96F6;
border-radius: 20rpx;
}
/* 显示的数字 */
.slider-number {
width: 100%;
position: absolute;
bottom: -10rpx;
}
.slider-number text {
position: absolute;
top: 0;
font-size: 24rpx;
color: #999999;
transition: all 0.3s;
}
/* 当前选中的数字 */
.slider-number text.active {
font-size: 32rpx;
color: #5B96F6;
transition: all 0.3s;
}
/* slider组件设置透明 */
slider {
opacity: 0;
}
wxml
<view style="position: relative;margin-top: 100rpx;">
<canvas canvas-id="circle" style="border: 1rpx solid red;width: 90vw;height: 90vw;margin: 0 auto;"></canvas>
<canvas canvas-id="circle2" style="border: 1rpx solid green;width: 90vw;height: 90vw;margin: 0 auto;position: absolute;top: 0;right: 0;bottom: 0;left: 0;z-index: 10;"></canvas>
</view>
<view style="display: flex;flex-direction: row;margin-top: 50rpx;padding: 0 40rpx;">
<button bindtap="btnHandler" data-type="{{1}}" type="default" style="margin-right: 50rpx;">加+1</button>
<button bindtap="btnHandler" data-type="{{2}}" type="default">减-1</button>
</view>
<view style="display: flex;flex-direction: row;margin-top: 50rpx;padding: 0 40rpx;">
<button bindtap="clickHandler" data-type="{{1}}" type="default" style="margin-right: 50rpx;">开始</button>
<button bindtap="clickHandler" data-type="{{2}}" type="default">结束</button>
</view>
js
const app = getApp();
Page({
data: {
min: 0, // 最小限制
max: 5, // 最大限制
value: 60, // 当前value
zu: [],
addId: null,
reduceId: null,
ctx: null,
centerPoint: 0,
radius: 0,
whiteRadius: 0,
subCircleth: 0,
sexth: 0,
},
onLoad(option) {
this.initData();
this.canvasCircle();
this.proCanvasCircle();
},
onShow() {},
initData() {
this.data.width = app.globalData.width * 0.9; //画布宽度
this.data.heigth = app.globalData.width * 0.9; //画布高度
this.data.radius = this.data.width / 2 - 80; //画布半径
this.data.centerPoint = { //中心点
x: this.data.width / 2,
y: this.data.heigth / 2
};
this.data.whiteRadius = this.data.radius * 0.7; //白色圆环半径
this.data.subCircleth = this.data.radius * 0.3; //
this.data.sexth = this.data.radius * 0.05;
this.data.ctx = wx.createCanvasContext('circle2');
this.data.ctx.save();
},
btnHandler(e) { //加1,减1事件
const type = e.currentTarget.dataset.type;
let value = this.data.value;
if (type == 1) {
value++;
if (value > 150) return;
} else {
value--;
if (value < 0) return;
}
this.data.value = value;
this.proCanvasCircle();
},
// 拖动过程中触发的事件
sliderchanging(e) {
var value = e.detail.value;
this.setData({
value: value
})
},
clickHandler(e) { //循环加减操作
const type = e.currentTarget.dataset.type;
if (type == 1) {
clearInterval(this.data.startId);
clearInterval(this.data.reduceId);
this.addNum();
} else {
clearInterval(this.data.addId);
clearInterval(this.data.reduceId);
}
},
addNum() {
this.data.addId = setInterval(() => {
let value = this.data.value;
value++;
if (value > 150) {
clearInterval(this.data.addId);
this.recuceNum();
} else {
this.data.value = value;
this.proCanvasCircle();
}
}, 100);
},
recuceNum() {
this.data.reduceId = setInterval(() => {
let value = this.data.value;
value--;
if (value < 0) {
clearInterval(this.data.reduceId);
this.addNum();
} else {
this.data.value = value;
this.proCanvasCircle();
}
}, 100);
},
test() {
for (let index = 0; index < 20; index++) {
this.data.zu.push(index);
}
},
// 完成一次拖动后触发的事件
sliderchange(e) {
var value = e.detail.value;
this.setData({
value: value
});
},
proCanvasCircle() { //画当前圆弧进度
const value = this.data.value;
this.data.ctx.translate(this.data.centerPoint.x, this.data.centerPoint.y); //把canvas圆点(0,0)移动到centerPoint点上
//进度色圆环
this.data.ctx.rotate(180 * Math.PI / 180); //旋转180,起点从9点钟开始
this.data.ctx.setLineWidth(this.data.subCircleth);
this.data.ctx.setLineCap('butt')
this.data.ctx.beginPath();
this.data.ctx.setStrokeStyle('#fbc850');
this.data.ctx.arc(0, 0, this.data.whiteRadius + this.data.subCircleth / 2, 0 * Math.PI, value / 150 * 1.25 * Math.PI);
this.data.ctx.stroke();
//进度圆点
this.data.ctx.rotate(-180 * Math.PI / 180); //旋转-180,起点从9点钟开始
const pointRadius = this.data.radius * 0.86;
const ponitPosition = {};
const bili = 30 / 45; // 每一值代表多度
const circleWidth = this.data.radius * 0.13;
this.data.ctx.beginPath();
if (value >= 0 && value <= 90) { //从0mmHg~90mmHg之间的夹角 0°~135°
ponitPosition.x = -Math.cos(value / bili * Math.PI / 180) * pointRadius;
ponitPosition.y = -Math.sin(value / bili * Math.PI / 180) * pointRadius;
} else if (value > 90 && value <= 120) { //从90mmHg~120mmHg之间的夹角 0°~45°
ponitPosition.x = Math.cos((45 - (value - 90) / bili) * Math.PI / 180) * pointRadius;
ponitPosition.y = -Math.sin((45 - (value - 90) / bili) * Math.PI / 180) * pointRadius;
} else if (value >= 120 && value <= 150) { //从120mmHg~150mmHg之间的夹角 0°~45°
ponitPosition.x = Math.cos((value - 120) / bili * Math.PI / 180) * pointRadius;
ponitPosition.y = Math.sin((value - 120) / bili * Math.PI / 180) * pointRadius;
}
this.data.ctx.drawImage('../../image/circle.png',
ponitPosition.x - circleWidth / 2, ponitPosition.y - circleWidth / 2,
circleWidth, circleWidth);
//最外侧有色圆环 180
this.data.ctx.rotate(180 * Math.PI / 180); //旋转180,起点从9钟开始
this.data.ctx.beginPath();
this.data.ctx.setLineWidth(this.data.sexth);
this.data.ctx.setLineCap('round');
this.data.ctx.setStrokeStyle('#fbc850');
this.data.ctx.fill();
this.data.ctx.arc(0, 0, this.data.whiteRadius + this.data.subCircleth + this.data.sexth, 0 * Math.PI, value / 150 * 1.25 * Math.PI);
this.data.ctx.stroke();
//当前值
this.data.ctx.rotate(-180 * Math.PI / 180); //旋转180,起点从9钟开始
this.data.ctx.setFontSize(22);
const valueStr = '' + value;
const valueWidth = this.data.ctx.measureText(valueStr).width;
this.data.ctx.beginPath();
this.data.ctx.setFillStyle('black');
this.data.ctx.fillText(valueStr, -valueWidth, -this.data.radius * 0.02);
this.data.ctx.fill();
this.data.ctx.stroke();
this.data.ctx.draw();
},
canvasCircle() {
const ctx = wx.createCanvasContext('circle');
ctx.save();
ctx.translate(this.data.centerPoint.x, this.data.centerPoint.y);
//中间白色圆
ctx.beginPath();
ctx.setStrokeStyle('white');
ctx.arc(0, 0, this.data.whiteRadius, 0, 2 * Math.PI);
ctx.setFillStyle('white')
ctx.fill();
ctx.stroke();
//半透明圆环
ctx.setLineWidth(this.data.subCircleth);
ctx.beginPath();
ctx.setStrokeStyle('white');
ctx.setGlobalAlpha(0.5); //设置为半透明
ctx.arc(0, 0, this.data.whiteRadius + this.data.subCircleth / 2, 0, 2 * Math.PI);
ctx.stroke();
ctx.setGlobalAlpha(1); //设置为不透明
ctx.rotate(90 * Math.PI / 180); //旋转-90,起点从9点钟开始
const singeAngle = 15;
const lineLength = 10;
const lineCount = 225 / 15 + 1;
for (let index = 0; index < lineCount; index++) {
const ponit = {
x: 0,
y: this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 2
};
const arrowPoint = {
x: 0,
y: this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength
}
if (index == 1 || index == 2 || index == 4 || index == 5 || index == 7 || index == 8 ||
index == 10 || index == 11 || index == 13 || index == 14) {
ponit.y = this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength;
arrowPoint.y = this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength;
}
ctx.setStrokeStyle('white');
ctx.setLineWidth(2);
ctx.beginPath();
ctx.moveTo(ponit.x, ponit.y);
ctx.lineTo(arrowPoint.x, arrowPoint.y);
ctx.stroke();
ctx.rotate(singeAngle * Math.PI / 180);
}
ctx.rotate(-(singeAngle * lineCount + 90) * Math.PI / 180); //旋转-singeAngle * lineCount,起点从9点钟开始
//画0 mmHg
const unitStr = 'mmHg';
const onetStr = '0';
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(14);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(onetStr, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 4), 0);
ctx.stroke();
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(10);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(unitStr, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 4) * 1.18, this.data.sexth * 2.5);
ctx.stroke();
//画30 mmHg
const twotStr = '30';
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(14);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(twotStr, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 4) * 0.8, -Math.sin(45) * this.data.radius * 1.1);
ctx.stroke();
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(10);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(unitStr, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength * 4) * 0.91, -Math.sin(45) * this.data.radius * 0.95);
ctx.stroke();
//画60 mmHg
const threetStr = '60';
const threeStrWidth = ctx.measureText(threetStr).width;
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(14);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(threetStr, -threeStrWidth, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength) * 1.2);
ctx.stroke();
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(10);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(unitStr, threeStrWidth / 2, -(this.data.whiteRadius + this.data.subCircleth + this.data.sexth + lineLength) * 1.2);
ctx.stroke();
//画90 mmHg
const fourStr = '90';
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(14);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(fourStr, this.data.radius, -this.data.radius * 0.9);
ctx.stroke();
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(10);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(unitStr, this.data.radius, -this.data.radius * 0.8);
ctx.stroke();
//画120 mmHg
const fiveStr = '120';
const fiveStrWidth = ctx.measureText(fiveStr).width;
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(14);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(fiveStr, this.data.radius * 1.35, 0);
ctx.stroke();
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(10);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(unitStr, this.data.radius * 1.35, fiveStrWidth * 0.65);
ctx.stroke();
//画150 mmHg
const sixStr = '150';
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(14);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(sixStr, this.data.radius * 0.95, this.data.radius * 1.1);
ctx.stroke();
ctx.beginPath();
ctx.setFillStyle('white');
ctx.setFontSize(10);
ctx.setLineWidth(0.5);
ctx.fill();
ctx.fillText(unitStr, this.data.radius * 0.95, this.data.radius * 1.23);
ctx.stroke();
//中间mmHg
ctx.setFontSize(10);
ctx.beginPath();
ctx.setFillStyle('black');
ctx.fillText(unitStr, 0, 0);
ctx.fill();
ctx.stroke();
//实时压力
ctx.setFontSize(15);
const tipStr = '实时压力';
const tipWidth = ctx.measureText(tipStr).width;
ctx.beginPath();
ctx.setFillStyle('black');
ctx.fillText(tipStr, -tipWidth / 2, this.data.radius * 0.2);
ctx.fill();
ctx.stroke();
//圆心
// ctx.setLineWidth(5);
// ctx.beginPath();
// ctx.setStrokeStyle('green');
// ctx.arc(0, 0, 2, 0, 2 * Math.PI);
// ctx.stroke();
ctx.draw();
}
})