整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

HTML使用Canvas绘制动画时钟

么是Canvas

<canvas> 是HTML中的一个元素,它可被用来通过 JavaScript(Canvas API 或 WebGL API)绘制图形及图形动画。

Canvas API 提供了一个通过 JavaScriptHTML<canvas> 元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。

<canvas>标签本身没有绘图能力,它仅仅是图形的容器。在HTML,一般通过Javascript语言来完成实际的操作。

创建HTML页面并添加绘图容器

本文通过Javascript操作Canvas制作一个简单的显示当前时间的动画时钟,了解和学习简单的canvas用法,仅以抛砖引玉。

首先创建一个HTML文件,为了方便管理,使用一个div标签包裹两个canvas标签,并加上一些简单的css样式。

<!doctype html>
<html lang="zh-cn">
<head><title>Canvas绘制动画时钟</title>
<style>
html,body{margin:0;padding:0}
#clockWrap {
	position: relative;
}
canvas {
	position: absolute;
}
#clock-ui {
	z-index: 2;
}
#clock-plate {
	z-index: 1;
}
</style>
</head>
<body>
  <div id="clockWrap">
  <canvas id="clock-plate"></canvas>
  <canvas id="clock-ui"></canvas>
</div>
<script></script>
</body></html>

本示例中使用了两个canvas标签(为什么使用两个,一个不是更简单吗?),一个用于绘制钟面,一个根据当前时间实时显示和更新时针、分针和秒针的动态指向。好了,话不多说,开干。

绘制钟面刻度

一个简单的时钟,可以分为钟面上的刻度和指针。其中刻度和12个数字是固定的,我们可以将它们绘制在当作背景的canvas上(示例中id为clock-plate的canvas)。

(1)要使用canvas,首先必须通过容器获取渲染上下文:

var $=function(id){return document.querySelector(id);}//这个函数只是为了方便获取dom元素
const canvasbg=$("#clock-plate"),
      canvas=$("#clock-ui"),
      ctx = canvasbg.getContext("2d"),//背景容器上下文
      ctxUI = canvas.getContext("2d");//指针容器上下文,后面代码要用
//定义画布宽度和高度,时钟圆直径,并设置画布大小
const oW=1000,oH=800,cW=400,r=cW/2,oX=oW/2,oY=oH/2;
canvas.width=oW;
canvas.height=oH;
canvasbg.width=oW;
canvasbg.height=oH;

(2)画钟的边框,为了好看,这里画两个圈:

 //画出时钟外圆框
  ctx.lineWidth = 12;
	ctx.beginPath();
	ctx.arc(oX, oY, r+14, 0, 2 * Math.PI);
	ctx.stroke();
	ctx.closePath();
	ctx.lineWidth = 8;
	//画出时钟内圆框(刻度圈)
	ctx.beginPath();
	ctx.arc(oX, oY, r, 0, 2 * Math.PI);
	ctx.stroke();
	ctx.closePath();
	ctx.beginPath();

边框效果图

(3)绘制刻度线和数字,可以利用三角函数计算出每个刻度的坐标:

利用三角函数计算刻度线的坐标位置

钟面上有12个大格,从正上方12开始,它们的度数分别是270,300,330,0,30,60,90,120,150,180,210,240。然后利用JS的Math.sin和Math.cos分别计算出各大格的坐标。注意:js中Math.sin()和Math.cos()的参数不是角度数弧度。可以使用Math.PI/180*角度来转化,比如将30度转换成弧度=Math.PI/180*30

