Transformations

  • Revision slug: Web/Guide/HTML/Canvas_tutorial/Transformations
  • Revision title: Transformations
  • Revision id: 434369
  • Created:
  • Creator: Sheppy
  • Is current revision? No
  • Comment

Revision Content

Saving and restoring state

Before we look at the transformation methods, let's look two other methods which are indispensable once you start generating ever more complex drawings.

save()
Saves the entire state of the canvas.
restore()
Restores the most recently saved canvas state.

Canvas states are stored on a stack. Every time the save() method is called, the current drawing state is pushed onto the stack. A drawing state consists of

  • The transformations that have been applied (i.e. translate, rotate and scale - see below).
  • The values of strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation properties.
  • The current clipping path, which we'll see in the next section.

You can call the save() method as many times as you like. Each time the restore() method is called, the last saved state is popped off the stack and all saved settings are restored.

A save and restore canvas state example

This example tries to illustrate how the stack of drawing states functions by drawing a set of consecutive rectangles.

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');

  ctx.fillRect(0,0,150,150);   // Draw a rectangle with default settings
  ctx.save();                  // Save the default state

  ctx.fillStyle = '#09F'       // Make changes to the settings
  ctx.fillRect(15,15,120,120); // Draw a rectangle with new settings

  ctx.save();                  // Save the current state
  ctx.fillStyle = '#FFF'       // Make changes to the settings
  ctx.globalAlpha = 0.5;    
  ctx.fillRect(30,30,90,90);   // Draw a rectangle with new settings

  ctx.restore();               // Restore previous state
  ctx.fillRect(45,45,60,60);   // Draw a rectangle with restored settings

  ctx.restore();               // Restore original state
  ctx.fillRect(60,60,30,30);   // Draw a rectangle with restored settings
}

The first step is to draw a large rectangle with the default settings. Next we save this state and make changes to the fill color. We then draw the second and smaller blue rectangle and save the state. Again we change some drawing settings and draw the third semi-transparent white rectangle.

So far this is pretty similar to what we've done in previous sections. However once we call the first restore() statement, the top drawing state is removed from the stack, and settings are restored. If we hadn't saved the state using save(), we would need to change the fill color and transparency manually in order to return to the previous state. This would be easy for two properties, but if we have more than that, our code would become very long, very fast.

When the second restore() statement is called, the original state (the one we set up before the first call to save) is restored and the last rectangle is once again drawn in black.

{{EmbedLiveSample("A_save_and_restore_canvas_state_example", "180", "180", "https://mdn.mozillademos.org/files/249/Canvas_savestate.png")}}

Translating

The first of the transformation methods we'll look at is translate(). This method is used to move the canvas and its origin to a different point in the grid.

translate(x, y)
Moves the canvas and its origin on the grid. x indicates the horizontal distance to move, and y indicates how far to move the grid vertically.

It's a good idea to save the canvas state before doing any transformations. In most cases, it is just easier to call the restore method than having to do a reverse translation to return to the original state. Also if you're translating inside a loop and don't save and restore the canvas state, you might end up missing part of your drawing, because it was drawn outside the canvas edge.

A translate example

This example demonstrates some of the benefits of translating the canvas origin. We'll create a drawSpirograph() function that draws spirograph patterns. These are drawn around the origin. Without the translate() function, we would only see a quarter of the pattern on the canvas. The translate() method also gives us the freedom to place it anywhere on the canvas without having to manually adjust coordinates in the spirograph function. This makes it a little easier to understand and use.

In the draw() function, we call the drawSpirograph() nine times using two for loops. In each loop, the canvas is translated, the spirograph is drawn, and the canvas is returned back to its original state.

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.fillRect(0,0,300,300);
  for (var i=0;i<3;i++) {
    for (var j=0;j<3;j++) {
      ctx.save();
      ctx.strokeStyle = "#9CFF00";
      ctx.translate(50+j*100,50+i*100);
      drawSpirograph(ctx,20*(j+2)/(j+1),-8*(i+3)/(i+1),10);
      ctx.restore();
    }
  }
}

