进阶版一步一步去实现一个环形进度条

曾经我写过一个简单的环形进度条
本篇文章对进度条再进行一下优化,效果如下

QQ20180228-204416-HD.gif

由图可见,进度加了渐变,文字和进度都加了动画
首先,我们来一步步分解
一个进度条由三个部分组成
1.底部背景线
2.中间文字
3.当前进度

注意事项1

先讲一下大概的思路吧,这个的难点是画渐变,首先,这里面的渐变是由一条条圆弧组成的,每一条圆弧都有自己的颜色,那么我们要平分圆弧和颜色,只要解决了这个,基本上渐变就出来了,这里要注意,不能用canvas自己的渐变,因为线性渐变要么是上下,要么是左右,没有沿着圆弧的,径向渐变就更加不行了,只能由内而外

注意事项2

一般情况下,假如我们用css来定义canvas时,还要给canvas定义宽度和高度,其值为css的两倍,如下所示

<style>
#canvas1{
  width:200px;
  height:100px
}
</style>

那么此时canvas要定义如下

<canvas width=400 height=200 />

但是,现在我们主要做手机端,要兼容各种分辨率,一般我们都是用rem来定义大小,此时就不能直接给canvas赋值width和height了,但是也很简单,假如我们的css这样定义

<style>
#canvas1{
  width:10rem;
  height:5rem
}
</style>

那么可以用js来定义canvas的大小,如下

  canvas.width = canvas.offsetWidth * 2
  canvas.height = canvas.offsetHeight * 2

1.底部背景线

这个比较简单,就是画一个圆即可,大概的代码如下

    ctx.lineWidth = percentageWidth / 2
    ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2 - percentageWidth / 2, 0, Math.PI * 2)
    ctx.strokeStyle = '#00FFFF'
    ctx.stroke()

其中的percentageWidth是指进度的宽度,这里把底部背景线设为了它的一半,另外注意arc中的半径,是canvas宽度的一般减去了进度条宽度的一半,如果不减的话,就超出了canvas了

2.画进度条

从上面我们知道,进度条是由一段一段的圆弧组成的,所以这里的核心就是怎么去分割圆弧,并且拼起来的圆弧的颜色要是渐变的,首先,需要定义一个变量来设置每一个段圆弧的大小,那么就能根据圆弧的起始点和结束点,算出需要画多少条圆弧才能拼起来
首先把一些全局变量列出来,注释就写清楚了

  var canvas = document.getElementById("canvas")
  var ctx = canvas.getContext('2d')
  // 要画的圆弧的百分比,总共为100
  var percentage = 77
  // 每一次加的圆弧大小
  var step = .05
  // 进度条的宽度
  var percentageWidth = 20
  // 结束点的弧度
  var endArc = Math.PI * 2 * percentage / 100 - Math.PI / 2
  // 当前进度的弧度,设置默认值,把当前进度放到12点位置(默认是3点位置)
  var currentArc = -Math.PI / 2
  // 用来做循环的对象
  var intervalObj = null
  // 文字大小,当canvas大小设置之后会再重新设置
  var fontSize = 20
  // 要画的弧度条数,结束弧度减去开始弧度,再除以每一次加的弧度,即可得到
  var times = (endArc - currentArc) / step
  // 调用的第三方算法,正好根据要画的弧度条数来分割
  var gradient = new gradientColor('#008000', '#ff0000', times)
  // 当前在画的那条弧的索引,用来取颜色值
  var index = -1

接下来我们来看怎么一步步去画圆弧的

    // 开启一段新路径
    ctx.beginPath()
    // 设置当前的圆弧的颜色
    ctx.strokeStyle = gradient[++index]
    // 设置圆弧的宽度
    ctx.lineWidth = percentageWidth
    // 记住圆弧开始的弧度
    var startArc = currentArc
    // 算出圆弧结束的角度
    currentArc += step
    // 0.05的偏移量,不加的话没有完全盖住上一个弧,看起来有间隔
    // 画文字
    drawText((currentArc + Math.PI / 2 - 0.05) / (Math.PI * 2))
    // 如果当前结束的弧度大于最后结束的弧度,那么取消动画,并且画出最后的这段弧度
    // 否则继续画下一条弧度
    if (currentArc >= endArc) {
      cancelAnimationFrame(intervalObj)
      ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2 - percentageWidth / 2, startArc, endArc)
      ctx.stroke()
      return
    } else {
      ctx.arc(canvas.width / 2, canvas.height / 2, canvas.height / 2 - percentageWidth / 2, startArc.toFixed(2) - 0.05, currentArc.toFixed(2))
      ctx.stroke()
    }
    // 做动画
    intervalObj = requestAnimationFrame(drawCurrentProgress)

3.画文字

其实,在上一步中,我们已经调用了画文字的方法,但是方法体没有给出,这里主要的思路是,每一次画出当前的百分比,下一次画时,把上一次的文字擦除即可,用到的clearRect和fillText,代码如下

  function drawText(text) {
    // 把文字转换为整数
    text = (text * 100).toFixed(0)
    // 设置字体,其实可以全局设置,没拿出去
    ctx.font = `${fontSize}px serif`
    // 获取文字的宽度,这里加上了百分号
    var textWidth = ctx.measureText(text + '%').width
    // 清除文字,注意这里是根据文字的宽度和文字的大小来计算的,
    // 宽度乘上1.1是为了将擦除区域变得比文字区域大一点
    ctx.clearRect(canvas.width / 2 - textWidth / 2, canvas.height / 2 - fontSize, textWidth * 1.1, fontSize * 2)
    //设置文本的垂直对齐方式
    ctx.textBaseline = 'middle'
    //设置文本的水平对齐方式
    ctx.textAlign = 'center'
    // 填充文字
    ctx.fillText(text + '%', canvas.width / 2, canvas.height / 2)
  }

ok,基本上差不多了
完整代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容