用初中数学知识撸一个canvas环形进度条

任何事都不是一蹴而就的,我们首先来实现一个静态的效果,然后再实现动画效果,甚至是复杂的控制逻辑。

确定画布大小


第一步是确定画布大小。从设计稿我们可以直观地看到,整个环形进度条的最外围是由进度圆点确定的,而进度圆点的圆心在圆环圆周上。

环形进度条模型

因此我们得出伪代码如下:

// canvasSize: canvas宽度/高度

// outerRadius: 外围半径

// pointRadius: 圆点半径

// pointRadius: 圆环半径

canvasSize = 2 * outerRadius = 2 * (pointRadius + circleRadius)

据此我们可以定义如下组件属性:

props: {

circleRadius: {

type: Number,

default: 40

},

pointRadius: {

type: Number,

default: 6

}

},

computed: {

// 外围半径

outerRadius() {

return this.circleRadius + this.pointRadius

},

// canvas宽/高

canvasSize() {

return 2 * this.outerRadius + ‘px’

}

}

那么canvas大小也可以先进行绑定了

获取绘图上下文


getContext('2d')方法返回一个用于在canvas上绘图的环境,支持一系列2d绘图API

mounted() {

// 在$nextTick初始化画布,不然dom还未渲染好

this.$nextTick(() => {

this.initCanvas()

})

},

methods: {

initCanvas() {

var canvas = this.$refs.canvasDemo;

var ctx = canvas.getContext(‘2d’);

}

}

画底色圆环


完成了上述步骤后,我们就可以着手画各个元素了。我们先画圆环,这时我们还要定义两个属性,分别是圆环线宽circleWidth和圆环颜色circleColor

circleWidth: {

type: Number,

default: 2

},

circleColor: {

type: String,

default: ‘#3B77E3’

}

canvas提供的画圆弧的方法是ctx.arc(),需要提供圆心坐标,半径,起止弧度,是否逆时针等参数。

ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

我们知道,Web网页中的坐标系是这样的,从绝对定位的设置上其实就能看出来(topleft设置正负值会发生什么变化),而且原点(0, 0)是在盒子(比如说canvas)的左上角哦。

web坐标系

对于角度而言,x轴正向,默认是顺时针方向旋转。

圆环的圆心就是canvas的中心,所以x, youterRadius的值就可以了。

ctx.strokeStyle = this.circleColor;

ctx.lineWidth = this.circleWidth;

ctx.beginPath();

ctx.arc(this.outerRadius, this.outerRadius, this.circleRadius, 0, this.deg2Arc(360));

ctx.stroke();

注意arc传的是弧度参数,而不是我们常理解的360°这种概念,因此我们需要将我们理解的360°转为弧度。

// deg转弧度

deg2Arc(deg) {

return deg / 180 * Math.PI

}

画圆环

画文字


调用fillText绘制文字,利用canvas.clientWidth / 2canvas.clientWidth / 2取得中点坐标,结合控制文字对齐的两个属性textAligntextBaseline,我们可以将文字绘制在画布中央。文字的值由label属性接收,字体大小由fontSize属性接收,颜色则取的fontColor

if (this.label) {

ctx.font = ${this.fontSize}px Arial,"Microsoft YaHei"

ctx.fillStyle = this.fontColor;

ctx.textAlign = ‘center’

ctx.textBaseline = ‘middle’

ctx.fillText(this.label, canvas.clientWidth / 2, canvas.clientWidth / 2);

}

画文字

画进度弧


支持普通颜色和渐变色,withGradient默认为true,代表使用渐变色绘制进度弧,渐变方向我默认给的从上到下。如果希望使用普通颜色,withGradientfalse即可,并可以通过lineColor自定义颜色。

if (this.withGradient) {

this.gradient = ctx.createLinearGradient(this.circleRadius, 0, this.circleRadius, this.circleRadius * 2);

this.lineColorStops.forEach(item => {

this.gradient.addColorStop(item.percent, item.color);

});

}

其中lineColorStops是渐变色的颜色偏移断点,由父组件传入,可传入任意个颜色断点,格式如下:

