技术开发 频道

HTML5的绝活:巧用Canvas制作会动的时钟

        【IT168 技术】这次我们的目标是画一个会和时间同步的时钟,不过没有美学感觉,样子丑的厉害。


  HTML5支持canvas了,我们可以直接在页面上绘图了,我看了下canvas和GDI+的接口差不多,所以我们先了解些基本的概念和方式,然后来做一个应用吧。

  我们做所有的画之情需要一个画布,html的canvas标签就是帮我们声明了一个画布。

<canvas id="mycanvas">  
</canvas>

       这个默认的画布的大小是300*150,接下来的工作大多就是javaScript来做了。

  首先要实例化这个画布

[javascript] view plaincopy
$(  
function() {  
    var canvas
= document.getElementById("mycanvas");  
    $.log(canvas.width);  
    $.log(canvas.height);  
    var context
= canvas.getContext("2d");  
    $.log(context.canvas);  
    $.log(context.fillStyle);
//要填充的区域的颜色  
    $.log(context.strokeStyle);
//要绘制的线条的颜色  
    $.log(context.lineCap);
//笔帽样式  
    $.log(context.lineJoin);
//两条连续线段的连接样式  
    $.log(context.lineWidth);
//线段的宽度  
    $.log(context.miterLimit);
//斜联接  
    $.log(context.shadowColor);
//阴影的颜色,默认为#000000,  
    $.log(context.shadowOffsetX);
//阴影在x方向上的偏移量,默认为0,不受坐标转换的影响。  
    $.log(context.shadowOffsetY);
//阴影在y方向上的偏移量,默认为0,不受坐标转换的影响。  
    $.log(context.shadowBlur);
//阴影的模糊度,默认为0,负数值将会被忽略  
}  
);

 

  上面的结果你可以得到一个大致的想法,就是content可以认为是我们将来作画用的画笔(估计有专业人士对强烈抗议,我直接忽略),canvas就是我们的画布。我们现在的画笔是2D的画笔,换句话说就是画平面几何的画笔。

  接下来,就是我们利用这个画笔来学习怎么画了

  各种线

[javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  var context
= canvas.getContext("2d");

  context.strokeStyle
= "rgb(255, 0, 0)";

  context.beginPath();

  context.lineCap
= "butt"; //默认

  context.lineWidth
= 10;

  context.moveTo(
10, 10);

  context.lineTo(
100, 10); //简单的一条线

  context.stroke();
//该方法真正在画布上绘制该线段

  context.beginPath();

  context.lineCap
= "round"; //圆形线头

  context.moveTo(
10, 30);

  context.lineTo(
100, 30);

  context.stroke();
//该方法真正在画布上绘制该线段

  context.beginPath();

  context.lineCap
= "square"; //方形线头

  context.moveTo(
10, 50);

  context.lineTo(
100, 50);

  context.stroke();
//该方法真正在画布上绘制该线段

  }

  );

 

  各种阴影

 [javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  var context
= canvas.getContext("2d");

  context.strokeStyle
= "rgb(255, 0, 0)";

  context.lineWidth
= 10;

  context.shadowColor
= "#0000FF";

  context.beginPath();

  context.lineCap
= "round";

  context.moveTo(
10, 10);

  context.lineTo(
100, 10);

  context.shadowOffsetX
= 10;

  context.shadowBlur
= 10;

  context.stroke();

  context.beginPath();

  context.lineCap
= "round";

  context.moveTo(
10, 30);

  context.lineTo(
100, 30);

  context.shadowOffsetY
= 10;

  context.shadowBlur
= 10;

  context.stroke();

  }

  );

  各种线∠连接

  [javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  var context
= canvas.getContext("2d");

  context.strokeStyle
= "rgb(255, 0, 0)";

  context.lineWidth
= 10;

  context.shadowColor
= "#0000FF";

  context.beginPath();

  context.lineJoin
= "miter"; //两条线段的外边缘一直扩展到它们相交

  context.moveTo(
10, 70);

  context.lineTo(
50, 10);

  context.lineTo(
80, 70);

  context.stroke();

  context.lineJoin
= "bevel"; //以一个斜边进行连接

  context.moveTo(
100, 70);

  context.lineTo(
140, 10);

  context.lineTo(
180, 70);

  context.stroke();

  context.lineJoin
= "round"; //:以一个圆弧边进行连接

  context.beginPath();

  context.moveTo(
200, 70);

  context.lineTo(
240, 10);

  context.lineTo(
280, 70);

  context.stroke();

  context.closePath();
//关闭path

  }

  );

  mitre的限定

  [javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  var context
= canvas.getContext("2d");

  context.strokeStyle
= "rgb(255, 0, 0)";

  context.lineWidth
= 10;

  context.shadowColor
= "#0000FF";

  context.beginPath();

  context.miterLimit
= 1; //miterLimit 属性为斜面的长度设置一个上限。

  
//只对线条使用设置为 "miter" 的 lineJoin 属性绘制并且两条线段以锐角相交的时候有效

  context.lineJoin
= "miter"; //两条线段的外边缘一直扩展到它们相交

  context.moveTo(
10, 70);

  context.lineTo(
50, 10);

  context.lineTo(
80, 70);

  context.stroke();

  }

  );

  各种几何图形

  [javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  canvas.height
= 500; //改变默认高度

  canvas.width
= 500;

  var context
= canvas.getContext("2d");

  context.strokeStyle
= "rgb(255, 0, 0)";

  context.fillStyle
= "#AABBCC";

  context.lineWidth
= 2;

  context.shadowColor
= "#0000FF";

  
//矩形

  context.beginPath();

  context.fillRect(
10, 10, 50, 50); //实体矩形:x,y,width,height

  context.strokeRect(
70, 10, 50, 50)//空心矩形:x,y,width,height

  
//context.move(10,100);

  
//圆弧:x, y, radius, startAngle, endAngle, anticlockwise

  context.beginPath();

  context.arc(
35, 110, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.stroke();

  
//context.closePath();

  context.beginPath();

  context.arc(
85, 110, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 180, false);

  context.stroke();

  
//context.closePath();

  context.beginPath();

  context.arc(
135, 110, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 180, true);

  context.stroke();

  
//context.closePath();

  context.beginPath();

  context.arc(
185, 110, 25, (Math.PI / 180) * 180, (Math.PI / 180) * 360, true);

  context.stroke();

  
//context.closePath();

  context.beginPath();

  context.arc(
235, 110, 25, (Math.PI / 180) * 90, (Math.PI / 180) * 0, false);

  context.fillStyle
= "blue";

  context.fill();

  
//context.stroke();

  
//context.closePath();

  context.beginPath();

  context.arc(
285, 110, 25, (Math.PI / 180) * 180, (Math.PI / 180) * 45, false);

  context.closePath();

  context.stroke();

  context.beginPath();

  context.arc(
335, 110, 25, (Math.PI / 180) * 180, (Math.PI / 180) * 45, false);

  context.closePath();

  context.fillStyle
= "blue";

  context.fill();

  context.stroke();

  
//曲线

  context.beginPath();

  context.moveTo(
10, 160); //二次贝塞尔曲线的起始点

  
//controlX, controlY, endX, endY

  context.quadraticCurveTo(
70, 280, 235, 140);

  context.stroke();

  context.closePath();

  context.beginPath();

  context.moveTo(
10, 300); //三次贝塞尔曲线的起始点

  
//controlX1, controlY1, controlX2, controlY2, endX, endY

  context.bezierCurveTo(
70, 280, 50, 400, 235, 190);

  context.stroke();

  context.closePath();

  }

  );

  各种变换

  记得CSS3中的transform不?canvas肯定也有啊

  平移

  [javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  canvas.height
= 500; //改变默认高度

  canvas.width
= 500;

  var context
= canvas.getContext("2d");

  context.strokeStyle
= "rgb(255, 0, 0)";

  context.fillStyle
= "#AABBCC";

  context.lineWidth
= 2;

  context.shadowColor
= "#0000FF";

  context.moveTo(
10, 10);

  
//context.beginPath();

  
//context.beginPath();

  context.fillRect(
10, 10, 50, 50); //实体矩形:x,y,width,height

  
//context.stroke();

  $(canvas).on(

  
"click",

  {
"context": context },

  
function(e) {

  $.log(e.data.context);

  var ctx
= e.data.context;

  ctx.translate(
10, 10); //再最后的路径点上偏移10*10的位置

  context.fillRect(
10, 10, 50, 50);

  context.stroke();

  }

  );

  }

  );

  缩放

  [javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  canvas.height
= 500; //改变默认高度

  canvas.width
= 500;

  var context
= canvas.getContext("2d");

  context.strokeStyle
= "rgb(255, 0, 0)";

  context.fillStyle
= "#AABBCC";

  context.lineWidth
= 2;

  context.shadowColor
= "#0000FF";

  context.moveTo(
10, 10);

  
//context.beginPath();

  
//context.beginPath();

  context.fillRect(
10, 10, 50, 50); //实体矩形:x,y,width,height

  
//context.stroke();

  $(canvas).on(

  
"click",

  {
"context": context },

  
function(e) {

  $.log(e.data.context);

  var ctx
= e.data.context;

  ctx.scale(
1.1, 1.1); //在最后的大小基础上缩放倍数 必须是正数

  context.fillRect(
10, 10, 50, 50);

  context.stroke();

  }

  );

  }

  );

 

  旋转

[javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  canvas.height
= 500; //改变默认高度

  canvas.width
= 500;

  var context
= canvas.getContext("2d");

  context.strokeStyle
= "rgb(255, 0, 0)";

  context.fillStyle
= "#AABBCC";

  context.lineWidth
= 2;

  context.shadowColor
= "#0000FF";

  context.moveTo(
10, 10);

  
//context.beginPath();

  
//context.beginPath();

  context.fillRect(
10, 10, 50, 50); //实体矩形:x,y,width,height

  
//context.stroke();

  $(canvas).on(

  
"click",

  {
"context": context },

  
function(e) {

  $.log(e.data.context);

  var ctx
= e.data.context;

  ctx.rotate((Math.PI
/ 180) * 10); //旋转的角度,旋转的中心是canvas坐标原点

  context.fillRect(
10, 10, 50, 50);

  context.stroke();

  }

  );

  }

  );

 

 

${PageNumber}

 transform,transform的参数比较多,也比较难理解,简单的说transform是最自由的变形方式,下面给出些参考  

[javascript] view plaincopy

  
//以下两段代码结果一致

  context.transform(
1, 0, 0, 1, 10, 10)

  context.translate(
10, 10);

  
//以下两段代码结果一致

  context.transform(
10, 0, 0, 10, 0, 0);

  context.scale(
10, 10);

  
//以下三段代码结果一致

  context.transform(Math.cos((Math.PI
/ 180) * 10), Math.sin((Math.PI / 180) * 10), -Math.sin((Math.PI / 180) * 10), Math.cos((Math.PI / 180)) * 10, 0, 0);

  context.transform(
-Math.sin((Math.PI/180)*10),Math.cos((Math.PI/180)*10),Math.cos((Math.PI/180)*10),Math.sin((Math.PI/180)*10), 0,0);

  context.rotate(
10);

 

  组合

 [javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  canvas.height
= 100;

  canvas.width
= 100;

  var context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  
//默认 新图形会覆盖在原有内容之上

  context.globalCompositeOperation
= "source-over";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  $(canvas).toggle(

  
function() {

  canvas.width
= 100;

  
// 原有内容之下绘制新图形

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "destination-over";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//新图形会仅仅出现与原有内容重叠的部分。其它区域都变成透明的

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "source-in";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//原有内容中与新图形重叠的部分会被保留,其它区域都变成透明的destination-in

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "destination-in";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//只有新图形中与原有内容不重叠的部分会被绘制出来source-out

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "source-out";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//原有内容中与新图形不重叠的部分会被保留

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "destination-out";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//新图形中与原有内容重叠的部分会被绘制,并覆盖于原有内容之上

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "source-atop";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//原有内容中与新内容重叠的部分会被保留,并会在原有内容之下绘制新图形

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "destination-atop";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//两图形中重叠部分作加色处理

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "lighter";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//两图形中重叠的部分作减色处理darker

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "darker";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//重叠的部分会变成透明

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "xor";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  },

  
function() {

  canvas.width
= 100;

  
//只有新图形会被保留,其它都被清除掉

  context.clearRect(
0, 0, 500, 500);

  context.beginPath();

  context
= canvas.getContext("2d");

  context.fillStyle
= "#AABBCC";

  context.fillRect(
10, 10, 50, 50);

  context.globalCompositeOperation
= "copy";

  context.fillStyle
= "blue";

  context.arc(
70, 30, 25, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.fill();

  $(
"span").html(context.globalCompositeOperation);

  alert(
"演示结束");

  }

  );

  }

  );

 

  字体(看文档说canvas的字体支持CSS样式的描写,但是,我不知道怎么样让canvas的font支持CSS3的在线字体)

