用canvas画一个进度盘

简介: 工作的时候收到了UI的设计图,有一个类似进度盘(话说我也不大清楚官方叫法是啥)的效果。。就想起了荒废了好久的canvas。确认过效果,就用canvas来玩一下吧。。

工作的时候收到了UI的设计图,有一个类似进度盘(话说我也不大清楚官方叫法是啥)的效果。。就想起了荒废了好久的canvas。确认过效果,就用canvas来玩一下吧。。

UI分析

UI图如图:

1d246fe86c02bcce2d037cc836e9f82a3f621a21

那么,按层级分析,里面有几个部分:

  1. 空心圆(进度条的背影)
  2. 空心圆(进度条)
  3. 空心圆(实心圆的边框)
  4. 实心圆
  5. 文字(进度)
  6. 文字(最二行)

确认了需要绘制的部分起码有6块。

开始绘制

canvas了解

根据上面的各层级需求,大概可能会使用上的api如下



// 线宽
ctx.lineWidth
// 用于指定结束线帽的样式
ctx.lineCap
// 绘制渐变色
ctx.createLinearGradient
// 指定线条绘图颜色
ctx.strokeStyle
// 指定填充绘图颜色
ctx.fillStyle
// 创建一个圆
ctx.arc()
// 绘制文字
ctx.fillText()
// 指定文字字样
ctx.font
// 指定文字位置
ctx.textAlign


canvas画圆说明

就是ctx.arc(x, y, redius, startAngle, endAngle, counterclockwise)这个方法可以创建一个圆形,接收6个参数,分别为 圆心x坐标,圆心y坐标, 圆半径,起始弧度,结束弧度,是否逆时针绘制



角度=180°×弧度÷π ,弧度=角度×π÷180°

一个完整圆为: 360° × π ÷ 180° = 2 × π

综上,从0开始到 Math.PI*2 为一个圆

圆的绘制路径如图所示

520384b8f25a42558210369d49d140393ab5bd20

更详细可参考: W3school

先声明一个canvas


// HTML部分
<canvas id="canvas"></canvas>
// JS部分
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
canvas.width = Math.ceil(300 / 1920 * window.innerWidth);
canvas.height = Math.ceil(300 / 1920 * window.innerWidth);

创建一个类


class Annulus {

    constructor(obj={}){
        this.size = 100;
        this.lineWidth = 10;
        this.location = obj.location || {x: canvas.width/2, y: canvas.height/2};
    }

    // 绘制进度条底层
    drawBg(){}
    // 绘制进度条
    drawCircleLay(){}
    // 绘制中心的大圆
    drawCenterCircle(){}
    // 绘制大圆边缘的边
    drawCenterBorderCircle(){}
    // 绘制进度文字
    drawTextPercent(){}
    // 绘制进度下面的文字 
    drawTextName(){}
    // 定义动画
    animate(){
        this.drawBg();
        this.drawCircleLay();
        this.drawCenterCircle();
        this.drawCenterBorderCircle();
        this.drawTextPercent();
        this.drawTextName();
    }
    // 执行
    run(){
        this.animate();
    }
}

进度条的背景


drawBg(){
    // 绘制背景圈
    ctx.beginPath();
    ctx.strokeStyle = '#2d4264';
    ctx.lineWidth = 10;
    ctx.lineCap = "round";
    ctx.arc(this.location.x, this.location.y, 100, Math.PI*0.75, Math.PI*2.25, false);
    ctx.stroke();
}

进度条


drawCircleLay(){
    // 绘制进度条
    ctx.beginPath();
    var gradient = ctx.createLinearGradient(0, this.linearLocation().start, 0, this.linearLocation().end);
    gradient.addColorStop(0, '#0f6cd9');
    gradient.addColorStop(1, '#05a6da');
    ctx.strokeStyle = gradient;
    ctx.lineWidth = this.lineWidth;
    ctx.lineCap = "round";
    ctx.arc(this.location.x, this.location.y, this.size, Math.PI*0.75, Math.PI*2.25, false);
    ctx.stroke();
}

linearLocation(){
    // 设定渐变背影的起始结束点
    let start = this.location.y - ((this.size-15)*2 + this.lineWidth)/2;
    let end = start + (this.size-15)*2 + this.lineWidth
    return {start: start, end: end}   
}

目前基本上跟背景圈一样,不同的是这里用了渐变色,先绘制出来,后再给加上变量变化

中心圆


drawCenterCircle(){
    // 绘制中心圆
    ctx.beginPath();
    var gradient = ctx.createLinearGradient(0, this.linearLocation().start, 0, this.linearLocation().end);
    gradient.addColorStop(0, '#39a8ce');
    gradient.addColorStop(1, '#5647c9');
    ctx.fillStyle = gradient;
    ctx.arc(this.location.x, this.location.y, this.size-15, 0, Math.PI*2, false);
    ctx.fill();
}

中心圆的边


drawCenterBorderCircle(){
    // 绘制中心圆周边的那圈
    ctx.beginPath();
    ctx.strokeStyle = 'rgba(0,0,0,0.3)';
    ctx.lineWidth = 10;
    ctx.arc(this.location.x, this.location.y, this.size-20, 0, Math.PI*2, false);
    ctx.stroke();
}

绘制进度


drawTextPercent(percent){
    // 绘制进度文字
    ctx.beginPath();
    ctx.font = '31px Arial';
    ctx.textAlign="center";
    ctx.fillStyle="#192f47";
    ctx.fillText(`100%`, this.location.x, this.location.y);
    ctx.stroke();
}


绘制进度下面的文字


