效果如图
image.png方案背景
需求
1.圆环需要呈现3/4进度条
2.小球跟随数字大小而准确落在正确的位置
3.可以配置舒适范围的颜色
4.当超出舒适范围变为红色,超出剩余部分变为红色
对应方案
1.用canvas实现3/4圆环
2.利用坐标系计算出小球的位置
3.利用容器百分比实现自适应
4.对应颜色范围使用弧长公式计算
代码实现
在canvas上面定义一个父容器,canvas使用百分比inline-block来达到自适应,
父容器可以使用vw,em等自适应单位就可以达到自适应效果,我这里已经做了全局转换为vw
<template>
<div class="canvas">
<canvas :id="id"
style="width:100%;height:100%;display: inline-block;"></canvas>
</div>
</template>
<style lang="scss" scoped>
.canvas {
display: block;
margin: 0 auto;
/*margin-top: 10px;*/
width: 150px;
height: 75px;
}
</style>
定义data和props来接受父组件配置的数据和定义弧长半径数值等
data() {
return {
// num: 0,
canvas: '',
context: '',
cirX: '',
cirY: '',
rad: '',
n: 1,
speed: 150,
r: 24
}
},
//percents 范围开始值 percente 范围结束值 num数值 WarningColor错误颜色 ballColor小球颜色 ratioColor 外圈范围值颜色 OuterColor
props: ['percents', 'ratioColor', 'id','percente','num','WarningColor',"ballColor","OuterColor"],
mounted() {
this.canvas = document.getElementById(this.id)
this.context = this.canvas.getContext('2d')
this.context.scale(2, 2);
(this.cirX = 30), //this.canvas.width/ 2
(this.cirY = 30), //this.canvas.height/ 2
(this.rad = (Math.PI * 2*0.75) / 100)
this.DreamLoading()
},
绘制圆弧方法入口
其实这是一个不断重复绘制,requestAnimationFrame用作于动画浏览器会自动作用最佳的动画时间间隔。每次移动0.4来达到小球运动的效果
DreamLoading() {
// console.log(this.n)
//清除所有,重新绘制
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.writeCircle()
this.writeText(this.n)
if(this.num < this.percents || this.num > this.percente) {
this.writeWarningColor()
}
if(this.num >= this.percents && this.num <= this.percente) {
this.writeBlueS()
}
this.writeSmallCircle(this.n)
// if(this.n < 100 && this.n <= 60){
var count = this.num
if (this.n <= count) {
this.n = this.n + 0.4
} else {
return (this.n = 0)
}
requestAnimationFrame(this.DreamLoading)
}
绘制外围的3/4圈,从1/4Π开始到7/4Π就是一个3/4圆(一个圆是2Π也就是8/4Π)
writeCircle() {
this.context.save() //save和restore可以保证样式属性只运用于该段canvas元素
this.context.beginPath() //开始路径
this.context.strokeStyle = this.OuterColor //设置边线的颜色
this.context.lineWidth = 4
this.context.arc(
this.cirX,
this.cirY,
this.r,
Math.PI * 0.75,
Math.PI * 2.25,
false
) //画一个3/4圆的路径
this.context.stroke() //绘制边线
// this.context.restore();
// this.context.closePath();
},
绘制舒适范围值要加上弧长即可
writeBlueS() {
this.context.save()
this.context.strokeStyle = this.ratioColor
this.context.lineWidth = 4
this.context.beginPath()
this.context.arc(
this.cirX,
this.cirY,
this.r,
Math.PI * 0.75 + this.percents*this.rad,
Math.PI * 0.75+ this.percente*this.rad,
false
) // 4/3处开始画
this.context.stroke()
this.context.restore()
},
绘制警告范围值也是同样
writeWarningColor() {
this.context.save()
this.context.strokeStyle = this.WarningColor
this.context.lineWidth = 4
this.context.beginPath()
this.context.arc(
this.cirX,
this.cirY,
this.r,
Math.PI * 0.75 + this.percente*this.rad,
Math.PI * 2.25,
false
) // 4/3处开始画
this.context.stroke()
this.context.restore()
},
绘制文本,付按揭是计算好圆心的位置
writeText(n) {
this.context.save()
this.context.font = '30px Arial'
this.context.fillStyle = 'rgba(255,255,255,1)' //字体颜色
this.context.fillText(n.toFixed(0), this.cirX-18, this.cirY + 10) //绘制实心
//context.strokeStyle = "#49f";
// context.strokeText(n.toFixed(0)+"%",cirX - 30 ,cirY +10); //绘制空心
this.context.stroke()
this.context.restore()
},
绘制小球和小球运动轨迹 关键在于这个xpos ypos的坐标公式
writeSmallCircle(n) {
this.context.save()
this.context.fillStyle = this.ballColor;
// this.context.lineWidth = 12;
let angle =2.25 * Math.PI - this.rad * n;
let xPos =- Math.cos(angle) * (this.r) + 58/2;
let yPos = Math.sin(angle) * (this.r) + 58/2;
this.context.beginPath()
this.context.arc(xPos,yPos, 5, 0,Math.PI * 2, true) // 4/3处开始画
this.context.fill()
this.context.restore()
//画小球的边框
this.context.save()
this.context.lineWidth = 1
this.context.strokeStyle ='rgba(255,255,255,1)';
this.context.beginPath()
this.context.arc(xPos,yPos, 5, 0,Math.PI * 2, true) // 4/3处开始画
this.context.stroke()
this.context.restore()
},
组件用法
引入组件
import canvasCircle from "./circle.vue";
components: {
canvasCircle,
},
data里面定义
cicleList: [
{
Number:'26',
startRadio: "12", //舒适范围值
endRadio: "46",
ballColor:"rgba(208, 232, 254, 1)",
OuterColor:'rgba(208, 232, 254, 1)',
ratioColor: "rgba(68, 163, 252, 1)" //外圈颜色
}
],
页面使用
<canvas-circle
v-for="(item, index) in cicleList"
:key="index"
:ballColor="item.ballColor"
:num="item.Number"
:percents="item.startRadio"
:percente="item.endRadio"
:OuterColor="item.OuterColor"
:ratioColor="item.ratioColor"
:id="index + 'circle'"
></canvas-circle>
网友评论