- 1.1. The canvas Element
- 1.2. Canvas Contexts
- 1.3. Canonical Examples in This Book
- 1.4. Getting Started
- 1.5. Fundamental Drawing Operations
- 1.6. Event Handling
- 1.7. Saving and Restoring the Drawing Surface
- 1.8. Using HTML Elements in a Canvas
- 1.9. Printing a Canvas
- 1.10. Offscreen Canvases
- 1.11. A Brief Math Primer
- 1.12. Conclusion
1.5. Fundamental Drawing Operations
In the next chapter we will look closely at drawing in a canvas. For now, however, to familiarize you with the drawing methods that the Canvas API provides, let’s begin with the application shown in Figure 1.13, which implements an analog clock.
Figure 1.13. A clock
The clock application, which is listed in Example 1.4, uses the following drawing methods from the Canvas API:
- arc()
- beginPath()
- clearRect()
- fill()
- fillText()
- lineTo()
- moveTo()
- stroke()
Like Adobe Illustrator and Apple’s Cocoa, Canvas lets you draw shapes by creating invisible paths that you subsequently make visible with calls to stroke(), which strokes the outline of the path, or fill(), which fills the inside of the path. You begin a path with the beginPath() method.
The clock application’s drawCircle() method draws the circle representing the clock face by invoking beginPath() to begin a path, and subsequently invokes arc() to create a circular path. That path is invisible until the application invokes stroke(). Likewise, the application’s drawCenter() method draws the small filled circle at the center of the clock with a combination of beginPath(), arc(), and fill().
The application’s drawNumerals() method draws the numbers around the face of the clock with the fillText() method, which draws filled text in the canvas. Unlike the arc() method, fillText() does not create a path; instead, fillText() immediately renders text in the canvas.
The clock hands are drawn by the application’s drawHand() method, which uses three methods to draw the lines that represent the clock hands: moveTo(), lineTo(), and stroke(). The moveTo() method moves the graphics pen to a specific location in the canvas, lineTo() draws an invisible path to the location that you specify, and stroke() makes the current path visible.
The application animates the clock with setInterval(), which invokes the application’s drawClock() function once every second. The drawClock() function uses clearRect() to erase the canvas, and then it redraws the clock.
Example 1.4. A basic clock
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
);