function drawSpirograph(ctx,R,r,O){
  var x1 = R-O;
  var y1 = 0;
  var i  = 1;
  ctx.beginPath();
  ctx.moveTo(x1,y1);
  do {
    if (i>20000) break;
    var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))
    var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))
    ctx.lineTo(x2,y2);
    x1 = x2;
    y1 = y2;
    i++;
  } while (x2 != R-O && y2 != 0 );
  ctx.stroke();
}

{{EmbedLiveSample("A_translate_example", "330", "330", "https://mdn.mozillademos.org/files/256/Canvas_translate.png")}}

Rotating

The second transformation method is rotate(). We use it to rotate the canvas around the current origin.

rotate(angle)
Rotates the canvas clockwise around the current origin by the angle number of radians.

The rotation center point is always the canvas origin. To change the center point, we will need to move the canvas by using the translate() method.

A rotate example

In the example, you can see on the right, I used the rotate method to draw shapes in a circular pattern. You could also have calculated the individual x and y coordinates (x = r*Math.cos(a); y = r*Math.sin(a)). In this case it doesn't really matter which method you choose, because we're drawing circles. Calculating the coordinates results in only rotating the center positions of the circles and not the circles themselves, while using rotate results in both, but of course circles look the same no matter how far they are rotated about their centers.

Again we have two loops. The first determines the number of rings, and the second determines the number of dots drawn in each ring. Before drawing each ring, I save the canvas state, so I can easily retrieve it. For each dot that is drawn, I rotate the canvas coordinate space by an angle that is determined by the number of dots in the ring. The innermost circle has six dots, so in each step, I rotate over an angle of 360/6 = 60 degrees. With each additional ring, the number of dots is doubled, and the angle in turn is halved.

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.translate(75,75);

  for (var i=1;i<6;i++){ // Loop through rings (from inside to out)
    ctx.save();
    ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)';

    for (var j=0;j<i*6;j++){ // draw individual dots
      ctx.rotate(Math.PI*2/(i*6));
      ctx.beginPath();
      ctx.arc(0,i*12.5,5,0,Math.PI*2,true);
      ctx.fill();
    }

    ctx.restore();
  }
}

{{EmbedLiveSample("A_rotate_example", "180", "180", "https://mdn.mozillademos.org/files/248/Canvas_rotate.png")}}

Scaling

The next transformation method is scaling. We use it to increase or decrease the units in our canvas grid. This can be used to draw scaled down or enlarged shapes and bitmaps.

scale(x, y)

This method takes two parameters. x is the scale factor in the horizontal direction and y is the scale factor in the vertical direction. Both parameters must be real numbers, and not necessarily positive. Values smaller than 1.0 reduce the unit size and values larger than 1.0 increase the unit size. Setting the scaling factor to precisely 1.0 doesn't affect the unit size. Using negative numbers you can do axis mirroring (for example using translate(0,canvas.height); scale(1,-1); you will have the well known Cartesian coordinate system, with origin in the bottom left corner).

By default one unit on the canvas is exactly one pixel. If we apply, for instance, a scaling factor of 0.5, the resulting unit would become 0.5 pixels and so shapes would be drawn at half size. In a similar way setting the scaling factor to 2.0 would increase the unit size and one unit now becomes two pixels. This results in shapes being drawn twice as large.

A scale example

In this last example I've used the spirograph function from one of the previous examples to draw nine shapes with different scaling factors. The top left shape has been drawn with no scaling applied. The yellow shapes to the right both have a uniform scaling factor (the same value for x and y parameters). If you look at the code below you'll see that I've used the scale method twice with equal parameter values for the second and third spirograph. Because I didn't restore the canvas state, the third shape is drawn with a scaling factor of 0.75 × 0.75 = 0.5625.

The second row of blue shapes have a non-uniform scaling applied in a vertical direction. Each of the shapes has the x scaling factor set to 1.0 which means no scaling. The y scaling factor is set to 0.75. This results in the three shapes being squashed down. The original circular shape has now become an ellipse. If you look closely you'll see that the line width has also been reduced in the vertical direction.