[javascript] view plaincopy

  我们尝试写一圈旋转的文字,吧上面的知识点合起来看看效果

 [javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  canvas.height
= 500;

  canvas.width
= 500;

  var context
= canvas.getContext("2d");

  context.translate(
150, 150);

  context.scale(
0.7, 0.7);

  context.font
= "12px Tahoma";

  
for (var i = 0; i < 12; i++) {

  context.fillText((i
+ 3) % 12 == 0 ? 12 : (i + 3) % 12, 150, 10);

  context.rotate((Math.PI
/ 6));

  }

  }

  );

 

  在具体绘制的时候,定位总是让我这样没有空间感的人感觉痛苦,所以我现在canvas上画上很多格子,帮助我进行布局

[javascript] view plaincopy

  $(

  
function() {

  var canvas
= document.getElementById("mycanvas");

  canvas.height
= 500;

  canvas.width
= 500;

  var context
= canvas.getContext("2d");

  context.lineWidth
= 1;

  context.strokeStyle
= "rgb(211,211,211)";

  
for (var i = 0; i < 50; i++) {

  $.log(i);

  context.moveTo(i
* 10, 0);

  context.lineTo(i
* 10, 500);

  context.stroke();

  }

  
for (var i = 0; i < 50; i++) {

  $.log(i);

  context.moveTo(
0, i * 10);

  context.lineTo(
500, i * 10);

  context.stroke();

  }

  }

  );

 

  前面的准备工作都完成了,现在我们来综合下,完成一个具有时分秒的会动的钟

  

[javascript] view plaincopy

  $(

  
function() {

  clock();

  setInterval(clock,
1000);

  }

  );

  
function clock() {

  var canvas
= document.getElementById("mycanvas");

  canvas.height
= 500;

  canvas.width
= 500;

  var context
= canvas.getContext("2d");

  context.beginPath();

  context.lineWidth
= 1;

  context.strokeStyle
= "rgb(211,211,211)";

  
for (var i = 0; i < 50; i++) {

  $.log(i);

  context.moveTo(i
* 10, 0);

  context.lineTo(i
* 10, 500);

  context.stroke();

  }

  
for (var i = 0; i < 50; i++) {

  $.log(i);

  context.moveTo(
0, i * 10);

  context.lineTo(
500, i * 10);

  context.stroke();

  }

  context.beginPath();

  context.strokeStyle
= "rgb(255,0,0)";

  context.arc(
250, 250, 200, (Math.PI / 180) * 0, (Math.PI / 180) * 360, false);

  context.stroke();

  context.save();
//存储当前画布坐标系状态

  context.beginPath();

  context.font
= "14px Tahoma"

  context.translate(
255, 255); //将坐标系坐标原点移至图中间

  context.strokeStyle
= "#FFFFFF";

  
for (var i = 0; i < 12; i++) {

  context.fillText((i
+ 3) % 12 == 0 ? 12 : (i + 3) % 12, 180, 0);

  context.rotate((Math.PI
/ 6));

  }

  context.restore();

  context.save();

  context.beginPath();

  context.lineWidth
= 5;

  context.translate(
255, 255); //将坐标系坐标原点移至图中间

  
for (i = 0; i < 60; i++) {

  
if (i % 5 != 0) {

  context.beginPath();

  context.moveTo(
180, 0);

  context.lineTo(
190, 0);

  context.stroke();

  }

  context.rotate(Math.PI
/ 30);

  }

  context.restore();

  var
now = new Date();

  var sec
= now.getSeconds();

  var min
= now.getMinutes();

  var hr
= now.getHours() >= 12 ? now.getHours() - 12 : now.getHours();

  context.save();

  context.translate(
255, 255); //将坐标系坐标原点移至图中间

  
// - (Math.PI / 6) * 3 是因为0度在3点这里

  context.rotate(hr
* (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec - (Math.PI / 6) * 3);

  context.lineWidth
= 14;

  context.beginPath();

  context.moveTo(
-20, 0);

  context.lineTo(
150, 0);

  context.stroke();

  context.restore();

  context.save();

  context.translate(
255, 255); //将坐标系坐标原点移至图中间

  context.rotate(min
* (Math.PI / 30) + (Math.PI / 1800) * sec - (Math.PI / 6) * 3)

  context.lineWidth
= 10;

  context.beginPath();

  context.moveTo(
-28, 0);

  context.lineTo(
160, 0);

  context.stroke();

  context.restore();

  context.save();

  context.translate(
255, 255); //将坐标系坐标原点移至图中间

  context.lineWidth
= 1;

  context.rotate(sec
* (Math.PI / 30) + (Math.PI / 1800) * sec - (Math.PI / 6) * 3)

  context.lineWidth
= 10;

  context.beginPath();

  context.moveTo(
-28, 0);

  context.lineTo(
160, 0);

  context.stroke();

  context.restore();

  }


 

0
相关文章