drawTextName(){
    // 绘制二级文字
    ctx.beginPath();
    ctx.font = '14px "Microsoft YaHei"';
    ctx.textAlign="center";
    ctx.fillStyle="#192f47";
    ctx.fillText('text', this.location.x, this.location.y+25);
    ctx.stroke();
}

让它运动

接下来我们可以让进度条动起来。
运动通用的有几个点:

  1. 速度speed
  2. 经过的路程,在这就是角度degree,因为是进度条,总量就是100, 这里的圈的角度是 Math.PI*2.25 - Math.PI*0.75 = Math.PI * 1.75。那么1%的角度就是 Math.PI*.1.5 / 100
  3. 我们还需要有一个变量来记录当前运动到的百分比 tol

综上,我们把 类的 constructor 改造一下,如下:



constructor(obj = {}){
    /*
    * speed -- 速度
    * color -- 颜色
    * size -- 大小
    * lineWidth -- 线宽
    * location -- 圆心位置
    * text -- 文字
    * value -- 圆环滚动的值 ,这里指是百分比
    */
    this.speed = obj.speed || 0.1;
    this.color = obj.color || '#ffeedd';
    // 180 是UI的大体尺寸是在180px,以1920为基准
    this.size = Math.ceil((obj.size || 80) * (canvas.width / 180));
    this.lineWidth = obj.lineWidth || 10;
    this.location = obj.location || {x: canvas.width/2, y: canvas.height/2};
    this.textName = obj.text || '第二行文字title';
    this.value = obj.value || 0;
    // 这里是圆的终点减去圆的起点
    this.degree = Math.PI*1.5/100;
    this.animate = this.animate.bind(this);
    this.tol = 0;
}


drawCircleLay() 方法的绘制结束点使用变量来控制

drawCircleLay(){
    // 绘制进度条
    if (this.value == 0) return;
    ctx.beginPath();
    var gradient = ctx.createLinearGradient(0, this.linearLocation().start, 0, this.linearLocation().end);
    gradient.addColorStop(0, '#0f6cd9');
    gradient.addColorStop(1, '#05a6da');
    ctx.strokeStyle = gradient;
    ctx.lineWidth = this.lineWidth;
    ctx.lineCap = "round";
    //这里改成用变量来控制 
    ctx.arc(this.location.x, this.location.y, this.size, Math.PI * 0.75, Math.PI*0.75+this.tol * this.degree, false);
    ctx.stroke();
}

drawTextPercent()  可以把变量 tol 传进来作用当前显示的进度数

drawTextPercent(percent){
    // 绘制进度文字
    ctx.beginPath();
    ctx.font = `${this.size / 2.5}px Arial`;
    ctx.textAlign="center";
    ctx.fillStyle="#192f47";
    ctx.fillText(`${parseInt(percent)}%`, this.location.x, this.location.y);
    ctx.stroke();
}

animate()  方法加上 RAF, 让其运动

animate(){
    window.requestAnimationFrame(this.animate);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.drawBg();
    this.drawCircleLay();
    this.drawCenterCircle();
    this.drawCenterBorderCircle();
    this.drawTextPercent(this.tol);
    this.drawTextName(this.textName);
    if (this.tol < this.value) { this.tol += this.speed }
}

至此,一个进度盘已完成。其实还有很多改进空间,比如更合理的封装,监听window.resize的时候进行大小的改变等。。

实例地址: codepen


原文发布时间为:2018年06月17日
作者: Su
本文来源:  掘金  如需转载请联系原作者
相关文章
|
前端开发 小程序
小程序使用canvas制作beas64图片
小程序使用canvas制作beas64图片
97 0
C盘满了,清理C盘常用步骤!
真正的深度清理c盘 放心,大胆的删除! 第一步 删除临时文件     1.win+R调出命令框     2.输入%temp% 点击确定
|
5月前
|
C# C++
C# 自定义时间进度条
本文作者通过参考leslie_xin的一篇文章,成功创建了一个自定义的WinForms控件——时间进度条,该控件带有时间刻度和多种可定制的属性,如颜色、时间间隔等。作者在控件中加入了开始和结束时间,以及自适应的时间刻度间隔。控件能根据设置显示时间标签,并提供了事件处理,如值改变时的触发。代码中包含了计算时间刻度、绘制刻度线和时间标签的逻辑。作者强调了避免循环调用事件、使用OnXXX()形式的事件处理函数以及注意自定义控件中的属性和事件设计。
132 7
|
6月前
|
前端开发 小程序
微信小程序canvas画布绘制;canvas画布图片保存
微信小程序canvas画布绘制;canvas画布图片保存
89 0
|
前端开发 索引
Canvas画布撤销与前进实现方案
Canvas画布撤销与前进实现方案
423 0
Canvas画布撤销与前进实现方案
|
前端开发 小程序
微信小程序canvas绘制完毕后如何保存
微信小程序canvas绘制完毕后如何保存
114 0
|
前端开发 容器
使用resize实现强大的图片拖拽切换预览功能
本文将介绍一个非常有意思的功能,使用纯 CSS 利用 resize 实现强大的图片切换预览功能。类似于这样:
|
存储 Web App开发 编解码
分享一个canvas录屏方案
对比Web开发和原生开发,通常APP被说到的优势在于两点:1.交互体验更流畅。2.硬件支持更友好。随着HTML5的发展,我们可以通过浏览器做的事情会越来越多,之前只能原生开发做的事情通过浏览器也能实现
从一个圆环进度功能来学习SVG
从一个圆环进度功能来学习SVG
154 0
从一个圆环进度功能来学习SVG
|
前端开发 Android开发
Canvas的保存和恢复的demo
Canvas的保存和恢复的demo