作者:Tusi

周末好,今天给大家带来一款接地气的环形进度条组件 vue-awesome-progress 。近日被设计小姐姐要求实现这么一个环形进度条效果,大体由四部分组成,分别是底色圆环,进度弧,环内文字,进度圆点。设计稿截图如下:




android画廊放大效果_用canvas 画布放大 坐标会改变吗


我的第一反应还是找现成的组件,市面上很多组件都实现了前3点,独独没找到能画进度圆点的组件,不然稍加定制也能复用。既然没有现成的组件,只有自己用 vue + canvas 撸一个了。


android画廊放大效果_android画廊放大效果_02


效果图

先放个效果图,然后再说下具体实现过程,各位看官且听我慢慢道来。


android画廊放大效果_环形进度条_03


安装与使用

源码地址私信,欢迎 star 和提 issue 。

安装

使用

全局注册

局部使用

webpack配置

由于当前版本发布时,未进行 babel 编译,因此使用时需要自行将 vue-awesome-progress 纳入 babel-loader 的解析范围。示例如下:

静态展示

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

确定画布大小

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


android画廊放大效果_环形进度条_04


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

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

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

获取绘图上下文

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

画底色圆环

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

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

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


android画廊放大效果_动画效果_05


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

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

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


android画廊放大效果_渐变色_06


画文字

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


android画廊放大效果_用canvas 画布放大 坐标会改变吗_07


画进度弧

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

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

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


android画廊放大效果_用canvas 画布放大 坐标会改变吗_08


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

画进度圆点

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


android画廊放大效果_动画效果_09


画圆点的代码如下:

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


android画廊放大效果_android画廊放大效果_10


角度自定义

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

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


android画廊放大效果_渐变色_11


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


android画廊放大效果_android画廊放大效果_12


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


android画廊放大效果_android画廊放大效果_13


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


android画廊放大效果_环形进度条_14


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

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

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

最后再补上偏移值即可。


android画廊放大效果_用canvas 画布放大 坐标会改变吗_15


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

动画展示

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

基础动画

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

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

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


android画廊放大效果_动画效果_16


缓动效果

线性动画显得有点单调,可操作性不大,因此我考虑引入贝塞尔缓动函数 easing ,并且支持传入动画执行时间周期 duration ,增强了可定制性,使用体验更好。这里不列出实现代码了,请前往 vue-awesome-progress查看。


android画廊放大效果_用canvas 画布放大 坐标会改变吗_17


可以看到,当传入不同的动画周期 duration 和缓动参数 easing 时,动画效果各异,完全取决于使用者自己。

其他效果

当然根据组件支持的属性,我们也可以定制出其他效果,比如不显示文字,不显示圆点,弧线线宽与圆环线宽一样,不使用渐变色,不需要动画,等等。我们后续也会考虑支持更多能力,比如控制进度,数字动态增长等!具体使用方法,请参考 vue-awesome-progress


android画廊放大效果_动画效果_18


结语

写完这个组件有让我感觉到,程序员最终不是输给了代码和技术的快速迭代,而是输给了自己的逻辑思维能力和数学功底。就 vue-awesome-progress这个组件而言,根据这个思路,我们也能迅速开发出适用于 React , Angular 以及其他框架生态下的组件。工作三年有余,接触了不少框架和技术,经历了 MVVM , Hybrid , 小程序 , 跨平台 , 大前端 , serverless 的大火,也时常感慨“学不动了”,在这个快速演进的代码世界里常常感到失落。好在自己还没有丢掉分析问题的能力,而不仅仅是调用各种 API 和插件,这可能是程序员最宝贵的财富吧。前路坎坷,我辈当不忘初心,愿你出走半生,归来仍是少年!按钮)