The third row of green shapes is similar to the one above but now I've applied a scaling in the horizontal direction.

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.strokeStyle = "#fc0";
  ctx.lineWidth = 1.5;
  ctx.fillRect(0,0,300,300);

  // Uniform scaling
  ctx.save()
  ctx.translate(50,50);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(100,0);
  ctx.scale(0.75,0.75);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(133.333,0);
  ctx.scale(0.75,0.75);
  drawSpirograph(ctx,22,6,5);
  ctx.restore();

  // Non uniform scaling (y direction)
  ctx.strokeStyle = "#0cf";
  ctx.save()
  ctx.translate(50,150);
  ctx.scale(1,0.75);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(100,0);
  ctx.scale(1,0.75);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(100,0);
  ctx.scale(1,0.75);
  drawSpirograph(ctx,22,6,5);
  ctx.restore();

  // Non uniform scaling (x direction)
  ctx.strokeStyle = "#cf0";
  ctx.save()
  ctx.translate(50,250);
  ctx.scale(0.75,1);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(133.333,0);
  ctx.scale(0.75,1);
  drawSpirograph(ctx,22,6,5);

  ctx.translate(177.777,0);
  ctx.scale(0.75,1);
  drawSpirograph(ctx,22,6,5);
  ctx.restore();

}
function drawSpirograph(ctx,R,r,O){
  var x1 = R-O;
  var y1 = 0;
  var i  = 1;
  ctx.beginPath();
  ctx.moveTo(x1,y1);
  do {
    if (i>20000) break;
    var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))
    var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))
    ctx.lineTo(x2,y2);
    x1 = x2;
    y1 = y2;
    i++;
  } while (x2 != R-O && y2 != 0 );
  ctx.stroke();
}

{{EmbedLiveSample("A_scale_example", "330", "330", "https://mdn.mozillademos.org/files/250/Canvas_scale.png")}}

Transforms

The final transformation methods allow modifications directly to the transformation matrix.

transform(m11, m12, m21, m22, dx, dy)

This method must multiply the current transformation matrix with the matrix described by:

m11 	m21 	dx
m12 	m22 	dy
0 	0 	1

If any of the arguments are Infinity the transformation matrix must be marked as infinite instead of the method throwing an exception.

setTransform(m11, m12, m21, m22, dx, dy)

This method must reset the current transform to the identity matrix, and then invoke the transform method with the same arguments.

transform / setTransform examples

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');

  var sin = Math.sin(Math.PI/6);
  var cos = Math.cos(Math.PI/6);
  ctx.translate(100, 100);
  var c = 0;
  for (var i=0; i <= 12; i++) {
    c = Math.floor(255 / 12 * i);
    ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";
    ctx.fillRect(0, 0, 100, 10);
    ctx.transform(cos, sin, -sin, cos, 0, 0);
  }
  
  ctx.setTransform(-1, 0, 0, 1, 100, 100);
  ctx.fillStyle = "rgba(255, 128, 255, 0.5)";
  ctx.fillRect(0, 50, 100, 100);
}

{{EmbedLiveSample("transform_.2F_setTransform_examples", "230", "280", "https://mdn.mozillademos.org/files/255/Canvas_transform.png")}}

