h5 Canvas实现圆形时间倒计时进度条_小程序圆环进度条倒计时-程序员宅基地

技术标签: canvas  html5  JavaScript  HTML  html  javascript  HTML5进阶学习  

在项目中,我们会遇到需要显示进度条或者倒计时的功能,我们今天就来实现一下。

一、效果展示

在这里插入图片描述
实现效果要求:
1.环形倒计时
2.能够根据总时间和当前时间进行比例性的倒计时
3.进度条环形能够有颜色渐变效果
4.中间文字能够有颜色渐变效果

二、准备文件

在开发canvas程序时,我们通常需要准备静态html和需要引用的js文件即可,这次我们使用的静态html模板如下:
1.html页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" /> -->
    <title>圆形倒计时</title>
  </head>
  <body>
    <div class="clock-show" style="width: 800px;height: 700px">
        <canvas id="canvas" width="800" height="700"></canvas>
      </div>
  </body>

  <script src="./index.js"></script>
</html>

2.js文件-需要操作canvas标签的代码
我们先定义好需要去实现的方法,分别如下面代码

function CircleClock(canvas) {
    
  // 定义一个CircleClock构造函数,用于初始化 
}

CircleClock.prototype.setProgress = function (progress) {
    
    // 用于设置从外部设置进度
};

CircleClock.prototype.getProgress = function () {
    
    // 用于获取进度
};

CircleClock.prototype.drawBackgroundCircle = function () {
    
    // 画圆形的背景环形条
};

CircleClock.prototype.drawCurrentProgressCircle = function () {
    
  // 绘制倒计时环形进度条
};

CircleClock.prototype.createLinearGradientByTime = function () {
    
    // 按照进度,计算渐变色
};

CircleClock.prototype.drawTimeText = function () {
    
    // 绘制环中间文字倒计时
};

CircleClock.prototype.clear = function () {
    
    // 清理canvas
};

CircleClock.prototype.setTime = function (seconds) {
    
    // 设置初始时间,需要倒计时的时间
};

CircleClock.prototype.setCurrentTime = function (currentSeconds) {
    
    // 实时同步当前剩下的时间
};

CircleClock.prototype.run = function (seconds, endCallback) {
    
    // 开始运行项目,运行时传入初始时间和回调函数
};

CircleClock.prototype.update = function (unPass) {
    
  this.setCurrentTime(unPass);
  this.clear();
  this.drawBackgroundCircle();
  this.drawCurrentProgressCircle();
  this.drawTimeText();
};