//绘制钟表中心点
	ctx.beginPath();
	ctx.arc(oX, oY, 8, 0, 2 * Math.PI);//圆心
	ctx.fill();
	ctx.closePath();
	//设置刻度线粗细度
	ctx.lineWidth = 3;
	//设置钟面12个数字的字体、大小和对齐方式
	ctx.font = "30px serif";
	ctx.textAlign="center";
	ctx.textBaseline="middle";
	var kdx,kdy;
	//绘制12个大刻度和12个数字
	//为方便计算,先定义了0-11这12个刻度对应的度数,也可以直接定义对应的弧度。
	const hd=Math.PI/180,degr=[270,300,330,0,30,60,90,120,150,180,210,240];
	for(var i=0;i<12;i++){
		kdx=oX+Math.cos(hd*degr[i])*(r-3);
		kdy=oY+Math.sin(hd*degr[i])*(r-3);
		ctx.beginPath();
		ctx.arc(kdx, kdy, 6, 0, 2 * Math.PI);//画圆形大刻度
		ctx.fill();
    //绘制刻度对应的数字
		ctx.strokeText(i==0? 12 : i,oX+Math.cos(hd*degr[i])*(r-24),oY+Math.sin(hd*degr[i])*(r-24));
		ctx.closePath();
	}
	
	//绘制小刻度
	ctx.lineWidth = 2;
	for(var i=0;i<60;i++){
		if(i % 5 == 0) continue;//跳过与刻度重叠的刻度
		x0=Math.cos(hd*i*6);
		y0=Math.sin(hd*i*6);
		ctx.beginPath();
		ctx.moveTo(oX+x0*(r-10), oY+y0*(r-10)); 
		ctx.lineTo(oX+x0*r, oY+y0*r); //画短刻度线
		ctx.stroke(); 
		ctx.closePath();
	}

效果如图:

钟面效果图

(4)根据当前时间绘制指针

习惯上,时针粗短,分针略粗而长,秒针细长。为加大区别,示例中秒针细长并且绘制成红色。

function drawHp(i){//绘制时针
	const x0=Math.cos(hd*i*30),y0=Math.sin(hd*i*30);
	drawPointer(oX,oY,oX+x0*(r-90),oY+y0*(r-90),10,"#000000");
}
function drawMp(i){//绘制分针
	const x0=Math.cos(hd*i*6),y0=Math.sin(hd*i*6);
	drawPointer(oX,oY,oX+x0*(r-60),oY+y0*(r-60),5,"#000000");
}
function drawSp(i){//绘制秒针
	const x0=Math.cos(hd*i*6),y0=Math.sin(hd*i*6);
	drawPointer(oX,oY,oX+x0*(r-20),oY+y0*(r-20),2,"#FF0000");
}
//抽取出绘制三种指针时共同的部分,注意指针绘制在渲染上下文ctxUI中
function drawPointer(ox,oy,tx,ty,width,color){
	ctxUI.strokeStyle = color;
	ctxUI.lineCap = "round";
	ctxUI.lineWidth = width;
	ctxUI.beginPath();
	ctxUI.moveTo(ox, oy);
	ctxUI.lineTo(tx,ty);
	ctxUI.stroke();
	ctxUI.closePath();
}

现在已经有了绘制三种指针的方法,参数是当前时间的时、分和秒,将根据它们的值确定指针的坐标。不过,因为使用的是默认的convas坐标体系,0值实际指向3的位置,需要小小的修正一下。

window.requestAnimationFrame(function fn(){
		var d = new Date();
		ctxUI.clearRect(0,0,oW,oH);
		//度数从0开始,而0在3刻度(15分/秒位置),修正为全值减15,如果小于0则修正回来
    var hour=d.getHours(),minute=d.getMinutes()-15,second=d.getSeconds()-15;
		hour=hour>11? hour-15 : hour-3;
		drawHp(hour>=0? hour : 12+hour);
		drawMp(minute>=0? minute : 60+minute);
		drawSp(second>=0? second : 60+second);
		window.requestAnimationFrame(fn);
});

接下来,调用window.requestAnimationFrame,在其中绘制并更新指标的位置。看看效果如何:

指针绘制情况与实际时间相符

貌似效果有了,截图时电脑上的时间是10时17分,指针绘制上,时针指向10时,分针指向17。嗯,感觉有点别扭?对了,时针和分针怎么是端端正正地指向它们的整时整分刻度上呢?实际钟表上时针和分针是展示动态进度的,此时时针应该越过10时的位置才对。没关系,我们在绘制时针和分针时别点东西,让它的角度值加上分针和秒针的值试试。