{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors", "Web/Guide/HTML/Canvas_tutorial/Compositing")}}

Revision Source

<h2 id="Saving_and_restoring_state" name="Saving_and_restoring_state">Saving and restoring state</h2>
<p>Before we look at the transformation methods, let's look two other methods which are indispensable once you start generating ever more complex drawings.</p>
<dl>
  <dt>
    <code>save()</code></dt>
  <dd>
    Saves the entire state of the canvas.</dd>
  <dt>
    <code>restore()</code></dt>
  <dd>
    Restores the most recently saved canvas state.</dd>
</dl>
<p>Canvas states are stored on a stack. Every time the <code>save()</code> method is called, the current drawing state is pushed onto the stack. A drawing state consists of</p>
<ul>
  <li>The transformations that have been applied (i.e. translate, rotate and scale - see below).</li>
  <li>The values of <code>strokeStyle</code>, <code>fillStyle</code>, <code>globalAlpha</code>, <code>lineWidth</code>, <code>lineCap</code>, <code>lineJoin</code>, <code>miterLimit</code>, <code>shadowOffsetX</code>, <code>shadowOffsetY</code>, <code>shadowBlur</code>, <code>shadowColor</code>, <code>globalCompositeOperation</code> properties.</li>
  <li>The current clipping path, which we'll see in the next section.</li>
</ul>
<p>You can call the <code>save()</code> method as many times as you like. Each time the <code>restore()</code> method is called, the last saved state is popped off the stack and all saved settings are restored.</p>
<h3 id="A_save_and_restore_canvas_state_example" name="A_save_and_restore_canvas_state_example">A save and restore canvas state example</h3>
<p>This example tries to illustrate how the stack of drawing states functions by drawing a set of consecutive rectangles.</p>
<pre class="brush: js">
function draw() {
&nbsp; var ctx = document.getElementById('canvas').getContext('2d');

&nbsp; ctx.fillRect(0,0,150,150);&nbsp;&nbsp; // Draw a rectangle with default settings
&nbsp; ctx.save();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Save the default state

&nbsp; ctx.fillStyle = '#09F'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Make changes to the settings
&nbsp; ctx.fillRect(15,15,120,120); // Draw a rectangle with new settings

&nbsp; ctx.save();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Save the current state
&nbsp; ctx.fillStyle = '#FFF'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Make changes to the settings
&nbsp; ctx.globalAlpha = 0.5;&nbsp;&nbsp; &nbsp;
&nbsp; ctx.fillRect(30,30,90,90);&nbsp;&nbsp; // Draw a rectangle with new settings

&nbsp; ctx.restore();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Restore previous state
&nbsp; ctx.fillRect(45,45,60,60);&nbsp;&nbsp; // Draw a rectangle with restored settings

&nbsp; ctx.restore();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Restore original state
&nbsp; ctx.fillRect(60,60,30,30);&nbsp;&nbsp; // Draw a rectangle with restored settings
}</pre>
<div class="hidden">
  <pre class="brush: html">
&lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;</pre>
  <pre class="brush: js">
draw();</pre>
</div>
<p>The first step is to draw a large rectangle with the default settings. Next we save this state and make changes to the fill color. We then draw the second and smaller blue rectangle and save the state. Again we change some drawing settings and draw the third semi-transparent white rectangle.</p>
<p>So far this is pretty similar to what we've done in previous sections. However once we call the first <code>restore()</code> statement, the top drawing state is removed from the stack, and settings are restored. If we hadn't saved the state using <code>save()</code>, we would need to change the fill color and transparency manually in order to return to the previous state. This would be easy for two properties, but if we have more than that, our code would become very long, very fast.</p>
<p>When the second <code>restore()</code> statement is called, the original state (the one we set up before the first call to <code>save</code>) is restored and the last rectangle is once again drawn in black.</p>
<p>{{EmbedLiveSample("A_save_and_restore_canvas_state_example", "180", "180", "https://mdn.mozillademos.org/files/249/Canvas_savestate.png")}}</p>
<h2 id="Translating" name="Translating">Translating</h2>
<p><img align="right" alt="" class="internal" src="https://mdn.mozillademos.org/files/234/Canvas_grid_translate.png" />The first of the transformation methods we'll look at is <code>translate()</code>. This method is used to move the canvas and its origin to a different point in the grid.</p>
<dl>
  <dt>
    <code>translate(<em>x</em>, <em>y</em>)</code></dt>
  <dd>
    Moves the canvas and its origin on the grid. <code>x</code> indicates the horizontal distance to move, and <code>y</code> indicates how far to move the grid vertically.</dd>
</dl>
<p>It's a good idea to save the canvas state before doing any transformations. In most cases, it is just easier to call the <code>restore</code> method than having to do a reverse translation to return to the original state. Also if you're translating inside a loop and don't save and restore the canvas state, you might end up missing part of your drawing, because it was drawn outside the canvas edge.</p>
<h3 id="A_translate_example" name="A_translate_example">A <code>translate</code> example</h3>
<p>This example demonstrates some of the benefits of translating the canvas origin. We'll create a <code>drawSpirograph()</code> function that draws spirograph patterns. These are drawn around the origin. Without the <code>translate()</code> function, we would only see a quarter of the pattern on the canvas. The <code>translate()</code> method also gives us the freedom to place it anywhere on the canvas without having to manually adjust coordinates in the spirograph function. This makes it a little easier to understand and use.</p>
<p>In the <code>draw()</code> function, we call the <code>drawSpirograph()</code> nine times using two <code>for</code> loops. In each loop, the canvas is translated, the spirograph is drawn, and the canvas is returned back to its original state.</p>
<pre class="brush: js">
function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.fillRect(0,0,300,300);
  for (var i=0;i&lt;3;i++) {
    for (var j=0;j&lt;3;j++) {
      ctx.save();
      ctx.strokeStyle = "#9CFF00";
      ctx.translate(50+j*100,50+i*100);
      drawSpirograph(ctx,20*(j+2)/(j+1),-8*(i+3)/(i+1),10);
      ctx.restore();
    }
  }
}