const circleClock = new CircleClock("canvas");
circleClock.run(30, () => {
    
  console.log("倒计时执行完毕");
});
三、代码方法说明
1.构造函数
function CircleClock(canvas) {
    
  // 定义一个CircleClock构造函数,用于初始化 
  this.canvas = document.querySelector(canvas);
  this.context = this.canvas.getContext("2d");
  this.width = 800;
  this.height = 700;
  this.progress = 0;  // 用于记录当前的进度
  this.seconds = 0; // 需要倒计时的时间,秒
  this.currentSeconds = 0; // 当前倒计时到的位置
}
2.设置进度和获取进度方法
CircleClock.prototype.setProgress = function (progress) {
    
    // 用于设置从外部设置进度
  this.progress = progress;
};
CircleClock.prototype.getProgress = function () {
    
    // 用于获取进度
  return this.progress;
};
3.绘制圆形进度背景环
CircleClock.prototype.drawBackgroundCircle = function () {
    
    // 画圆形的背景环形条
  const x = this.width / 2;
  const y = this.height / 2;
  this.context.strokeStyle = "#40C4FF";
  this.context.lineWidth = 16;
  this.context.beginPath();
  // 绘制圆形背景。从弧度-(Math.PI / 2)到2 * Math.PI
  this.context.arc(x, y, this.width / 5, -(Math.PI / 2), 2 * Math.PI);
  this.context.stroke();
};
4.绘制当前进度环
CircleClock.prototype.drawCurrentProgressCircle = function () {
    
  const x = this.width / 2;
  const y = this.height / 2;
  // 绘制倒计时环形进度条
  const canvasGradient = this.context.createLinearGradient(
    0,
    0,
    0,
    this.height
  );
  // 在offset为0的位置(即起点位置)添加一个蓝色的渐变
  canvasGradient.addColorStop(0, "#00BCD4");
  // 在offset为0.4的位置(线段左起20%的位置)添加一个绿色的渐变
  canvasGradient.addColorStop(0.4, "#7C4DFF");
  // 在offset为0.8的位置(即终点位置)添加一个红色的渐变
  canvasGradient.addColorStop(0.8, "#DCE775");
  // 在offset为1的位置(即终点位置)添加一个红色的渐变
  canvasGradient.addColorStop(1, "#FF5722");
  // 将strokeStyle的属性值设为该CanvasGradient对象
  this.context.strokeStyle = canvasGradient;
  // 计算进度
  const progress = 1 - this.currentSeconds / this.seconds;
  this.setProgress(progress);
  // - (Math.PI/2),  (progress) *(3/2 *Math.PI)   [-0.5, 1.5]-[0, 1],
  this.context.lineWidth = 16;
  this.context.lineCap = "round";
  this.context.beginPath();
  // 绘制圆形进度环
  this.context.arc(
    x,
    y,
    this.width / 5,
    -(Math.PI / 2),
    (-0.5 + 2 * progress) * Math.PI
  );
  this.context.stroke();
};
5.根据时间或者进度创建渐变色
CircleClock.prototype.createLinearGradientByTime = function () {
    
    // 按照进度,计算渐变色
  const progress = this.getProgress();
  // 修改填充颜色
  const canvasGradient = this.context.createLinearGradient(
    this.width / 2 - 18,
    this.height / 2 - 18,
    this.width / 2,
    this.height / 2 + 50
  );
  canvasGradient.addColorStop(0, "#00BCD4");
  progress > 0 && progress < 0.4 && canvasGradient.addColorStop(0.3, "#7C4DFF");

  progress > 0.4 &&
    progress < 0.8 &&
    canvasGradient.addColorStop(0.6, "#DCE775");
  progress > 0.6 &&
    progress < 0.9 &&
    canvasGradient.addColorStop(0.8, "#EEFF41");
  canvasGradient.addColorStop(1, "#FF5722");
  return canvasGradient;
};
6.绘制中间的倒计时文本
CircleClock.prototype.drawTimeText = function () {
    
    // 绘制环中间文字倒计时
  this.context.fillStyle = this.createLinearGradientByTime();
  this.context.textAlign = "start";
  this.context.font = "36px bold";
  this.context.textBaseline = "alphabetic";
  let s = parseInt(this.currentSeconds);
  let ss = parseInt(s % 60);
  let mm = parseInt(s / 60);
  const text = `${
      mm.toString().padStart(2, 0)}: ${
      ss
    .toString()
    .padStart(2, 0)}`;
    // 计算文本长度,适配位置
  const textWidth = this.context.measureText(text).width;
  this.context.fillText(
    text,
    this.width / 2 - textWidth / 2,
    this.height / 2 + 18
  );
};

7.清理canvas
CircleClock.prototype.clear = function () {
    
    // 清理canvas
  this.context.clearRect(0, 0, this.width, this.height); //每改变一次动画就要对这个空间范围进行一次刷新,不然会重叠在一起
};
8.设置初始倒计时时间
CircleClock.prototype.setTime = function (seconds) {
    
    // 设置初始时间,需要倒计时的时间
  this.seconds = seconds;
};
9.设置当前已经倒计时的时间点
CircleClock.prototype.setCurrentTime = function (currentSeconds) {
    
    // 实时同步当前剩下的时间
  this.currentSeconds = currentSeconds;
};
10.开始运行项目

运行项目,传入初始时间和回调函数,这里会不停的反复运行,如果需要停止,可以通过打开clearInterval(intervalTime);代码进行关闭计时器

CircleClock.prototype.run = function (seconds, endCallback) {
    
    // 开始运行项目,运行时传入初始时间和回调函数
  let count = 0;
  const intervalTime = setInterval(() => {
    
    this.setTime(seconds);
    const allTime = this.seconds;
    const unPass = allTime - count;
    count = count + 1;
    console.log("unPass", unPass);
    if (unPass < 0) {
    
      //   clearInterval(intervalTime);
      this.setTime(30);
      count = 0;
      endCallback && endCallback();
    } else {
    
      this.update(unPass);
    }
  }, 1000);
};
11.更新时间

用于更新当前已经倒计时倒计到的时间,相当于重新绘制整个界面

CircleClock.prototype.update = function (unPass) {
    
  this.setCurrentTime(unPass);
  this.clear();
  this.drawBackgroundCircle();
  this.drawCurrentProgressCircle();
  this.drawTimeText();
};
12.初始化,运行代码

