JavaScript Canvas – Graphics State

0


The state of Canvas Graphics looks complicated, but it is very easy to use and very useful. In this excerpt from Ian Elliot’s book on JavaScript graphics, we take a look at how to use state.

Now available in paperback or ebook from Amazon.

JavaScript bitmap graphics
With Canvas

Contents

  1. JavaScript graphics
  2. Getting started with Canvas
  3. Plot paths
    Extract: Basic paths
    Extract: Bézier curves
  4. Outline and fill
    Extract: Line properties
    Extract: Filling and holes
  5. Transformations
    Extract: Transformations
    Extract: Personalized contact details
    Extract the state of the graphics **NEW!
  6. Text
    Extract: Text, Typography & SVG
    Extract: Unicode
  7. Cutting, composition and effects
    Extract: Clipping & Basic Compositing
  8. Generating bitmaps
    Extract: Introduction to bitmaps
    Extract: Animation
  9. WebWorkers & OffscreenCanvas
    Extract: OffscreenCanvas
  10. Bit manipulation in JavaScript
    Extract: Bit manipulation
  11. Typed arrays
  12. Files, blobs, urls and fetch
    Extract: Blobs and files
    Extract: Read / write local files
  13. Image processing
    Extract: ImageData
    Extract: The Filter API
  14. WebGL 3D
  15. 2D WebGL
    Extract: WebGL Convolutions

In the chapter but not in this excerpt

Stack of States

In general, drawing many shapes involves changing the transformation and attributes used to draw the path. After modifying elements, you often want to restore the state of the original canvas. The state of the canvas is a set of properties that determines how graphics primitives will be drawn.

The state consists of:

  • the current transformation matrix

  • the current clipping region – see below

  • all drawing attributes such as fillStyle, lineWidth and so on.

In short, it includes everything that determines what the result of a drawing operation actually produces. Note that any current drawing operation, such as the current path or the current bitmap, is not part of the report.

Why are we concerned with defining the state of the context?

The answer is that there is a backup method that saves the current state to an internal state stack and a restore method that sets the state to the current top of the stack.

So, for example, you can define a fill color and save it to the report stack:

ctx.fillStyle = "rgb(200,0,0)";
ctx.save();
ctx.fillStyle = "rgb(0,200,0)";
ctx.save();
ctx.fillStyle = "rgb(0,0,200)";
ctx.fillRect (10, 10, 55, 50);

At this point, the current fill color is blue with green and red on the stack. Therefore, we have just drawn a blue rectangle. If we now restore from the top of the stack and draw a rectangle, it will be green:

ctx.restore();
ctx.fillRect (20, 20, 55, 50);

Repeat this one more time and we draw a red rectangle:

ctx.restore();
ctx.fillRect (30, 30, 55, 50);

Note that in this case we are only changing the fill color, but in practice the entire state of the drawing is saved and restored.

Red Square

You can use the state stack to change the state of the drawing to draw a sub-object, and then restore the state to continue drawing the main object.

Active transformations and state

There is another use of the state stack – another holistic approach to systematic drawing.

We have already encountered the idea of ​​defining a path to draw a “standard” shape. You draw all your paths starting from 0.0 and with a unit size. Then when you want a shape at x, y, and size s, you translate the coordinate system to x, y and the scale to s. Of course, if you save the state before drawing and restore it after, nothing has changed and you are ready to draw the next standard shape.

For example, if you define a unit square:

var path1 = new Path2D();
path1.rect(0,0,1,1);

you can draw it at 100 200 and size 400 using:

ctx.save();     
ctx.translate(100,200);
ctx.scale(400,400);
ctx.lineWidth=1/400;    
ctx.stroke(path1);
ctx.restore();

Note that you must perform the transformation in the reverse order of what you might think and you must remember to specify the row width in the new units. Due to backup and restore, you can draw the next standard shape using the same method.

This is a particularly useful approach for working with a library of complex shapes. For example, the spaceship SVG string used previously can be changed to be drawn from 0.0:

path1=new Path2D( "m 0,0 c 2.891926,-25.77092 -1.958475,
-65.0136 -13.037221,-73.92172 -11.072474,
8.90817 -17.526591,48.1508 -14.634563,
73.92172 -9.479011,8.46903 -9.015897,
17.40218 -9.068381,29.71617 l 5.92022,
-0.074 c 0,0 2.493141,-15.15787 5.105513,
-16.98251 l 3.15542,8.06751 -1.537022,
9.15649 c 13.277647,-0.17974 7.242537,
-0.17974 20.52018,0 l -1.537022,-9.15649 3.15552,
-8.06751 c 2.979241,2.08093 3.605942,
17.00168 3.605942,17.00168 l 6.61799,0.0548 c -0.0526,
-12.31399 1.21238,-21.24714 -8.266576,-29.71617 z");

It can now be drawn at any position, angle, and scale using:

ctx.save();
ctx.translate(200,200); 
ctx.rotate(Math.PI/8); 
ctx.scale(2,2); 
ctx.lineWidth=1/2;
ctx.stroke(path1);
ctx.restore();

The only problem with this is defining the “center” of the shape. The center is the point of the shape that is located at 0.0 when the shape is drawn at 0.0. Usually you want it to be in a “natural” position – the center of the rocket or the tip of the nose. The reason is that the pixel at 0.0 when the shape is drawn is the one at x, y when you translate to x, y and this is the point around which any rotation is done. It is generally easy to fix the center when building shapes by hand – it is more difficult when using an automatic method such as InkScape. In these cases, the simplest solution is to find an initial offset that places the center where you want it when the shape is drawn at 0, 0. For the spaceship, changing the initial m command to m 13.5,74.5 places the center at the top of the nose.


rocket angle

Summary

  • Transformations include rotate, scale, and translate.

  • You can write all three as a simple matrix if you use homogeneous coordinates (x, y, 1)

  • You can set the transformation matrix directly or use one of the utility methods to set the scale, rotate, and translate.

  • The order in which you apply the transformations makes a difference.

  • You can think of transformations as actively moving something or just changing coordinates. Canvas transformations change the coordinate system.

  • If you want to think “actively,” think about what transformations you want to perform on a shape, and then apply them in reverse order before you draw the shape.

  • One approach to organizing graphics is to draw everything centered at the origin and scale to unity, then use transformations for the size, rotation, and position where you really want to draw the shape.

  • You can change the coordinate system used to whatever suits the current drawing task.

  • You can save the state of the drawing before changing the coordinate system so that it can be restored.

  • Drawing state includes current transformation matrix, clipping region, and all drawing attributes

To be informed of new articles on I Programmer, subscribe to our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook Where Linkedin.

square

comments

or send your comment to: [email protected]



Share.

Comments are closed.