function drawSpirograph(ctx,R,r,O){
  var x1 = R-O;
  var y1 = 0;
  var i  = 1;
  ctx.beginPath();
  ctx.moveTo(x1,y1);
  do {
    if (i&gt;20000) break;
    var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))
    var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))
    ctx.lineTo(x2,y2);
    x1 = x2;
    y1 = y2;
    i++;
  } while (x2 != R-O &amp;&amp; y2 != 0 );
  ctx.stroke();
}
</pre>
<div class="hidden">
  <pre class="brush: html">
&lt;canvas id="canvas" width="300" height="300"&gt;&lt;/canvas&gt;</pre>
  <pre class="brush: js">
draw();</pre>
</div>
<p>{{EmbedLiveSample("A_translate_example", "330", "330", "https://mdn.mozillademos.org/files/256/Canvas_translate.png")}}</p>
<h2 id="Rotating" name="Rotating">Rotating</h2>
<p><img align="right" alt="" class="internal" src="https://mdn.mozillademos.org/files/233/Canvas_grid_rotate.png" />The second transformation method is <code>rotate()</code>. We use it to rotate the canvas around the current origin.</p>
<dl>
  <dt>
    <code>rotate(<em>angle</em>)</code></dt>
  <dd>
    Rotates the canvas clockwise around the current origin by the <code>angle</code> number of radians.</dd>
</dl>
<p>The rotation center point is always the canvas origin. To change the center point, we will need to move the canvas by using the <code>translate()</code> method.</p>
<h3 id="A_rotate_example" name="A_rotate_example">A <code>rotate</code> example</h3>
<p>In the example, you can see on the right, I used the <code>rotate</code> method to draw shapes in a circular pattern. You could also have calculated the individual <var>x</var> and <var>y</var> coordinates (<code>x = r*Math.cos(a); y = r*Math.sin(a)</code>). In this case it doesn't really matter which method you choose, because we're drawing circles. Calculating the coordinates results in only rotating the center positions of the circles and not the circles themselves, while using <code>rotate</code> results in both, but of course circles look the same no matter how far they are rotated about their centers.</p>
<p>Again we have two loops. The first determines the number of rings, and the second determines the number of dots drawn in each ring. Before drawing each ring, I save the canvas state, so I can easily retrieve it. For each dot that is drawn, I rotate the canvas coordinate space by an angle that is determined by the number of dots in the ring. The innermost circle has six dots, so in each step, I rotate over an angle of 360/6 = 60 degrees. With each additional ring, the number of dots is doubled, and the angle in turn is halved.</p>
<pre class="brush: js">
function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.translate(75,75);

  for (var i=1;i&lt;6;i++){ // Loop through rings (from inside to out)
    ctx.save();
    ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)';

    for (var j=0;j&lt;i*6;j++){ // draw individual dots
      ctx.rotate(Math.PI*2/(i*6));
      ctx.beginPath();
      ctx.arc(0,i*12.5,5,0,Math.PI*2,true);
      ctx.fill();
    }

    ctx.restore();
  }
}
</pre>
<div class="hidden">
  <pre class="brush: html">
&lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;</pre>
  <pre class="brush: js">
draw();</pre>
</div>
<p>{{EmbedLiveSample("A_rotate_example", "180", "180", "https://mdn.mozillademos.org/files/248/Canvas_rotate.png")}}</p>
<h2 id="Scaling" name="Scaling">Scaling</h2>
<p>The next transformation method is scaling. We use it to increase or decrease the units in our canvas grid. This can be used to draw scaled down or enlarged shapes and bitmaps.</p>
<div style="border: 1px solid rgb(208, 221, 158); background: rgb(239, 248, 206) none repeat scroll 0% 0%; padding-left: 10px;">
  <p><code><strong>scale(x, y)</strong></code></p>