//修改后的绘制三种指针的方法
function drawHp(i,f,m){//绘制时针,参数:时,分,秒
	const x0=Math.cos(hd*(i+(f/60)+(m/600))*30),y0=Math.sin(hd*(i+(f/60)+(m/600))*30);
	drawPointer(oX,oY,oX+x0*(r-90),oY+y0*(r-90),10,"#000000");
}
function drawMp(i,f){//绘制分针,参数,分,秒
	const x0=Math.cos(hd*(i+(f/60))*6),y0=Math.sin(hd*(i+(f/60))*6);
	drawPointer(oX,oY,oX+x0*(r-60),oY+y0*(r-60),5,"#000000");
}
function drawSp(i){//绘制秒针
	const x0=Math.cos(hd*i*6),y0=Math.sin(hd*i*6);
	drawPointer(oX,oY,oX+x0*(r-20),oY+y0*(r-20),2,"#FF0000");
}

再来看看效果,嗯,立竿见影呀:

指针指向更合理了

到此为止,canvas绘制一个简易时钟就完成了。下面继续优化一下。刚才使用requestAnimationFrame方法即时更新绘制情况。这个方法与刷新率有关,看看mdn上面怎么说的:

window.requestAnimationFrame() 方法会告诉浏览器你希望执行一个动画。它要求浏览器在下一次重绘之前,调用用户提供的回调函数。

对回调函数的调用频率通常与显示器的刷新率相匹配。虽然 75hz、120hz 和 144hz 也被广泛使用,但是最常见的刷新率还是 60hz(每秒 60 个周期/帧)。为了提高性能和电池寿命,大多数浏览器都会暂停在后台选项卡或者隐藏的 <iframe> 中运行的 requestAnimationFrame()。

本示例中,更新指针的位置并不需要很高的刷新频率,可以通过节流进行一下优化:

var fps = 5, fpsInterval = 1000 / fps,lastTime = new Date().getTime(); //记录上次执行的时间
function runStep() {
    requestAnimationFrame(runStep);
    var d=new Date(),now = d.getTime()
    var elapsed = now - lastTime;
    if (elapsed > fpsInterval) {
				ctxUI.clearRect(0,0,oW,oH);
        lastTime = now - (elapsed % fpsInterval); 
			//度数从0开始,而0在3刻度(15分/秒位置),修正为全值-15,如果小于0则用60减回
        var hour=d.getHours(),minute=d.getMinutes()-15,second=d.getSeconds()-15;//console.log(d.getSeconds(),second);
        hour=hour>11? hour-15 : hour-3;
        drawHp(hour>=0? hour : 12+hour,minute+15,second+15);
        drawMp(minute>=0? minute : 60+minute,second+15);
        drawSp(second>=0? second : 60+second);
    }
}
runStep();

当然,实现时钟的方法是很多,比如可以使用画布的旋转(rotate方法)来实现指针的动态转动等等。

完整HTML+JS源码:

用canvas做的一个时钟,先看效果

1、绘制时钟圆盘

绘制时钟圆盘,其实就是绘制一个圆,这个比较简单,代码如下:

2、标绘刻度

标绘刻度的难点在于确定刻度点的位置,这需要用到一些的几何知识,圆弧和三角函数。

如上图,要求圆上任意一点A的位置,通过三角函数可以推出:

x1=BC=cos(a)*AC

y1=BC=sin(a)*AC

其中AC就是半径,是已知的

现在我们要来确定角a的度数:

小时刻度:12个刻度,每一小时的角度为:360/12,弧度为:2π/12

分钟刻度:60个刻度,每一分钟的角度为:360/60,弧度为:2π/60

有了半径和角度我们就可以算出坐标,这样就可以绘制刻度了。注意js里面用的是弧度。

为了方便,我们用ctx.translate()把画布原点重定向到了圆心,这改变了画布的状态,为了不影响后续的绘制,每次绘完之后都要把画布恢复为原来的状态,具体操作就是:重定向画布原点之前,用ctx.save()保存画布状态,绘制完成之后,用ctx.restore()恢复之前保存的画布状态。

//保存画布当前环境状态
ctx.save();
//重定向画布原点
ctx.translate(250,250);
//绘制代码......
//恢复之前保存的状态
ctx.restore();

绘制小时刻度代码

同理,绘制分钟刻度就简单,把12换成60就可以了。

绘制分钟刻度代码

3、画时针

时针、分针、秒针都是和当前时间有关,所以先要获取当前时间的小时数、分钟数、秒数。

