diff --git a/2ech16.md b/3ech17.md index b3a1f00..4814dc7 100644 --- a/2ech16.md +++ b/3ech17.md @@ -1,20 +1,20 @@ -# Chapter 16Drawing on Canvas +# Chapter 17Drawing on Canvas > Drawing is deception. > > <footer>M.C. Escher, <cite>cited by Bruno Ernst in The Magic Mirror of M.C. Escher</cite></footer> -Browsers give us several ways to display graphics. The simplest way is to use styles to position and color regular DOM elements. This can get you quite far, as the game in the [previous chapter](15_game.html#game) showed. By adding partially transparent background images to the nodes, we can make them look exactly the way we want. It is even possible to rotate or skew nodes by using the `transform` style. +Browsers give us several ways to display graphics. The simplest way is to use styles to position and color regular DOM elements. This can get you quite far, as the game in the [previous chapter](16_game.html) shows. By adding partially transparent background images to the nodes, we can make them look exactly the way we want. It is even possible to rotate or skew nodes with the `transform` style. But we'd be using the DOM for something that it wasn't originally designed for. Some tasks, such as drawing a line between arbitrary points, are extremely awkward to do with regular HTML elements. -There are two alternatives. The first is DOM-based but utilizes _Scalable Vector Graphics (SVG)_, rather than HTML elements. Think of SVG as a dialect for describing documents that focuses on shapes rather than text. You can embed an SVG document in an HTML document, or you can include it through an `<img>` tag. +There are two alternatives. The first is DOM-based but utilizes _Scalable Vector Graphics_ (SVG), rather than HTML. Think of SVG as a document-markup dialect that focuses on shapes rather than text. You can embed an SVG document directly in an HTML document or include it with an `<img>` tag. The second alternative is called a _canvas_. A canvas is a single DOM element that encapsulates a picture. It provides a programming interface for drawing shapes onto the space taken up by the node. The main difference between a canvas and an SVG picture is that in SVG the original description of the shapes is preserved so that they can be moved or resized at any time. A canvas, on the other hand, converts the shapes to pixels (colored dots on a raster) as soon as they are drawn and does not remember what these pixels represent. The only way to move a shape on a canvas is to clear the canvas (or the part of the canvas around the shape) and redraw it with the shape in a new position. ## SVG -This book will not go into SVG in detail, but I will briefly explain how it works. At the [end of the chapter](16_canvas.html#graphics_tradeoffs), I'll come back to the trade-offs that you must consider when deciding which drawing mechanism is appropriate for a given application. +This book will not go into SVG in detail, but I will briefly explain how it works. At the [end of the chapter](17_canvas.html#graphics_tradeoffs), I'll come back to the trade-offs that you must consider when deciding which drawing mechanism is appropriate for a given application. This is an HTML document with a simple SVG picture in it: @@ -29,10 +29,10 @@ This is an HTML document with a simple SVG picture in it: The `xmlns` attribute changes an element (and its children) to a different _XML namespace_. This namespace, identified by a URL, specifies the dialect that we are currently speaking. The `<circle>` and `<rect>` tags, which do not exist in HTML, do have a meaning in SVG—they draw shapes using the style and position specified by their attributes. -These tags create DOM elements, just like HTML tags. For example, this changes the `<circle>` element to be colored cyan instead: +These tags create DOM elements, just like HTML tags, that scripts can interact with. For example, this changes the `<circle>` element to be colored cyan instead: ``` -var circle = document.querySelector("circle"); +let circle = document.querySelector("circle"); circle.setAttribute("fill", "cyan"); ``` @@ -40,21 +40,21 @@ circle.setAttribute("fill", "cyan"); Canvas graphics can be drawn onto a `<canvas>` element. You can give such an element `width` and `height` attributes to determine its size in pixels. -A new canvas is empty, meaning it is entirely transparent and thus shows up simply as empty space in the document. +A new canvas is empty, meaning it is entirely transparent and thus shows up as empty space in the document. -The `<canvas>` tag is intended to support different styles of drawing. To get access to an actual drawing interface, we first need to create a _context_, which is an object whose methods provide the drawing interface. There are currently two widely supported drawing styles: `"2d"` for two-dimensional graphics and `"webgl"` for three-dimensional graphics through the OpenGL interface. +The `<canvas>` tag is intended to allow different styles of drawing. To get access to an actual drawing interface, we first need to create a _context_, an object whose methods provide the drawing interface. There are currently two widely supported drawing styles: `"2d"` for two-dimensional graphics and `"webgl"` for three-dimensional graphics through the OpenGL interface. -This book won't discuss WebGL. We stick to two dimensions. But if you are interested in three-dimensional graphics, I do encourage you to look into WebGL. It provides a very direct interface to modern graphics hardware and thus allows you to render even complicated scenes efficiently, using JavaScript. +This book won't discuss WebGL—we'll stick to two dimensions. But if you are interested in three-dimensional graphics, I do encourage you to look into WebGL. It provides a very direct interface to graphics hardware and allows you to render even complicated scenes efficiently, using JavaScript. -A context is created through the `getContext` method on the `<canvas>` element. +You create a context with the `getContext` method on the `<canvas>` DOM element. ```
Before canvas.
After canvas.
@@ -64,22 +64,22 @@ After creating the context object, the example draws a red rectangle 100 pixels Just like in HTML (and SVG), the coordinate system that the canvas uses puts (0,0) at the top-left corner, and the positive y-axis goes down from there. So (10,10) is 10 pixels below and to the right of the top-left corner. -## Filling and stroking +## Lines and surfaces In the canvas interface, a shape can be _filled_, meaning its area is given a certain color or pattern, or it can be _stroked_, which means a line is drawn along its edge. The same terminology is used by SVG. The `fillRect` method fills a rectangle. It takes first the x- and y-coordinates of the rectangle's top-left corner, then its width, and then its height. A similar method, `strokeRect`, draws the outline of a rectangle. -Neither method takes any further parameters. The color of the fill, thickness of the stroke, and so on are not determined by an argument to the method (as you might justly expect) but rather by properties of the context object. +Neither method takes any further parameters. The color of the fill, thickness of the stroke, and so on are not determined by an argument to the method (as you might reasonably expect) but rather by properties of the context object. -Setting `fillStyle` changes the way shapes are filled. It can be set to a string that specifies a color, and any color understood by CSS can also be used here. +The `fillStyle` property controls the way shapes are filled. It can be set to a string that specifies a color, using the color notation used by CSS. The `strokeStyle` property works similarly but determines the color used for a stroked line. The width of that line is determined by the `lineWidth` property, which may contain any positive number. ``` ``` -When no `width` or `height` attribute is specified, as in the previous example, a canvas element gets a default width of 300 pixels and height of 150 pixels. +When no `width` or `height` attribute is specified, as in the example, a canvas element gets a default width of 300 pixels and height of 150 pixels. ## Paths @@ -96,9 +96,9 @@ A path is a sequence of lines. The 2D canvas interface takes a peculiar approach ``` ``` -This example draws a filled triangle. Note that only two of the triangle's sides are explicitly drawn. The third, from the bottom-right corner back to the top, is implied and won't be there when you stroke the path. +This example draws a filled triangle. Note that only two of the triangle's sides are explicitly drawn. The third, from the bottom-right corner back to the top, is implied and wouldn't be there when you stroke the path. You could also use the `closePath` method to explicitly close a path by adding an actual line segment back to the path's start. This segment _is_ drawn when stroking the path. ## Curves -A path may also contain curved lines. These are, unfortunately, a bit more involved to draw than straight lines. +A path may also contain curved lines. These are unfortunately a bit more involved to draw. -The `quadraticCurveTo` method draws a curve to a given point. To determine the curvature of the line, the method is given a control point as well as a destination point. Imagine this control point as _attracting_ the line, giving the line its curve. The line won't go through the control point. Rather, the direction of the line at its start and end points will be such that it aligns with the line from there to the control point. The following example illustrates this: +The `quadraticCurveTo` method draws a curve to a given point. To determine the curvature of the line, the method is given a control point as well as a destination point. Imagine this control point as _attracting_ the line, giving it its curve. The line won't go through the control point, but its direction at the start and end points will be such that a straight in that direction would point towards the control point. The following example illustrates this: ``` -``` - -The `arcTo` method won't draw the line from the end of the rounded part to the goal position, though the word _to_ in its name would suggest it does. You can follow up with a call to `lineTo` with the same goal coordinates to add that part of the line. - -To draw a circle, you could use four calls to `arcTo` (each turning 90 degrees). But the `arc` method provides a simpler way. It takes a pair of coordinates for the arc's center, a radius, and then a start and end angle. - -Those last two parameters make it possible to draw only part of circle. The angles are measured in radians, not degrees. This means a full circle has an angle of 2π, or `2 * Math.PI`, which is about 6.28\. The angle starts counting at the point to the right of the circle's center and goes clockwise from there. You can use a start of 0 and an end bigger than 2π (say, 7) to draw a full circle. +Those last two parameters make it possible to draw only part of the circle. The angles are measured in radians, not degrees. This means a full circle has an angle of 2π, or `2 * Math.PI`, which is about 6.28\. The angle starts counting at the point to the right of the circle's center and goes clockwise from there. You can use a start of 0 and an end bigger than 2π (say, 7) to draw a full circle. ``` ``` -The resulting picture contains a line from the right of the full circle (first call to `arc`) to the right of the quarter-circle (second call). Like other path-drawing methods, a line drawn with `arc` is connected to the previous path segment by default. You'd have to call `moveTo` or start a new path if you want to avoid this. +The resulting picture contains a line from the right of the full circle (first call to `arc`) to the right of the quarter-circle (second call). Like other path-drawing methods, a line drawn with `arc` is connected to the previous path segment. You can call `moveTo` or start a new path to avoid this. ## Drawing a pie chart Imagine you've just taken a job at EconomiCorp, Inc., and your first assignment is to draw a pie chart of their customer satisfaction survey results. -The `results` variable contains an array of objects that represent the survey responses. +The `results` binding contains an array of objects that represent the survey responses. ``` -var results = [ +const results = [ {name: "Satisfied", count: 1043, color: "lightblue"}, {name: "Neutral", count: 563, color: "lightgreen"}, {name: "Unsatisfied", count: 510, color: "pink"}, @@ -227,14 +208,13 @@ To draw a pie chart, we draw a number of pie slices, each made up of an arc and ``` ``` -But a chart that doesn't tell us what it means isn't very helpful. We need a way to draw text to the canvas. +But a chart that doesn't tell us what the slices mean isn't very helpful. We need a way to draw text to the canvas. ## Text -A 2D canvas drawing context provides the methods `fillText` and `strokeText`. The latter can be useful for outlining letters, but usually `fillText` is what you need. It will fill the given text with the current `fillColor`. +A 2D canvas drawing context provides the methods `fillText` and `strokeText`. The latter can be useful for outlining letters, but usually `fillText` is what you need. It will fill the outline of the given text with the current `fillStyle`. ``` ``` -You can specify the size, style, and font of the text with the `font` property. This example just gives a font size and family name. You can add `italic` or `bold` to the start of the string to select a style. +You can specify the size, style, and font of the text with the `font` property. This example just gives a font size and family name. It is also possible to add `italic` or `bold` to the start of the string to select a style. -The last two arguments to `fillText` (and `strokeText`) provide the position at which the font is drawn. By default, they indicate the position of the start of the text's alphabetic baseline, which is the line that letters “stand” on, not counting hanging parts in letters like _j_ or _p_. You can change the horizontal position by setting the `textAlign` property to `"end"` or `"center"` and the vertical position by setting `textBaseline` to `"top"`, `"middle"`, or `"bottom"`. +The last two arguments to `fillText` and `strokeText` provide the position at which the font is drawn. By default, they indicate the position of the start of the text's alphabetic baseline, which is the line that letters “stand” on, not counting hanging parts in letters like _j_ or _p_. You can change the horizontal position by setting the `textAlign` property to `"end"` or `"center"` and the vertical position by setting `textBaseline` to `"top"`, `"middle"`, or `"bottom"`. -We will come back to our pie chart, and the problem of labeling the slices, in the [exercises](16_canvas.html#exercise_pie_chart) at the end of the chapter. +We'll come back to our pie chart, and the problem of labeling the slices, in the [exercises](17_canvas.html#exercise_pie_chart) at the end of the chapter. ## Images In computer graphics, a distinction is often made between _vector_ graphics and _bitmap_ graphics. The first is what we have been doing so far in this chapter—specifying a picture by giving a logical description of shapes. Bitmap graphics, on the other hand, don't specify actual shapes but rather work with pixel data (rasters of colored dots). -The `drawImage` method allows us to draw pixel data onto a canvas. This pixel data can originate from an `<img>` element or from another canvas, and neither has to be visible in the actual document. The following example creates a detached `<img>` element and loads an image file into it. But it cannot immediately start drawing from this picture because the browser may not have fetched it yet. To deal with this, we register a `"load"` event handler and do the drawing after the image has loaded. +The `drawImage` method allows us to draw pixel data onto a canvas. This pixel data can originate from an `<img>` element or from another canvas. The following example creates a detached `<img>` element and loads an image file into it. But it cannot immediately start drawing from this picture because the browser may not have loaded it yet. To deal with this, we register a `"load"` event handler and do the drawing after the image has loaded. ``` ``` -By default, `drawImage` will draw the image at its original size. You can also give it two additional arguments to dictate a different width and height. +By default, `drawImage` will draw the image at its original size. You can also give it two additional arguments to set a different width and height. When `drawImage` is given _nine_ arguments, it can be used to draw only a fragment of an image. The second through fifth arguments indicate the rectangle (x, y, width, and height) in the source image that should be copied, and the sixth to ninth arguments give the rectangle (on the canvas) into which it should be copied. This can be used to pack multiple _sprites_ (image elements) into a single image file and then draw only the part you need. For example, we have this picture containing a game character in multiple poses: - +