</div>
<p>This method takes two parameters. <code>x</code> is the scale factor in the horizontal direction and <code>y</code> is the scale factor in the vertical direction. Both parameters must be real numbers, and not necessarily positive. Values smaller than 1.0 reduce the unit size and values larger than 1.0 increase the unit size. Setting the scaling factor to precisely 1.0 doesn't affect the unit size. Using negative numbers you can do axis mirroring (for example using translate(0,canvas.height); scale(1,-1); you will have the well known Cartesian coordinate system, with origin in the bottom left corner).</p>
<p>By default one unit on the canvas is exactly one pixel. If we apply, for instance, a scaling factor of 0.5, the resulting unit would become 0.5 pixels and so shapes would be drawn at half size. In a similar way setting the scaling factor to 2.0 would increase the unit size and one unit now becomes two pixels. This results in shapes being drawn twice as large.</p>
<h4 id="A_scale_example" name="A_scale_example">A <code>scale</code> example</h4>
<p>In this last example I've used the spirograph function from one of the previous examples to draw nine shapes with different scaling factors. The top left shape has been drawn with no scaling applied. The yellow shapes to the right both have a uniform scaling factor (the same value for <var>x</var> and <var>y</var> parameters). If you look at the code below you'll see that I've used the <code>scale</code> method twice with equal parameter values for the second and third spirograph. Because I didn't restore the canvas state, the third shape is drawn with a scaling factor of 0.75 × 0.75 = 0.5625.</p>
<p>The second row of blue shapes have a non-uniform scaling applied in a vertical direction. Each of the shapes has the <var>x</var> scaling factor set to 1.0 which means no scaling. The <var>y</var> scaling factor is set to 0.75. This results in the three shapes being squashed down. The original circular shape has now become an ellipse. If you look closely you'll see that the line width has also been reduced in the vertical direction.</p>
<p>The third row of green shapes is similar to the one above but now I've applied a scaling in the horizontal direction.</p>
<pre class="brush: js">
function draw() {
&nbsp; var ctx = document.getElementById('canvas').getContext('2d');
&nbsp; ctx.strokeStyle = "#fc0";
&nbsp; ctx.lineWidth = 1.5;
&nbsp; ctx.fillRect(0,0,300,300);

&nbsp; // Uniform scaling
&nbsp; ctx.save()
&nbsp; ctx.translate(50,50);
&nbsp; drawSpirograph(ctx,22,6,5);

&nbsp; ctx.translate(100,0);
&nbsp; ctx.scale(0.75,0.75);
&nbsp; drawSpirograph(ctx,22,6,5);

&nbsp; ctx.translate(133.333,0);
&nbsp; ctx.scale(0.75,0.75);
&nbsp; drawSpirograph(ctx,22,6,5);
&nbsp; ctx.restore();

&nbsp; // Non uniform scaling (y direction)
&nbsp; ctx.strokeStyle = "#0cf";
&nbsp; ctx.save()
&nbsp; ctx.translate(50,150);
&nbsp; ctx.scale(1,0.75);
&nbsp; drawSpirograph(ctx,22,6,5);

&nbsp; ctx.translate(100,0);
&nbsp; ctx.scale(1,0.75);
&nbsp; drawSpirograph(ctx,22,6,5);

&nbsp; ctx.translate(100,0);
&nbsp; ctx.scale(1,0.75);
&nbsp; drawSpirograph(ctx,22,6,5);
&nbsp; ctx.restore();

&nbsp; // Non uniform scaling (x direction)
&nbsp; ctx.strokeStyle = "#cf0";
&nbsp; ctx.save()
&nbsp; ctx.translate(50,250);
&nbsp; ctx.scale(0.75,1);
&nbsp; drawSpirograph(ctx,22,6,5);

&nbsp; ctx.translate(133.333,0);
&nbsp; ctx.scale(0.75,1);
&nbsp; drawSpirograph(ctx,22,6,5);

&nbsp; ctx.translate(177.777,0);
&nbsp; ctx.scale(0.75,1);
&nbsp; drawSpirograph(ctx,22,6,5);
&nbsp; ctx.restore();

}
function drawSpirograph(ctx,R,r,O){
&nbsp; var x1 = R-O;
&nbsp; var y1 = 0;
&nbsp; var i&nbsp; = 1;
&nbsp; ctx.beginPath();
&nbsp; ctx.moveTo(x1,y1);
&nbsp; do {
&nbsp;&nbsp;&nbsp; if (i&gt;20000) break;
&nbsp;&nbsp;&nbsp; var x2 = (R+r)*Math.cos(i*Math.PI/72) - (r+O)*Math.cos(((R+r)/r)*(i*Math.PI/72))
&nbsp;&nbsp;&nbsp; var y2 = (R+r)*Math.sin(i*Math.PI/72) - (r+O)*Math.sin(((R+r)/r)*(i*Math.PI/72))
&nbsp;&nbsp;&nbsp; ctx.lineTo(x2,y2);
&nbsp;&nbsp;&nbsp; x1 = x2;
&nbsp;&nbsp;&nbsp; y1 = y2;
&nbsp;&nbsp;&nbsp; i++;
&nbsp; } while (x2 != R-O &amp;&amp; y2 != 0 );
&nbsp; ctx.stroke();
}
</pre>
<div class="hidden">
  <pre class="brush: html">
