Canvas学习之绘制时钟

Canvas官方API网址:http://www.w3school.com.cn/tags/html_ref_canvas.asp
效果图:
这里写图片描述

笔记:
@fillStyle 只能与fill()方法一起使用,
@strokeStyle 与stroke()一起使用;
@fill()方法时用来填充画布的,而stroke()是用来绘制线条的;
@drawHour()这些带有旋转角度(rotate())的情况,画线的时候要以无旋转先画出来,再在此基础上添加旋转角;
@save()和 restore() 一般是成对出现的,restore会去寻找没有被restore过的最近的save状态

可以用window.setInterval(), window.setTimeout(),和window.requestAnimationFrame()来设定定期执行一个指定函数。
@setInterval(function, delay)
当设定好间隔时间后,function会定期执行。
@setTimeout(function, delay)
在设定好的时间之后执行函数
@requestAnimationFrame(callback)
告诉浏览器你希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。
如果你并不需要与用户互动,你可以使用setInterval()方法,它就可以定期执行指定代码。如果我们需要做一个游戏,我们可以使用键盘或者鼠标事件配合上setTimeout()方法来实现。通过设置事件监听,我们可以捕捉用户的交互,并执行相应的动作。

下面的例子,采用 window.requestAnimationFrame()实现动画效果。这个方法提供了更加平缓并更加有效率的方式来执行动画,当系统准备好了重绘条件的时候,才调用绘制动画帧。一般每秒钟回调函数执行60次,也有可能会被降低。

原生代码实现:

<!DOCTYPE HTML>
<html>
<body>

<canvas id="clock1" width="200" height="200" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>

<script type="text/javascript">
    let dom = document.getElementById('clock1');
    let ctx = dom.getContext('2d');
    let width = ctx.canvas.width;
    let height = ctx.canvas.height;
    let r = width / 2;
    let rem=width/200;  //比例像素大小

    function drawBackground(){
        ctx.save();         //保存当前环境的状态,此处保存了最初原点位置
        /*画圆圈*/
        ctx.translate(r,r);  //重设原点,使其在原来基础上在x,y两个方向均偏移r的长度
        ctx.beginPath();    //起始一条路径,或重置当前路径
        ctx.lineWidth = 10 * rem;
        ctx.arc(0,0,r-ctx.lineWidth/2,0,2*Math.PI,false);   //画圆:.arc(x,y,r,起始角,结束角,顺时针false/逆时针true)
        ctx.stroke();    // "绘制"已定义的路径

        /*刻数字*/
        ctx.font = 18* rem+'px Arial';   //设置或返回文本内容的当前字体属性
        ctx.textAlign = 'center';  //设置或返回文本内容的当前对齐方式
        ctx.textBaseline = 'middle';  //设置或返回在绘制文本时使用的当前文本基线
        let hours = [3,4,5,6,7,8,9,10,11,12,1,2];
        hours.forEach(function(ele,i){
            var rad = 2*Math.PI / 12 *i;
            let x = Math.cos(rad) * (r-30* rem);
            let y = Math.sin(rad) * (r-30* rem);
            ctx.fillText(ele,x,y);   //在画布上绘制“被填充的”文本,位置(x,y):.fillText(文本内容,x,y,允许的最大文本宽度,以像素计.可选);
        })
        /*画刻度点*/
        for(let i=0;i<60;i++){
            let rad = 2*Math.PI / 60 * i;
            let x = Math.cos(rad) * (r-18* rem);  //r-18是为了把点画在圈内18px位置
            let y = Math.sin(rad) * (r-18* rem);
            ctx.beginPath();
            if(i % 5 === 0){
                ctx.fillStyle = '#000';
                ctx.arc(x,y,2* rem,0,2*Math.PI,false)
            }else{
                ctx.fillStyle = '#ccc';
                ctx.arc(x,y,2* rem,0,2*Math.PI,false)
            }

            ctx.fill();  // "填充"当前绘图
        }
    }

    function drawHour(hour,minute){
        ctx.save();
        ctx.beginPath();
        let rad = 2 *Math.PI / 12 * hour;
        var mrad = 2*Math.PI/12/60*minute;
        ctx.rotate(rad + mrad);  //旋转当前绘图,参数为旋转角弧度
        ctx.lineWidth = 5* rem;
        ctx.lineCap = 'round';  //设置或返回线条的结束端点样式
        ctx.moveTo(0,10* rem);
        ctx.lineTo(0,-r/2);   添加一个新点坐标,然后在画布中创建从该点到最后lineTo指定点的线条
        ctx.stroke();
        ctx.restore();
    }

    function drawMinute(minute){
        ctx.save();
        ctx.beginPath();
        let rad = 2 *Math.PI / 60 * minute;
        ctx.rotate(rad);
        ctx.lineWidth = 3* rem;
        ctx.lineCap = 'round';
        ctx.moveTo(0,15* rem);
        ctx.lineTo(0,-r/2-10* rem);
        ctx.stroke();
        ctx.restore();
    }
    /*画秒针:秒针不是一条线而是一个尖三角形,先lineTo三角形的三个点然后fill()填充*/
    function drawSec(sec) {
        ctx.save();
        // ctx.translate(r,r);
        ctx.beginPath();
        ctx.fillStyle='#c14543';
        var rad=2*Math.PI/60*sec;//定义每秒旋转的弧度
        ctx.rotate(rad);

        ctx.moveTo(-2*rem,20*rem);  //  把路径移动到画布中的指定点,不创建线条,会作为后面创建线条的起始点
        ctx.lineTo(2*rem,20*rem);  //添加一个新点,然后在画布中创建从该点到最后lineTo指定点的线条
        ctx.lineTo(0,-r+18*rem);
        ctx.lineTo(-2*rem,20*rem);
        ctx.fill();       
        ctx.restore();
    }
    /*画中心固定点*/
    function drawDot(){
        ctx.beginPath();
        ctx.fillStyle = 'green';
        ctx.arc(0,0,3* rem,0,2*Math.PI,false);
        ctx.fill();
    }

    function draw(){
        ctx.clearRect(0,0,width,height);   //清除矩形画布,后面开始重新画圆
        drawBackground();
        drawDot();        
        let now = new Date();
        let hour = now.getHours();
        let munite = now.getMinutes();
        let sec = now.getSeconds();
        drawHour(hour,munite);
        drawMinute(munite);
        drawSec(sec);   
        ctx.restore();  //返回之前保存过的路径状态和属性,此处返回drawBackground()开始保存到的空画布状态
        /*采用 window.requestAnimationFrame()实现动画效果。这个方法提供了更加平缓并更加有效率的方式来执行动画,当系统准备好了重绘条件的时候,才调用绘制动画帧。一般每秒钟回调函数执行60次,也有可能会被降低*/
        window.requestAnimationFrame(draw); 
    }

    draw();
    window.requestAnimationFrame(draw);
    /*此处要么一个setInterval,要么两个requestAnimationFrame,都可以短时间刷新*/
    // setInterval(draw, 1000);