const circleClock = new CircleClock("canvas");
circleClock.run(30, () => {
    
  console.log("倒计时执行完毕");
});

上面是所有代码的分布解析,下面给出完整代码

四、完整代码

1.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" /> -->
    <title>圆形倒计时</title>
  </head>
  <body>
    <div class="clock-show" style="width: 800px;height: 700px">
        <canvas id="canvas" width="800" height="700"></canvas>
      </div>
  </body>

  <script src="./index.js"></script>
</html>

2.js

function CircleClock(canvas) {
    
  // 定义一个CircleClock构造函数,用于初始化 
  this.canvas = document.querySelector(canvas);
  this.context = this.canvas.getContext("2d");
  this.width = 800;
  this.height = 700;
  this.progress = 0;  // 用于记录当前的进度
  this.seconds = 0; // 需要倒计时的时间,秒
  this.currentSeconds = 0; // 当前倒计时到的位置
}

CircleClock.prototype.setProgress = function (progress) {
    
    // 用于设置从外部设置进度
  this.progress = progress;
};

CircleClock.prototype.getProgress = function () {
    
    // 用于获取进度
  return this.progress;
};

CircleClock.prototype.drawBackgroundCircle  = function () {
    
    // 画圆形的背景环形条
  const x = this.width / 2;
  const y = this.height / 2;
  this.context.strokeStyle = "#40C4FF";
  this.context.lineWidth = 16;
  this.context.beginPath();
  // 绘制圆形背景。从弧度-(Math.PI / 2)到2 * Math.PI
  this.context.arc(x, y, this.width / 5, -(Math.PI / 2), 2 * Math.PI);
  this.context.stroke();
};

CircleClock.prototype.drawCurrentProgressCircle = function () {
    
  const x = this.width / 2;
  const y = this.height / 2;
  // 绘制倒计时环形进度条
  const canvasGradient = this.context.createLinearGradient(
    0,
    0,
    0,
    this.height
  );
  // 在offset为0的位置(即起点位置)添加一个蓝色的渐变
  canvasGradient.addColorStop(0, "#00BCD4");
  // 在offset为0.4的位置(线段左起20%的位置)添加一个绿色的渐变
  canvasGradient.addColorStop(0.4, "#7C4DFF");
  // 在offset为0.8的位置(即终点位置)添加一个红色的渐变
  canvasGradient.addColorStop(0.8, "#DCE775");
  // 在offset为1的位置(即终点位置)添加一个红色的渐变
  canvasGradient.addColorStop(1, "#FF5722");
  // 将strokeStyle的属性值设为该CanvasGradient对象

  this.context.strokeStyle = canvasGradient;

  // 计算进度
  const progress = 1 - this.currentSeconds / this.seconds;
  this.setProgress(progress);
  // - (Math.PI/2),  (progress) *(3/2 *Math.PI)   [-0.5, 1.5]-[0, 1],
  this.context.lineWidth = 16;
  this.context.lineCap = "round";
  this.context.beginPath();
  // 绘制圆形进度环
  this.context.arc(
    x,
    y,
    this.width / 5,
    -(Math.PI / 2),
    (-0.5 + 2 * progress) * Math.PI
  );
  this.context.stroke();
};

CircleClock.prototype.createLinearGradientByTime = function () {
    
    // 按照进度,计算渐变色
  const progress = this.getProgress();
  // 修改填充颜色
  const canvasGradient = this.context.createLinearGradient(
    this.width / 2 - 18,
    this.height / 2 - 18,
    this.width / 2,
    this.height / 2 + 50
  );

  canvasGradient.addColorStop(0, "#00BCD4");
  progress > 0 && progress < 0.4 && canvasGradient.addColorStop(0.3, "#7C4DFF");

  progress > 0.4 &&
    progress < 0.8 &&
    canvasGradient.addColorStop(0.6, "#DCE775");
  progress > 0.6 &&
    progress < 0.9 &&
    canvasGradient.addColorStop(0.8, "#EEFF41");
  canvasGradient.addColorStop(1, "#FF5722");
  return canvasGradient;
};

