• 使用canvas绘制时钟 (http://heeroluo.net/Article/Detail/95)


    准备工作

    在HTML中指定一个区域放置时钟:

    <div id="clock" style="position: relative;"></div>

    时钟的一些外观设定:

    var width = 260; // 桌布宽度
    var height= 260; // 桌布高度
    var dot = {
    x : width / 2,
    y : height / 2,
    radius : 6
    }; // 圆点位置、半径
    var radius = 120; // 圆半径
    var borderWidth = 6; // 圆边框宽度

    创建<canvas>元素:

    var clock = document.getElementById('clock');
    var clockBg = document.createElement('canvas');
    var clockPointers = document.createElement('canvas');

    clockPointers.width = clockBg.width = width;
    clockPointers.height = clockBg.height = height;
    clockPointers.style.position = 'absolute';
    clockPointers.style.left = 0;
    clockPointers.style.right = 0;

    clock.appendChild(clockBg);
    clock.appendChild(clockPointers);

    这里要创建两个<canvas>元素,目的在于把时钟的圆盘跟指针分离开。这是因为指针要根据当前时间擦除重绘,如果放置在一个<canvas>中,擦除的时候就会把圆盘也给擦掉了。

    绘制圆盘

    但凡要在<canvas>中绘图,都要先获得其上下文,对应的接口是 canvas.getContext

    var bgCtx = clockBg.getContext('2d');

    目前canvas.getContext接口的唯一一个合法参数是'2d',将来应该会支持3D绘图。

    先来绘制最外面的圆框:

    bgCtx.beginPath();
    bgCtx.lineWidth = borderWidth;
    bgCtx.strokeStyle = '#000';
    bgCtx.arc(dot.x, dot.y, radius, 0, 2 * Math.PI, true);
    bgCtx.stroke();
    bgCtx.closePath();

    绘图的流程其实都是类似的:

    1. 调用 context.beginPath() 新建路径;
    2. 设置颜色等样式;
    3. 调用路径函数生成路径;
    4. 画线(stroke)或者填充(fill);
    5. 调用 context.closePath() 关闭路径;

    上面用到的 context.arc 接口可以生成圆弧路径,其详细说明参见此处

    用类似的方法,画出圆点:

    bgCtx.beginPath();
    bgCtx.fillStyle = '#000';
    bgCtx.arc(dot.x, dot.y, dot.radius, 0, 2 * Math.PI, true);
    bgCtx.fill();
    bgCtx.closePath();

    此时,结果如下图所示:

    时钟圆框和圆点

    绘制刻度

    最复杂的地方就是画刻度了,这里要先复习一下数学中的三角函数

    三角函数

    刻度的起始位置就是圆框上的一个点,第一步就是要知道这个点的坐标。上图中:

    sinθ = AC / AO
    cosθ = OC / AO

    其中AO即为圆半径,而θ的值则根据刻度而定。0是π/2,3是0,6是3π/2,9是π:

    三角函数坐标

    由此可得到刻度起始点的位置为:

    x = 圆点横坐标 + AO * cosθ
    y = 圆点纵坐标 + AO * sinθ

    同理可算出刻度结束点的位置为(结束点相当于在一个半径为圆框半径-刻度长度的圆上):

    x = 圆点横坐标 + (AO - 刻度长度) * cosθ
    y = 圆点纵坐标 + (AO - 刻度长度) * sinθ

    于是,这程序可以写了:

    for (var i = 0, angle = 0, tmp, len; i < 60; i++) {
    bgCtx.beginPath();

    // 突出显示能被5除尽的刻度
    if (0 === i % 5) {
    bgCtx.lineWidth = 5;
    len = 12;
    bgCtx.strokeStyle = '#000';
    } else {
    bgCtx.lineWidth = 2;
    len = 6;
    bgCtx.strokeStyle = '#999';
    }

    tmp = radius - borderWidth / 2; // 因为圆有边框,所以要减去边框宽度
    bgCtx.moveTo(
    dot.x + tmp * Math.cos(angle),
    dot.y + tmp * Math.sin(angle)
    );
    tmp -= len;
    bgCtx.lineTo(dot.x + tmp * Math.cos(angle), dot.y + tmp * Math.sin(angle));
    bgCtx.stroke();
    bgCtx.closePath();

    angle += Math.PI / 30; // 每次递增1/30π
    }

    画好刻度后,结果应该是这样:

    时钟刻度

    画指针

    先得获取指针<canvas>的上下文:

    var ptxContext = clockPointers.getContext('2d');

    由于画指针的操作每隔一秒都要执行一次,所以这里就写成一个函数,方便传给setInterval调用:

    function updatePointers() {
    ptCtx.clearRect(0, 0, width, height);  // 清掉原来的指针

    // 获取当前时间
    var now = new Date();
    var h = now.getHours();
    var m = now.getMinutes();
    var s = now.getSeconds();

    // 算出时分秒指针现在应指向圆的几分之几处
    h = h > 12 ? h - 12 : h;
    h = h + m / 60;
    h = h / 12;
    m = m / 60;
    s = s / 60;

    drawPointers(s, 2, 92); // 画秒针
    drawPointers(m, 4, 82); // 画分针
    drawPointers(h, 6, 65); // 画时针
    }

    drawPointers函数的实现是:

    // angle是角度,lineWidth是指针宽度,length是指针长度
    function drawPointers(angle, lineWidth, length) {
    angle = angle * Math.PI * 2 - Math.PI / 2;

    ptCtx.beginPath();
    ptCtx.lineWidth = lineWidth;
    ptCtx.strokeStyle = "#000";
    ptCtx.moveTo(dot.x, dot.y);
    ptCtx.lineTo(dot.x + length * Math.cos(angle), dot.y + length * Math.sin(angle));
    ptCtx.stroke();
    ptCtx.closePath();
    }

    这里主要也是用到三角函数,就不啰嗦了,但是要注意angel的计算。由于传入的angel是一个百分数,所以要乘以一个圆周,也就是2π,才知道对应的弧度,算出来以后还要减去π/2,因为从上面的坐标图就可以看到,0是位于x轴而不是y轴除,刚好比正常的时钟多了π/2。

    最后别忘了调用updatePointers实时更新指针:

    setInterval(updatePointers, 1000);
    updatePointers();

    这下时钟完全出来了,除了初步熟悉<canvas>绘图API外,还顺便复习了一次三角函数。

    时钟最终结果

    (完整代码)http://sandbox.runjs.cn/show/3afkqodu

  • 相关阅读:
    【NIFI】 Apache NiFI 之 ExecuteScript处理(一)
    【NIFI】 实现数据库到数据库之间数据同步
    【Git】 GitLab服务器社区版安装与配置
    【ElasticSearch】 ElasticSearch安装(一)
    【NIFI】 开发自定义Nifi Processor
    【NIFI】 Apache NiFI 与 SQL 操作
    【NIFI】 Apache NiFI 安装及简单的使用
    【RabbitMQ】 Java简单的实现RabbitMQ
    【RabbitMQ】 RabbitMQ配置开机启动
    【Linux】开机自动启动脚本
  • 原文地址:https://www.cnblogs.com/icelin/p/3633535.html
Copyright © 2020-2023  润新知