Canvas绘制时钟及API说明

Canvas时钟demo及API说明

此篇博客主要通过绘制时钟demo分享一下Canvas API及个人理解误区。
HTML5 canvas学习 HTML5 Canvas 这一篇文章就够了主要参考了这两篇博客,收获颇丰值得推荐。

下边结合个人理解,绘制一个钟表demo作为演示,主要逻辑会在下面完整demo代码中备注说明;


canvas是HTML5新增标签,只定义图形,绘制图形需要利用API及JS来实现;可以进行画线 图形等自定义路径进行绘制;

一. 起步
1.创建

由于某些较老的浏览器(尤其是 IE9 之前的 IE 浏览器)不支持canvas元素,可以在标签中声明替代内容做一下兼容处理。支持canvas的浏览器会只渲染 标签,而忽略其中的替代内容。不支持canvas的浏览器则会直接渲染替代内容。

// 声明canvas
<canvas id="canvas" width="800" height="600">您的浏览器不支持Canvas,请升级或更换浏览器!</canvas>

注意:
1.标签通常需要指定一个id属性方便引用, 元素默认大小是300*150,单位px;宽高需要使用width和height属性声明,而不是css样式控制,行内样式内联样式都是不对的,会拉伸画布造成绘制的图形跟预想的并不一样。

2.理解绘制

绘制图形分为以下4步骤,根据需求绘制每一帧画布,再指定顺序播放每一帧播放也就动起来变成了动画。

  1. 清除画布 clearRect()
  2. 保存状态 save()
  3. 绘制
  4. 恢复状态 restore()

以绘制表盘刻度为例,每次绘制前先save()保存当前绘制状态,旋转画布绘制结束后再restore()恢复到之前保存的状态,绘制此帧动画结束。进入下一次循环绘制下一帧也是同理。

// 刻度
for (let i = 0; i < 60; i++) {
  ctx.save();
  ctx.beginPath();
  ctx.rotate(-PI / 2 + ((2 * PI) / 60) * i);
  ctx.moveTo(290, 0);
  ctx.lineTo(300, 0);
  ctx.strokeStyle = "#fff";
  ctx.lineWidth = 1;
  ctx.stroke();
  ctx.restore();
}

注意:

1.canvas是基于状态进行绘制的,在每一次进行绘制时,canvas不是简单的将上一段代码进行绘制,而是检查在整个程序中设置的所有状态,基于这些状态完成一次绘制。这一点需要理解(参考学习 HTML5 Canvas 这一篇文章就够了)。所以才会有save()、restore()、beginPath()等。

2.补充一下,beginPath()与closePath()没有必然联系,不是必须成对存在。beginPath是新建路径,closePath的意思不是结束路径,而是关闭路径,它会试图从当前路径起点和终点之间创建一条路径,让整个路径闭合起来。简单举例用线段绘制了一个L,在声明closePath后,画布会自动闭合这个绘制路径为一个三角形就是这个意思;

示例demo

以下是一个自适应父级宽高的demo;每一步的逻辑都已经解释清楚,注释标注才是我最想说明的部分,理解这个逻辑,这个demo非常容易写出来。
在这里插入图片描述

<style>
  * {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  #wrap {
    width: 80vw;
    height: 80vh;
    border: 2px solid #000;
  }

  #canvas {
    background: #000;
  }
</style>
<div id="wrap">
  <canvas id="canvas">您的浏览器不支持Canvas,请升级或更换浏览器!</canvas>
</div>
// 获取包裹宽高
var wrap = document.getElementById("wrap"),
  wrap_style = window.getComputedStyle(wrap),
  wrap_width = wrap_style.width.split("px")[0],
  wrap_height = wrap_style.height.split("px")[0];
// 创建画布
var canvas = document.getElementById("canvas"),
  ctx = canvas.getContext("2d"),
  PI = Math.PI;

window.onload = function () {
  init();
};

// 初始化
function init() {
  //判断是否支持canvas
  if (!ctx) {
    alert("您的浏览器不支持Canvas,请升级或更换浏览器!");
    return;
  }
  // 将父容器的宽高赋值给canvas宽高属性,适应父级宽高
  canvas.width = wrap_width;
  canvas.height = wrap_height;

  drawClock();
}

// 绘制表盘
function drawClock() {
  // 绘制4大步骤,在这里具体实现
  // 1.清除画布
  ctx.clearRect(0, 0, wrap_width, wrap_height);

  //2.保存状态&位移原点到画布中心
  ctx.save();
  ctx.translate(wrap_width / 2, wrap_height / 2);

  //3.绘制部分
  //表盘
  ctx.beginPath();
  ctx.arc(0, 0, 300, 0, Math.PI * 2);
  ctx.strokeStyle = "rgba(255,255,255,1)";
  ctx.stroke();

  // 刻度,绘制每一个刻度后返回原点.旋转到指定位置绘制下一个刻度
  for (let i = 0; i < 60; i++) {
    ctx.save();
    ctx.beginPath();
    ctx.rotate(-PI / 2 + ((2 * PI) / 60) * i);
    ctx.moveTo(290, 0);
    ctx.lineTo(300, 0);
    ctx.strokeStyle = i % 5 === 0 ? "#fff" : "rgba(255,255,255,0.5)";
    ctx.lineWidth = i % 5 === 0 ? 3 : 1;
    ctx.stroke();
    ctx.restore();
  }

  // 绘制每一帧 时针 分针 秒针
  drawAllHands();

  // 4.恢复上一次保存状态
  ctx.restore();

  // 5.requestAnimationFrame 递归调用实现动画
  window.requestAnimationFrame(drawClock);
}

// 绘制时分秒
function drawAllHands() {
  var date = new Date(),
    h = date.getHours(),
    m = date.getMinutes(),
    s = date.getSeconds();

  // 注意区分每一刻度  时分秒代表的时间
  var s_angle = ((2 * PI) / 60) * s;
  var m_angle = ((2 * PI) / 60) * m + s_angle / 60;
  var h_angle = ((2 * PI) / 12) * h + m_angle / 12;

  drawHand(s_angle, -30, 290, "#fff");
  drawHand(m_angle, -20, 250, "green", 4);
  drawHand(h_angle, -10, 180, "red", 8);
}
// 绘制指针
function drawHand(angele, start, end, color, width) {
  ctx.save();
  ctx.beginPath();
  ctx.rotate(-PI / 2 + angele);
  ctx.moveTo(start, 0);
  ctx.lineTo(end, 0);
  ctx.strokeStyle = color || "#fff";
  ctx.lineWidth = width || 1;
  ctx.lineCap = "round" || "butt";
  ctx.stroke();
  ctx.restore();
}

有问题欢迎指正,分享请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值