CircleClock.prototype.drawTimeText = function () {
    
    // 绘制环中间文字倒计时
  this.context.fillStyle = this.createLinearGradientByTime();
  this.context.textAlign = "start";
  this.context.font = "36px bold";
  this.context.textBaseline = "alphabetic";
  let s = parseInt(this.currentSeconds);
  let ss = parseInt(s % 60);
  let mm = parseInt(s / 60);
  const text = `${
      mm.toString().padStart(2, 0)}: ${
      ss
    .toString()
    .padStart(2, 0)}`;
    // 计算文本长度,适配位置
  const textWidth = this.context.measureText(text).width;
  this.context.fillText(
    text,
    this.width / 2 - textWidth / 2,
    this.height / 2 + 18
  );
};

CircleClock.prototype.clear = function () {
    
    // 清理canvas
  this.context.clearRect(0, 0, this.width, this.height); //每改变一次动画就要对这个空间范围进行一次刷新,不然会重叠在一起
};

CircleClock.prototype.setTime = function (seconds) {
    
    // 设置初始时间,需要倒计时的时间
  this.seconds = seconds;
};

CircleClock.prototype.setCurrentTime = function (currentSeconds) {
    
    // 实时同步当前剩下的时间
  this.currentSeconds = currentSeconds;
};

CircleClock.prototype.run = function (seconds, endCallback) {
    
    // 开始运行项目,运行时传入初始时间和回调函数
  let count = 0;
  const intervalTime = setInterval(() => {
    
    this.setTime(seconds);
    const allTime = this.seconds;
    const unPass = allTime - count;
    count = count + 1;
    console.log("unPass", unPass);
    if (unPass < 0) {
    
      //   clearInterval(intervalTime);
      this.setTime(30);
      count = 0;
      endCallback && endCallback();
    } else {
    
      this.update(unPass);
    }
  }, 1000);
};

CircleClock.prototype.update = function (unPass) {
    
  this.setCurrentTime(unPass);
  this.clear();
  this.drawBackgroundCircle();
  this.drawCurrentProgressCircle();
  this.drawTimeText();
};

const circleClock = new CircleClock("canvas");
circleClock.run(30, () => {
    
  console.log("倒计时执行完毕");
});

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/suwu150/article/details/115032260

智能推荐

python api调用百度ai平台_百度ai开放平台使用方法(附带详细案例步骤)-程序员宅基地

文章浏览阅读1.2k次。百度ai开放平台1.百度ai开放平台内有众多功能,如文字识别,语音技术等等内容,本文章以身份证识别为例子,教大家怎么使用它啦链接走起:https://cloud.baidu.com/?from=console身份证识别1.点开链接,即可看到文字识别内的身份证识别,请求说明部分,可以看到bash,python,java,c++,PHP等,这里的例子用的是python,选择python拷贝到代码软件内..._将ai平台导入python

关于REST framework 中的Request 与 Response的分析_restschema不能用reponse-程序员宅基地

文章浏览阅读3.5k次。Request 与 Response1. RequestREST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单..._restschema不能用reponse

Apache的Order Allow,Deny 配置详解_order deny,allow-程序员宅基地

文章浏览阅读430次。原文地址:https://www.cnblogs.com/top5/archive/2009/09/22/1571709.htmlAllow和Deny可以用于apache的conf文件或者.htaccess文件中(配合Directory, Location, Files等),用来控制目录和文件的访问授权。所以,最常用的是:Order Deny,AllowAllow from Al..._order deny,allow

浙大版《C语言程序设计(第3版)》题目集-习题11-6 查找子串 (20分)_6-5 查找子串 分数 20 作者 张泳 单位 浙大城市学院 本题要求实现一个字符串查找-程序员宅基地

文章浏览阅读748次。本题要求实现一个字符串查找的简单函数。函数接口定义:char *search( char *s, char *t );函数search在字符串s中查找子串t,返回子串t在s中的首地址。若未找到,则返回NULL。裁判测试程序样例:#include <stdio.h>#define MAXS 30char *search(char *s, char *t);void R..._6-5 查找子串 分数 20 作者 张泳 单位 浙大城市学院 本题要求实现一个字符串查找

dtree做权限控制_权限控制复选框插件-程序员宅基地

文章浏览阅读4.2k次。一个项目需要一个权限控制的模块,因为是重构的项目,用的是dtree,所有着手研究了一手dtree。网上也有好的地案例,比如http://www.jq22.com/jquery-info5331这个网址的模式,挺好的,就是太贵了,再比如:http://www.16css.com/menu/1184.html这个网站,里面有代码,是免费的,但是就是感觉里面的方法不太全,所以自己动手写了一个,代码如..._权限控制复选框插件