</script>

<!-- fillStyle 只能与fill()方法一起使用,
strokeStyle  与stroke()一起使用;
fill()方法时用来填充画布的,而stroke()是用来绘制线条的;
drawHour()这些带有旋转角度(rotate())的情况,画线的时候要以无旋转先画出来,再在此基础上添加旋转角;
save()和 restore() 一般是成对出现的,restore会去寻找没有被restore过的最近的save状态-->
</body>
</html>

在Vue工程中可以采用以下代码实现:

<template>
    <div class="clock">
        <canvas id="clock1" width="200" height="200" style="border:1px solid #c3c3c3;">
        Your browser does not support the canvas element.
        </canvas>
    </div>
</template>
<script>
    export default{
        data(){
            return{

            }
        },
        mounted:function(){
            this.dom = document.getElementById('clock1');
            this.ctx = this.dom.getContext('2d');
            this.width = this.ctx.canvas.width;
            this.height = this.ctx.canvas.height;
            this.r = this.width / 2;
            this.rem=this.width/200;  //比例像素大小          
            this.draw();
            setInterval(this.draw, 1000);
        },
        methods:{
            drawBackground:function(){
                this.ctx.save();         //保存当前环境的状态,此处保存了最初原点位置
                /*画圆圈*/
                this.ctx.translate(this.r,this.r);  //重设原点,使其在原来基础上在x,y两个方向均偏移r的长度
                this.ctx.beginPath();    //起始一条路径,或重置当前路径
                this.ctx.lineWidth = 10 * this.rem;
                this.ctx.arc(0,0,this.r-this.ctx.lineWidth/2,0,2*Math.PI,false);   //画圆:.arc(x,y,r,起始角,结束角,顺时针false/逆时针true)
                this.ctx.stroke();    // "绘制"已定义的路径

                /*刻数字*/
                this.ctx.font = 18* this.rem+'px Arial';   //设置或返回文本内容的当前字体属性
                this.ctx.textAlign = 'center';  //设置或返回文本内容的当前对齐方式
                this.ctx.textBaseline = 'middle';  //设置或返回在绘制文本时使用的当前文本基线
                let hours = [3,4,5,6,7,8,9,10,11,12,1,2];
                for(let i=0;i<hours.length;i++){
                    let rad = 2*Math.PI / 12 *i;
                    let x = Math.cos(rad) * (this.r-30* this.rem);
                    let y = Math.sin(rad) * (this.r-30* this.rem);
                    this.ctx.fillText(hours[i],x,y);   //在画布上绘制“被填充的”文本,位置(x,y):.fillText(文本内容,x,y,允许的最大文本宽度,以像素计.可选);
                }
                /*hours.forEach(function(ele,i){
                    let rad = 2*Math.PI / 12 *i;
                    let x = Math.cos(rad) * (this.r-30* this.rem);
                    let y = Math.sin(rad) * (this.r-30* this.rem);
                    this.ctx.fillText(ele,x,y);   //在画布上绘制“被填充的”文本,位置(x,y):.fillText(文本内容,x,y,允许的最大文本宽度,以像素计.可选);
                })*/
                /*画刻度点*/
                for(let i=0;i<60;i++){
                    let rad = 2*Math.PI / 60 * i;
                    let x = Math.cos(rad) * (this.r-18* this.rem);  //r-18是为了把点画在圈内18px位置
                    let y = Math.sin(rad) * (this.r-18* this.rem);
                    this.ctx.beginPath();
                    if(i % 5 === 0){
                        this.ctx.fillStyle = '#000';
                        this.ctx.arc(x,y,2* this.rem,0,2*Math.PI,false)
                    }else{
                        this.ctx.fillStyle = '#ccc';
                        this.ctx.arc(x,y,2* this.rem,0,2*Math.PI,false)
                    }

                    this.ctx.fill();  // "填充"当前绘图
                }
            },

            drawHour:function(hour,minute){
                this.ctx.save();
                this.ctx.beginPath();
                let rad = 2 *Math.PI / 12 * hour;
                var mrad = 2*Math.PI/12/60*minute;
                this.ctx.rotate(rad + mrad);  //旋转当前绘图,参数为旋转角弧度
                this.ctx.lineWidth = 5* this.rem;
                this.ctx.lineCap = 'round';  //设置或返回线条的结束端点样式
                this.ctx.moveTo(0,10* this.rem);
                this.ctx.lineTo(0,-this.r/2);   添加一个新点坐标,然后在画布中创建从该点到最后lineTo指定点的线条
                this.ctx.stroke();
                this.ctx.restore();
            },

            drawMinute:function(minute){
                this.ctx.save();
                this.ctx.beginPath();
                let rad = 2 *Math.PI / 60 * minute;
                this.ctx.rotate(rad);
                this.ctx.lineWidth = 3* this.rem;
                this.ctx.lineCap = 'round';
                this.ctx.moveTo(0,15* this.rem);
                this.ctx.lineTo(0,-this.r/2-10* this.rem);
                this.ctx.stroke();
                this.ctx.restore();
            },
            /*画秒针:秒针不是一条线而是一个尖三角形,先lineTo三角形的三个点然后fill()填充*/
            drawSec:function(sec) {
                this.ctx.save();
                this.ctx.beginPath();
                this.ctx.fillStyle='#c14543';
                var rad=2*Math.PI/60*sec;//定义每秒旋转的弧度
                this.ctx.rotate(rad);

                this.ctx.moveTo(-2*this.rem,20*this.rem);  //  把路径移动到画布中的指定点,不创建线条,会作为后面创建线条的起始点
                this.ctx.lineTo(2*this.rem,20*this.rem);  //添加一个新点,然后在画布中创建从该点到最后lineTo指定点的线条
                this.ctx.lineTo(0,-this.r+18*this.rem);
                this.ctx.lineTo(-2*this.rem,20*this.rem);
                this.ctx.fill();       
                this.ctx.restore();
            },
            /*画中心固定点*/
            drawDot:function(){
                this.ctx.beginPath();
                this.ctx.fillStyle = 'green';
                this.ctx.arc(0,0,3* this.rem,0,2*Math.PI,false);
                this.ctx.fill();
            },

            draw:function(){
                this.ctx.clearRect(0,0,this.width,this.height);   //清除矩形画布,后面开始重新画圆
                this.drawBackground();
                this.drawDot();        
                let now = new Date();
                let hour = now.getHours();
                let munite = now.getMinutes();
                let sec = now.getSeconds();
                this.drawHour(hour,munite);
                this.drawMinute(munite);
                this.drawSec(sec);   
                this.ctx.restore();  //返回之前保存过的路径状态和属性,此处返回drawBackground()开始保存到的空画布状态
            }
        }
    }
</script>

本文时钟绘制根据慕课网canvas教程学习时间,做了部分小修改,以及更多的注释,后自己放在vue工程中做了修改测试,希望对大家理解学习Canvas有所帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值