colorStops2: [

{ percent: 0, color: ‘#FF9933’ },

{ percent: 1, color: ‘#FF4949’ }

]

画一条从上到下的进度弧,即270°90°

ctx.strokeStyle = this.withGradient ? this.gradient : this.lineColor;

ctx.lineWidth = this.lineWidth;

ctx.beginPath();

ctx.arc(this.outerRadius, this.outerRadius, this.circleRadius, this.deg2Arc(270), this.deg2Arc(90));

ctx.stroke();

画进度弧

其中lineWidth是弧线的宽度,由父组件传入

lineWidth: {

type: Number,

default: 8

}

画进度圆点


最后我们需要把进度圆点补上,我们先写死一个角度90°,显而易见,圆点坐标为(this.outerRadius, this.outerRadius + this.circleRadius)

90度圆点坐标

画圆点的代码如下:

ctx.fillStyle = this.pointColor;

ctx.beginPath();

ctx.arc(this.outerRadius, this.outerRadius + this.circleRadius, this.pointRadius, 0, this.deg2Arc(360));

ctx.fill();

其中pointRadius是圆点的半径,由父组件传入:

pointRadius: {

type: Number,

default: 6

}

90度画圆点

角度自定义


当然,进度条的角度是灵活定义的,包括开始角度,结束角度,都应该由调用者随意给出。因此我们再定义一个属性angleRange,用于接收起止角度。

angleRange: {

type: Array,

default: function() {

return [270, 90]

}

}

有了这个属性,我们就可以随意地画进度弧和圆点了,哈哈哈哈。

等等,你忘了这个场景

老哥,这种圆点坐标怎么求?

特殊角度怎么求圆点圆心坐标

噗…看来高兴过早了,最重要的是根据不同角度求得圆点的圆心坐标,这让我顿时犯了难。

你要冷静

经过冷静思考,我脑子里闪过了一个利用正余弦公式求坐标的思路,但前提是坐标系原点如果在圆环外接矩形的左上角才好算。仔细想想,冇问题啦,我先给坐标系平移一下,最后求出来结果,再补个平移差值不就行了嘛。

平移坐标系后求圆点坐标

👆画图工具不是很熟练,这里图没画好,线歪了,请忽略细节。

好的,我们先给坐标系向右下方平移pointRadius,最后求得结果再加上pointRadius就好了。伪代码如下:

// realx:真实的x坐标

// realy:真实的y坐标

// resultx:平移后求取的x坐标

// resultx:平移后求取的y坐标

// pointRadius 圆点半径

realx = resultx + pointRadius

realy = resulty + pointRadius

求解坐标的思路大概如下,分四个范围判断,得出求解公式,应该还可以化简,不过我数学太菜了,先这样吧。

getPositionsByDeg(deg) {

let x = 0;

let y = 0;

if (deg >= 0 && deg <= 90) {

// 0~90度

x = this.circleRadius * (1 + Math.cos(this.deg2Arc(deg)))

y = this.circleRadius * (1 + Math.sin(this.deg2Arc(deg)))

} else if (deg > 90 && deg <= 180) {

// 90~180度

x = this.circleRadius * (1 - Math.cos(this.deg2Arc(180 - deg)))

y = this.circleRadius * (1 + Math.sin(this.deg2Arc(180 - deg)))

} else if (deg > 180 && deg <= 270) {

// 180~270度

x = this.circleRadius * (1 - Math.sin(this.deg2Arc(270 - deg)))

y = this.circleRadius * (1 - Math.cos(this.deg2Arc(270 - deg)))

} else {

// 270~360度

x = this.circleRadius * (1 + Math.cos(this.deg2Arc(360 - deg)))

y = this.circleRadius * (1 - Math.sin(this.deg2Arc(360 - deg)))

}

return { x, y }

}

最后再补上偏移值即可。

const pointPosition = this.getPositionsByDeg(nextDeg);

ctx.arc(pointPosition.x + this.pointRadius, pointPosition.y + this.pointRadius, this.pointRadius, 0, this.deg2Arc(360));

 任意角度画弧线和圆点

这样,一个基本的canvas环形进度条就成型了。

动画展示

=======================================================================

静态的东西逼格自然是不够的,因此我们需要再搞点动画效果装装逼。

基础动画


我们先简单实现一个线性的动画效果。基本思路是把开始角度和结束角度的差值分为N段,利用window.requestAnimationFrame依次执行动画。

比如从30°90°,我给它分为6段,每次画10°。要注意canvas画这种动画过程一般是要重复地清空画布并重绘的,所以第一次我画的弧线范围就是30°~40°,第二次我画的弧线范围就是30°~50°,以此类推…

基本的代码结构如下,具体代码请参考vue-awesome-progress v1.1.0版本,如果顺手帮忙点个star也是极好的。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

资料领取方式:戳这里获取

道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-lT1u5Jfp-1713373166298)]

[外链图片转存中…(img-u5XBypLu-1713373166298)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-nJCTagcY-1713373166299)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

资料领取方式:戳这里获取

html5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值