第一章 基础知识

第五节 基本的绘制操作

    在下一章中,将详细讲述canvas的绘制。现在,为了让读者熟悉一下Canvas所提供的绘图API方法,所以咱们先来看看图1-13中的这个程序,它是一个带指针的时钟(analog clock)。

    该时钟的代码列在程序清单1-4之中,它用到了如下的Canvas绘图API:

    ·arc()

    ·beginPath()

    ·clearRect()

    ·fill()

    ·fillText()

    ·lineTo()

    ·moveTo()

    ·stroke()

    像Adobe Illustrator与Apple的Cocoa框架那样,Canvas也可以让开发者先创建不可见的路径,稍后再调用stroke()来描绘路径的边缘,或者调用fill()来对路径的内部进行填充,使路径变得可见。可以调用beginPath()方法来开始定义某一段路径。

    在时钟应用程序的代码中,drawCircle()方法绘制了一个表示钟面的圆形,该方法先调用beginPath()来开始定义路径,然后再调用arc()来创建一个圆形的路径。等到应用程序的代码调用了stroke()方法之后,刚才定义的路径才会变得可见。与之类似,该程序的drawCenter()方法也是通过组合调用beginPath()、arc()与fill()这几个方法来绘制时钟中心的那个小实心圆的。

    该应用程序的drawNumerals()方法通过调用fillText()来绘制钟面周围的数字,fillText()这个方法是用来在canvas中进行文本填充的。与arc()方法不同,fillText()方法并不会创建路径,而是立即将文本渲染到canvas之上。

    钟表的指针则是通过应用程序代码中的drawHand()方法来绘制的。该方法调用了如下三个用于绘制线段方法将钟表指针绘制出来:moveTo()、lineTo()与stroke()。先调用moveTo()方法将画笔(graphics pen)移动到canvas中的指定地点,然后调用lineTo()方法,在该点与另外一个指定的点之间绘制一条不可见的路径,最后使用stroke()方法将当前路径变为可见。

    应用程序通过调用setInterval()方法来制作时钟的动画效果。该方法每秒钟都会调用一次drawClock()函数。而后者则使用clearRect()方法来擦除canvas,然后再重绘时钟。

    程序清单1-4 一个基本的时钟程序

var canvas = document.getElementById('canvas'),

    context = canvas.getContext('2d'),

    FONT_HEIGHT = 15,

    MARGIN = 35,

    HAND_TRUNCATION = canvas.width/25,

    HOUR_HAND_TRUNCATION = canvas.width/10,

    NUMERAL_SPACING = 20,

    RADIUS = canvas.width/2 - MARGIN,

    HAND_RADIUS = RADIUS + NUMERAL_SPACING;

//Functions..........................................................

function drawCircle() {

   context.beginPath();

   context.arc(canvas.width/2, canvas.height/2,

               RADIUS, 0, Math.PI*2, true);

   context.stroke();

}

function drawNumerals() {

   var numerals = [ 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12 ],

       angle = 0,

       numeralWidth = 0;

   numerals.forEach(function(numeral) {

      angle = Math.PI/6 * (numeral-3);

      numeralWidth =context.measureText(numeral).width;

      context.fillText(numeral,

         canvas.width/2 + Math.cos(angle)*(HAND_RADIUS) -

            numeralWidth/2,

         canvas.height/2 + Math.sin(angle)*(HAND_RADIUS) +

            FONT_HEIGHT/3);

  });

}

function drawCenter() {

   context.beginPath();

   context.arc(canvas.width/2, canvas.height/2,5, 0, Math.PI*2, true);

   context.fill();

}

function drawHand(loc, isHour) {

   var angle = (Math.PI*2) * (loc/60) -Math.PI/2,

      handRadius = isHour ? RADIUS -HAND_TRUNCATION-HOUR_HAND_TRUNCATION

                          : RADIUS -HAND_TRUNCATION;

   context.moveTo(canvas.width/2,canvas.height/2);

   context.lineTo(canvas.width/2 +Math.cos(angle)*handRadius,

                  canvas.height/2 +Math.sin(angle)*handRadius);

   context.stroke();

}

function drawHands() {

   var date = new Date,

       hour = date.getHours();

   hour = hour > 12 ? hour - 12 : hour;

   drawHand(hour*5 + (date.getMinutes()/60)*5,true,0.5);

   drawHand(date.getMinutes(), false,0.5);

   drawHand(date.getSeconds(), false,0.2);

}

function drawClock() {

   context.clearRect(0,0,canvas.width,canvas.height);

   drawCircle();

   drawCenter();

   drawHands();

   drawNumerals();

}

//Initialization................................................

context.font = FONT_HEIGHT + 'px Arial';

loop = setInterval(drawClock, 1000);

    提示:进一步研究路径、描边与填充

    本节的时钟范例程序展示了canvas图形绘制的概况,在第2章之中,我们将详细地研究如何在canvas中进行图形的绘制与处理。