VMware虚拟机安装Windows8.1_win8镜像csdn-程序员宅基地

文章浏览阅读2k次。最近,很多小伙伴们都觉得自己的电脑内存不够了,又想换块硬盘,但又是一件复杂的事,那么就有很多人想到了PC上安装虚拟机,占用空间呢也不是很大,又方便。那么要怎么安装呢?今天就来教大家如何安装!“叮咚——”准备做好系统一时游,我们开始吧!1.安装时必不可缺镜像!如果要正版镜像那么就去这个网址:MSDN, 我告诉你 - 做一个安静的工具站 (itellyou.cn)或这个微软官方:下载 Windows 8.1 光盘映像(ISO 文件) (microsoft.com)https://www.micros_win8镜像csdn

随便推点

七年级计算机上教学计划,七年级上册信息技术教学计划-程序员宅基地

文章浏览阅读209次。信息技术课是很多学生都感兴趣的,根据这一点,我们可以制定相应教学计划,引导学生正确应用电脑来学习。下面是出国留学网给大家带来七年级上册信息技术教学计划,欢迎阅读。【七年级上册信息技术教学计划一】一、 学生情况分析:大部分同学对计算机有浓厚的兴趣,在学习时能够勤于动手,积极参与课堂教学。但也有部分同学对计算机还是不甚理解,所以在以后的教学中,要针对具体的学生采取不同的方法,引导他们学好计算机,为将来..._七年级上册信息技术线上教学教学计划

jqGrid用法及增删改查方法_jqgrid可以在列表增删改查吗-程序员宅基地

文章浏览阅读3.5k次。jquery grid是富客户端的,基于XML , Ajax网格插件的jQuery库。 jqGridView提供专业的解决方案,代表和编辑表格数据在网络上。精心设计的,具有强大的脚本API的,这可编辑网格是很简单的DHTML与XML 的配置,并显示出令人信服的成果与大量数据。现在我现在熟悉一下jquery grid的的使用方法和一些常用的option设置。1.调用gridjqGrid已经可_jqgrid可以在列表增删改查吗

怎么调试Delphi源码_delphi如何联调源码-程序员宅基地

文章浏览阅读1.7k次。delphi调试概述2007-06-13 16:15delphi调试入门级的经典文章,如果是新手的话此文值得好好的读一读Delphi调试概述除非你的程序只有几行,否则几乎不可能一次写成,因此调试就非常必要。然而许多初学者不知道如何进行调试,写完程序就运行,发现结果不对再看代码,这样觉得非常吃力。这里,简单介绍一下Delphi所提供的调试功能。1. 语法检查(Syntax Che_delphi如何联调源码

最新海康摄像机、NVR、流媒体服务器、回放取流RTSP地址规则说明_海康威视流媒体地址-程序员宅基地

文章浏览阅读1.9w次,点赞2次,收藏21次。本文档主要介绍海康威视设备预览、回放、流媒体取流的RTSP URL和IE直接预览、回放的HTTP URL。RTSP为取流协议,取到码流后需要解码显示,可以通过VLC播放器进行测试,IE等浏览器网页不支持RTSP协议直接取流预览或者回放。网页上需要跳过登录界面直接访问我们设备的预览或者回放画面,可以使用文档中所述的HTTP的URL实现。注:1)URL中“:”“?”“&_海康威视流媒体地址

Mybatis学习记录_findbypaging(findallparam findallparam)-程序员宅基地

文章浏览阅读193次。转载自:https://dingjn-coder.github.io/2020/03/16/%E6%A1%86%E6%9E%B6/Mybatis%E4%BB%8B%E7%BB%8D%E5%8F%8A%E4%BD%BF%E7%94%A8/#toc-heading-41Mybatis介绍Mybatis是一个java持久层框架,内部封装了JDBC,并且做了很多的优化,开发者只需要关注如何写sq..._findbypaging(findallparam findallparam)

getchar getche getch-程序员宅基地

文章浏览阅读165次。转至:https://blog.csdn.net/hairetz/article/details/4161954(1) getch()和getche()函数 这两个函数都是从键盘上读入一个字符。其调用格式为: getch(); getche(); 两者的区别是: getch()函数不将读入的字符回显在显示屏幕上,而getche()函数却将读入..._利用getch()与getche()用于交互输入的过程中完成暂停

推荐文章

热门文章

相关标签