您的位置:首页 > 产品设计 > UI/UE

Canvas学习之绘制时钟

2017-09-22 16:25 274 查看
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有所帮助!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  canvas vue