//得到当前时间
var now=new Date();
//当前小时数
var hour=now.getHours();
//当前分钟数
var minute=now.getMinutes();
//当前秒数
var second=now.getSeconds();
hour=hour+minute/60;

画时针时,我们先画出指向12点的时针:

//指向12点的时针
ctx.beginPath();
ctx.lineWidth=8;
ctx.lineCap="round";
ctx.moveTo(0,10);
ctx.lineTo(0,-120);
ctx.stroke()

之后每一个时刻度点的时针就是把指向12点的时针旋转一定的度数:

//旋转度数
ctx.rotate(hour*2*Math.PI/12);

绘制时针代码

同理,时针绘制好之后,分针和秒针也就简单,直接看代码。

绘制分针代码

绘制秒针代码

为了好看我们再画一个中心点。

绘制中心点代码

最后,为了让时钟实时转动,需要加一个定时任务,每秒执行一次。

//一秒钟执行一次
setInterval(clock,1000);

还要注意,每次重绘都要清除画布:

 ctx.clearRect(0,0,canvas.width,canvas.height);

最终完整的代码逻辑如下:

这样一个时钟就完成了,如果想看更详细的操作,推荐观看视频版。

家我呀,我是yangyang,此刻看到一个小特效,是国外一哥们做的,特来分享给大家.

效果介绍

数字时代,数字发光时钟体现了形式与功能的融合。在这篇文章中,我们将深入研究如何使用 HTML、CSS 和 JavaScript 来构建一个。

HTML

构建一个结构化基础,其中包含小时、分钟、秒和可选的 AM/PM 指示器元素。可访问性至关重要,因此我们将使用语义标签并提供有意义的描述。

<!DOCTYPE html>
<html lang="zh-cn">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Digital clock</title>
    <link rel="stylesheet" href="css/style.css" />
  </head>
  <body>
    <div class="hero">
      <div class="box">
        <style></style>
        <div class="clock">
          <span id="hrs">00</span>
          <span>:</span>
          <span id="min">00</span>
          <span>:</span>
          <span id="sec">00</span>
        </div>
      </div>
    </div>
    <script src="js/index.js"></script>
  </body>
</html>

css

利用 box-shadow 和 text-shadow 等属性,我们将为时钟注入活力。通过微调颜色和发光强度,我们将确保它吸引用户。

* {
  margin: 0;
  padding: 0;
  font-family: "Poppins", sans-serif;
  box-sizing: border-box;
}
.hero {
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #2f363e;
  position: relative;
}
.box {
  position: relative;
  width: 800px;
  height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.clock {
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(40px);
  color: #6e7f92f6;
  z-index: 10;
}
.clock span {
  font-size: 80px;
  width: 110px;
  display: inline-block;
  text-align: center;
  position: relative;
}
.clock span::after {
  font-size: 16px;
  position: absolute;
  bottom: -15px;
  left: 50%;
  transform: translateX(-50%);
}
#hrs::after {
  content: "HOURES";
  color: #0f0;
  font-weight: 600;
  margin-bottom: -10px;
}
#min::after {
  content: "MINS";
  color: #0ff;
  font-weight: 600;
  margin-bottom: -10px;
}
#sec::after {
  content: "SEC";
  color: #ff0;
  font-weight: 600;
  margin-bottom: -10px;
}

/*------Anemated Border---------*/
.box::before {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-conic-gradient(
    from var(--a),
    #0f0,
    #ff0,
    #0ff,
    #f0f,
    #0ff
  );
  border-radius: 20px;
  animation: rotate 6s linear infinite;
}
.box::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-conic-gradient(
    from var(--a),
    #0f0,
    #ff0,
    #0ff,
    #f0f,
    #0ff
  );
  border-radius: 20px;
  animation: rotate 4s linear infinite;
  filter: blur(40px);
  opacity: 0.75;
}
.box style {
  position: absolute;
  inset: 4px;
  background: #2f363e;
  border-radius: 16px;
  color: #ff0;
  font-size: 20px;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
@property --a {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
@keyframes rotate {
  0% {
    --a: 0;
  }
  0% {
    --a: -360deg;
  }
}

js

JavaScript 为我们的时钟注入了活力,实现了小时、分钟和秒的实时更新。我们还将考虑添加时区支持和用户偏好的自定义选项等功能。