&lt;canvas id="canvas" width="300" height="300"&gt;&lt;/canvas&gt;</pre>
  <pre class="brush: js">
draw();</pre>
</div>
<p>{{EmbedLiveSample("A_scale_example", "330", "330", "https://mdn.mozillademos.org/files/250/Canvas_scale.png")}}</p>
<h2 id="Transforms" name="Transforms">Transforms</h2>
<p>The final transformation methods allow modifications directly to the transformation matrix.</p>
<div style="border: 1px solid rgb(208, 221, 158); background: rgb(239, 248, 206) none repeat scroll 0% 0%; padding-left: 10px;">
  <p><code><strong>transform(m11, m12, m21, m22, dx, dy)</strong></code></p>
</div>
<p>This method must multiply the current transformation matrix with the matrix described by:</p>
<p><img src="http://www.numberempire.com/equation.render?%5Cbegin{bmatrix}%20m11%20&amp;%20m21%20%5C%5C%20m12%20&amp;%20m22%5Cend{bmatrix}%5Cbegin{bmatrix}%20x%20%5C%5C%20y%5Cend{bmatrix}" style="" /></p>
<pre>
m11 	m21 	dx
m12 	m22 	dy
0 	0 	1
</pre>
<p>If any of the arguments are Infinity the transformation matrix must be marked as infinite instead of the method throwing an exception.</p>
<div style="border: 1px solid rgb(208, 221, 158); background: rgb(239, 248, 206) none repeat scroll 0% 0%; padding-left: 10px;">
  <p><code><strong>setTransform(m11, m12, m21, m22, dx, dy)</strong></code></p>
</div>
<p>This method must reset the current transform to the identity matrix, and then invoke the <code><strong>transform</strong></code> method with the same arguments.</p>
<h4 id="transform_.2F_setTransform_examples" name="transform_.2F_setTransform_examples"><code>transform</code> / <code>setTransform</code> examples</h4>
<pre class="brush: js">
function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');

  var sin = Math.sin(Math.PI/6);
  var cos = Math.cos(Math.PI/6);
  ctx.translate(100, 100);
  var c = 0;
  for (var i=0; i &lt;= 12; i++) {
    c = Math.floor(255 / 12 * i);
    ctx.fillStyle = "rgb(" + c + "," + c + "," + c + ")";
    ctx.fillRect(0, 0, 100, 10);
    ctx.transform(cos, sin, -sin, cos, 0, 0);
  }
  
  ctx.setTransform(-1, 0, 0, 1, 100, 100);
  ctx.fillStyle = "rgba(255, 128, 255, 0.5)";
  ctx.fillRect(0, 50, 100, 100);
}
</pre>
<div class="hidden">
  <pre class="brush: html">
&lt;canvas id="canvas" width="200" height="250"&gt;&lt;/canvas&gt;</pre>
  <pre class="brush: js">
draw();</pre>
</div>
<p>{{EmbedLiveSample("transform_.2F_setTransform_examples", "230", "280", "https://mdn.mozillademos.org/files/255/Canvas_transform.png")}}</p>
<p>{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors", "Web/Guide/HTML/Canvas_tutorial/Compositing")}}</p>
Revert to this revision