Draw Circle on Canvas Onclick

Chapter 17Drawing on Canvas

Drawing is deception.

Picture of a robot arm drawing on paper

Browsers give us several means to display graphics. The simplest manner is to employ styles to position and color regular DOM elements. This can go you quite far, as the game in the previous chapter showed. By adding partially transparent groundwork images to the nodes, we can make them await exactly the fashion nosotros want. Information technology is even possible to rotate or skew nodes with the transform fashion.

Simply we'd be using the DOM for something that it wasn't originally designed for. Some tasks, such as cartoon a line betwixt capricious points, are extremely awkward to practice with regular HTML elements.

There are two alternatives. The offset is DOM-based merely utilizes Scalable Vector Graphics (SVG), rather than HTML. Think of SVG every bit a document-markup dialect that focuses on shapes rather than text. You can embed an SVG document direct 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. Information technology provides a programming interface for drawing shapes onto the space taken upwardly by the node. The principal difference between a sail and an SVG pic 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 before long equally 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 sheet (or the part of the canvas around the shape) and redraw it with the shape in a new position.

SVG

This book will not become into SVG in detail, but I will briefly explain how information technology works. At the end of the affiliate, I'll come back to the trade-offs that yous must consider when deciding which cartoon mechanism is appropriate for a given application.

This is an HTML document with a simple SVG moving picture in it:

          <          p          >Normal HTML here.</          p          >          <          svg          xmlns="http://www.w3.org/2000/svg"          >          <          circumvolve          r="50"          cx="50"          cy="50"          fill="red"          />          <          rect          x="120"          y="5"          width="ninety"          height="90"          stroke="blue"          fill="none"          />          </          svg          >        

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 non exist in HTML, do have a pregnant in SVG—they draw shapes using the style and position specified past their attributes.

These tags create DOM elements, just like HTML tags, that scripts can interact with. For example, this changes the <circle> element to exist colored cyan instead:

          let          circumvolve          =          document.querySelector("circle");          circumvolve.setAttribute("fill up",          "cyan");

The canvas element

Sail graphics tin exist fatigued 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 as empty infinite in the document.

The <canvas> tag is intended to let dissimilar styles of cartoon. To get access to an actual drawing interface, we first need to create a context, an object whose methods provide the cartoon interface. In that location are currently 2 widely supported cartoon styles: "second" for two-dimensional graphics and "webgl" for 3-dimensional graphics through the OpenGL interface.

This volume won't discuss WebGL—nosotros'll stick to two dimensions. But if you are interested in three-dimensional graphics, I practise encourage you to look into WebGL. It provides a direct interface to graphics hardware and allows y'all to render even complicated scenes efficiently, using JavaScript.

You create a context with the getContext method on the <canvas> DOM element.

          <          p          >Before canvas.</          p          >          <          canvas          width="120"          peak="60"          >          </          sail          >          <          p          >After canvas.</          p          >          <          script          >          let          canvas          =          document.querySelector("canvas");          let          context          =          sail.getContext("2d");          context.fillStyle          =          "carmine";          context.fillRect(ten,          x,          100,          l);          </          script          >        

Afterward creating the context object, the example draws a reddish rectangle 100 pixels wide and fifty pixels high, with its acme-left corner at coordinates (ten,10).

Just similar in HTML (and SVG), the coordinate system that the sail uses puts (0,0) at the tiptop-left corner, and the positive y-centrality goes down from there. So (10,10) is 10 pixels beneath and to the correct of the meridian-left corner.

Lines and surfaces

In the canvas interface, a shape can be filled, pregnant its surface area is given a certain color or pattern, or information technology can be stroked, which means a line is drawn along its border. 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 summit-left corner, so its width, and then its height. A similar method, strokeRect, draws the outline of a rectangle.

Neither method takes whatsoever further parameters. The color of the fill, thickness of the stroke, and then on, are non determined by an argument to the method (every bit yous might reasonably wait) merely rather past properties of the context object.

The fillStyle belongings controls the way shapes are filled. It tin can be ready to a string that specifies a color, using the color annotation used past CSS.

The strokeStyle belongings works similarly only determines the color used for a stroked line. The width of that line is adamant by the lineWidth property, which may contain any positive number.

          <          sail          >          </          canvas          >          <          script          >          let          cx          =          document.querySelector("canvas").getContext("2d");          cx.strokeStyle          =          "bluish";          cx.strokeRect(5,          5,          50,          50);          cx.lineWidth          =          five;          cx.strokeRect(135,          5,          50,          50);          </          script          >        

When no width or summit aspect is specified, as in the example, a canvas element gets a default width of 300 pixels and height of 150 pixels.

Paths

A path is a sequence of lines. The 2D canvas interface takes a peculiar approach to describing such a path. Information technology is washed entirely through side effects. Paths are not values that can be stored and passed around. Instead, if you want to do something with a path, you make a sequence of method calls to describe its shape.

          <          sheet          >          </          sail          >          <          script          >          let          cx          =          document.querySelector("sail").getContext("2d");          cx.beginPath();          for          (permit          y          =          10;          y          <          100;          y          +=          10) {          cx.moveTo(10,          y);          cx.lineTo(90,          y);   }          cx.stroke();          </          script          >        

This example creates a path with a number of horizontal line segments and then strokes it using the stroke method. Each segment created with lineTo starts at the path'southward electric current position. That position is usually the end of the terminal segment, unless moveTo was called. In that case, the adjacent segment would start at the position passed to moveTo.

When filling a path (using the fill method), each shape is filled separately. A path can comprise multiple shapes—each moveTo motion starts a new 1. But the path needs to be closed (meaning its get-go and end are in the same position) before information technology can be filled. If the path is not already airtight, a line is added from its end to its outset, and the shape enclosed by the completed path is filled.

          <          canvas          >          </          canvas          >          <          script          >          let          cx          =          certificate.querySelector("canvas").getContext("2d");          cx.beginPath();          cx.moveTo(50,          10);          cx.lineTo(10,          70);          cx.lineTo(90,          seventy);          cx.fill();          </          script          >        

This case draws a filled triangle. Note that only 2 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 at that place when y'all stroke the path.

You could besides apply the closePath method to explicitly close a path by adding an bodily line segment dorsum to the path'south get-go. This segment is drawn when stroking the path.

Curves

A path may likewise contain curved lines. These are unfortunately a bit more involved to draw.

The quadraticCurveTo method draws a curve to a given point. To make up one's mind the curvature of the line, the method is given a control betoken also equally a destination bespeak. Imagine this control point equally attracting the line, giving it its bend. The line won't go through the control point, simply its management at the start and stop points will be such that a directly line in that direction would bespeak toward the control signal. The following example illustrates this:

          <          canvas          >          </          canvas          >          <          script          >          allow          cx          =          document.querySelector("canvas").getContext("2d");          cx.beginPath();          cx.moveTo(ten,          90);             cx.quadraticCurveTo(60,          10,          90,          90);          cx.lineTo(60,          10);          cx.closePath();          cx.stroke();          </          script          >        

We draw a quadratic bend from the left to the correct, with (60,ten) as control point, and so draw ii line segments going through that control betoken and dorsum to the start of the line. The result somewhat resembles a Star Trek insignia. You can encounter the event of the control point: the lines leaving the lower corners start off in the direction of the control point and so curve toward their target.

The bezierCurveTo method draws a similar kind of curve. Instead of a single control betoken, this one has ii—ane for each of the line'due south endpoints. Hither is a similar sketch to illustrate the behavior of such a curve:

          <          canvas          >          </          canvass          >          <          script          >          permit          cx          =          document.querySelector("canvas").getContext("2d");          cx.beginPath();          cx.moveTo(10,          xc);             cx.bezierCurveTo(10,          10,          ninety,          10,          50,          xc);          cx.lineTo(xc,          ten);          cx.lineTo(10,          10);          cx.closePath();          cx.stroke();          </          script          >        

The ii control points specify the direction at both ends of the curve. The farther they are away from their corresponding point, the more the curve will "bulge" in that direction.

Such curves can be hard to work with—it's non always clear how to find the control points that provide the shape you are looking for. Sometimes you can compute them, and sometimes you'll just have to detect a suitable value by trial and error.

The arc method is a fashion to draw a line that curves forth the border of a circle. It takes a pair of coordinates for the arc's center, a radius, and then a start angle and finish angle.

Those last two parameters make it possible to draw only office of the circle. The angles are measured in radians, non degrees. This ways a full circle has an angle of 2π, or two * Math.PI, which is about half dozen.28. The bending starts counting at the point to the right of the circle's center and goes clockwise from there. You can use a get-go of 0 and an terminate bigger than 2π (say, seven) to draw a total circle.

          <          sheet          >          </          canvas          >          <          script          >          let          cx          =          document.querySelector("canvas").getContext("2d");          cx.beginPath();             cx.arc(50,          l,          40,          0,          7);             cx.arc(150,          50,          40,          0,          0.5          *          Math.PI);          cx.stroke();          </          script          >        

The resulting film contains a line from the right of the full circle (beginning telephone call to arc) to the right of the quarter-circle (second call). Like other path-cartoon methods, a line drawn with arc is connected to the previous path segment. You tin can call moveTo or starting time a new path to avoid this.

Drawing a pie chart

Imagine you've only taken a job at EconomiCorp, Inc., and your first assignment is to draw a pie chart of its customer satisfaction survey results.

The results bounden contains an assortment of objects that correspond the survey responses.

          const          results          =          [   {proper noun:          "Satisfied",          count:          1043,          color:          "lightblue"},   {name:          "Neutral",          count:          563,          colour:          "lightgreen"},   {name:          "Unsatisfied",          count:          510,          color:          "pink"},   {proper noun:          "No comment",          count:          175,          color:          "silver"} ];

To draw a pie chart, we draw a number of pie slices, each made up of an arc and a pair of lines to the center of that arc. We can compute the angle taken upward by each arc by dividing a full circle (2π) past the total number of responses and then multiplying that number (the bending per response) by the number of people who picked a given choice.

          <          canvas          width="200"          height="200"          >          </          canvas          >          <          script          >          allow          cx          =          certificate.querySelector("canvas").getContext("second");          let          full          =          results          .reduce((sum, {count})          =>          sum          +          count,          0);             permit          currentAngle          =          -          0.five          *          Math.PI;          for          (allow          upshot          of          results) {          let          sliceAngle          =          (upshot.count          /          total)          *          ii          *          Math.PI;          cx.beginPath();                    cx.arc(100,          100,          100,          currentAngle,          currentAngle          +          sliceAngle);          currentAngle          +=          sliceAngle;          cx.lineTo(100,          100);          cx.fillStyle          =          result.colour;          cx.make full();   }          </          script          >        

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 cartoon context provides the methods fillText and strokeText. The latter tin can exist useful for outlining messages, but commonly fillText is what you lot need. It will fill the outline of the given text with the current fillStyle.

          <          canvas          >          </          canvass          >          <          script          >          let          cx          =          document.querySelector("canvas").getContext("2d");          cx.font          =          "28px Georgia";          cx.fillStyle          =          "fuchsia";          cx.fillText("I can draw text, likewise!",          ten,          fifty);          </          script          >        

You tin can specify the size, mode, and font of the text with the font property. This example just gives a font size and family proper noun. It is likewise possible to add italic or bold to the first of the string to select a way.

The last two arguments to fillText and strokeText provide the position at which the font is fatigued. Past default, they indicate the position of the get-go of the text'southward alphabetic baseline, which is the line that letters "stand" on, not counting hanging parts in letters such equally j or p. You tin can change the horizontal position by setting the textAlign property to "end" or "center" and the vertical position past setting textBaseline to "pinnacle", "middle", or "bottom".

Nosotros'll come back to our pie chart, and the problem of labeling the slices, in the exercises at the end of the chapter.

Images

In computer graphics, a distinction is oft made between vector graphics and bitmap graphics. The beginning is what we have been doing and so far in this affiliate—specifying a picture past giving a logical clarification of shapes. Bitmap graphics, on the other manus, don't specify bodily shapes just rather work with pixel data (rasters of colored dots).

The drawImage method allows us to draw pixel data onto a canvass. This pixel data tin originate from an <img> element or from another sail. The post-obit instance creates a discrete <img> element and loads an epitome file into information technology. But information technology cannot immediately showtime drawing from this motion-picture show because the browser may not have loaded it withal. To bargain with this, we register a "load" consequence handler and practice the drawing later the prototype has loaded.

          <          canvass          >          </          canvas          >          <          script          >          permit          cx          =          document.querySelector("canvas").getContext("2d");          permit          img          =          document.createElement("img");          img.src          =          "img/hat.png";          img.addEventListener("load", ()          =>          {          for          (permit          x          =          x;          x          <          200;          10          +=          30) {          cx.drawImage(img,          x,          10);     }   });          </          script          >        

Past default, drawImage volition draw the paradigm at its original size. Yous can too requite information technology two additional arguments to gear up a different width and superlative.

When drawImage is given 9 arguments, it can be used to draw only a fragment of an epitome. The second through fifth arguments bespeak the rectangle (x, y, width, and height) in the source paradigm that should exist copied, and the sixth to ninth arguments give the rectangle (on the sheet) into which it should be copied.

This tin be used to pack multiple sprites (image elements) into a unmarried prototype file and and then draw only the part you lot need. For example, we take this picture show containing a game graphic symbol in multiple poses:

Various poses of a game character

By alternating which pose we draw, we tin can testify an animation that looks like a walking grapheme.

To animate a picture on a canvas, the clearRect method is useful. It resembles fillRect, only instead of coloring the rectangle, it makes it transparent, removing the previously fatigued pixels.

We know that each sprite, each subpicture, is 24 pixels wide and xxx pixels high. The following code loads the image and then sets upward an interval (repeated timer) to depict the adjacent frame:

          <          canvas          >          </          canvas          >          <          script          >          permit          cx          =          document.querySelector("canvas").getContext("2nd");          let          img          =          document.createElement("img");          img.src          =          "img/actor.png";          let          spriteW          =          24,          spriteH          =          30;          img.addEventListener("load", ()          =>          {          let          bicycle          =          0;          setInterval(()          =>          {          cx.clearRect(0,          0,          spriteW,          spriteH);          cx.drawImage(img,                              cycle          *          spriteW,          0,          spriteW,          spriteH,                              0,          0,          spriteW,          spriteH);          cycle          =          (cycle          +          one)          %          8;     },          120);   });          </          script          >        

The cycle binding tracks our position in the animation. For each frame, it is incremented and then clipped back to the 0 to 7 range by using the remainder operator. This binding is then used to compute the 10-coordinate that the sprite for the current pose has in the pic.

Transformation

Only what if we want our character to walk to the left instead of to the right? Nosotros could draw another prepare of sprites, of class. But we can likewise instruct the canvas to depict the picture the other way round.

Calling the scale method will crusade anything drawn after it to be scaled. This method takes two parameters, one to set a horizontal calibration and one to set a vertical scale.

          <          sail          >          </          canvas          >          <          script          >          let          cx          =          document.querySelector("canvas").getContext("2nd");          cx.scale(iii,          .5);          cx.beginPath();          cx.arc(50,          l,          forty,          0,          7);          cx.lineWidth          =          three;          cx.stroke();          </          script          >        

Scaling volition cause everything almost the drawn image, including the line width, to be stretched out or squeezed together equally specified. Scaling by a negative amount will flip the picture around. The flipping happens around indicate (0,0), which means it will too flip the direction of the coordinate system. When a horizontal scaling of -i is applied, a shape drawn at x position 100 will end up at what used to be position -100.

So to turn a movie around, we tin't only add cx.scale(-one, 1) before the call to drawImage considering that would move our picture outside of the canvass, where information technology won't be visible. You lot could adjust the coordinates given to drawImage to recoup for this past drawing the image at x position -50 instead of 0. Some other solution, which doesn't require the code that does the cartoon to know about the scale change, is to arrange the axis around which the scaling happens.

At that place are several other methods besides scale that influence the coordinate system for a canvas. You tin rotate later on drawn shapes with the rotate method and move them with the translate method. The interesting—and confusing—matter is that these transformations stack, meaning that each i happens relative to the previous transformations.

So if nosotros translate past 10 horizontal pixels twice, everything volition exist drawn 20 pixels to the correct. If nosotros kickoff move the center of the coordinate organization to (50,50) and so rotate by xx degrees (about 0.1π radians), that rotation will happen around point (50,50).

Stacking transformations

But if we first rotate by 20 degrees and then translate by (l,l), the translation volition happen in the rotated coordinate system and thus produce a dissimilar orientation. The society in which transformations are practical matters.

To flip a moving-picture show effectually the vertical line at a given x position, we can do the post-obit:

          function          flipHorizontally(context,          effectually) {          context.translate(effectually,          0);          context.scale(-          i,          one);          context.translate(-          around,          0); }

We motility the y-axis to where we want our mirror to be, apply the mirroring, and finally move the y-centrality back to its proper place in the mirrored universe. The following picture explains why this works:

Mirroring around a vertical line

This shows the coordinate systems earlier and after mirroring across the key line. The triangles are numbered to illustrate each step. If we depict a triangle at a positive ten position, it would, by default, be in the place where triangle i is. A telephone call to flipHorizontally first does a translation to the right, which gets the states to triangle 2. It and then scales, flipping the triangle over to position 3. This is non where information technology should be, if information technology were mirrored in the given line. The second translate call fixes this—information technology "cancels" the initial translation and makes triangle iv appear exactly where it should.

We can now describe a mirrored grapheme at position (100,0) by flipping the world around the grapheme's vertical center.

          <          canvas          >          </          canvas          >          <          script          >          let          cx          =          certificate.querySelector("canvas").getContext("second");          let          img          =          document.createElement("img");          img.src          =          "img/player.png";          let          spriteW          =          24,          spriteH          =          30;          img.addEventListener("load", ()          =>          {          flipHorizontally(cx,          100          +          spriteW          /          2);          cx.drawImage(img,          0,          0,          spriteW,          spriteH,          100,          0,          spriteW,          spriteH);   });          </          script          >        

Storing and clearing transformations

Transformations stick around. Everything else we draw after drawing that mirrored character would too be mirrored. That might exist inconvenient.

Information technology is possible to save the electric current transformation, do some drawing and transforming, and and then restore the old transformation. This is usually the proper thing to practise for a part that needs to temporarily transform the coordinate system. Commencement, we save whatsoever transformation the lawmaking that called the function was using. And so the role does its affair, adding more than transformations on top of the electric current transformation. Finally, we revert to the transformation we started with.

The salvage and restore methods on the 2D canvas context do this transformation management. They conceptually go along a stack of transformation states. When you lot phone call save, the electric current state is pushed onto the stack, and when you call restore, the state on top of the stack is taken off and used every bit the context'due south current transformation. You lot tin can also call resetTransform to fully reset the transformation.

The branch part in the following example illustrates what you tin do with a part that changes the transformation and and so calls a role (in this case itself), which continues drawing with the given transformation.

This role draws a treelike shape past drawing a line, moving the middle of the coordinate organization to the end of the line, and calling itself twice—first rotated to the left and so rotated to the correct. Every call reduces the length of the branch drawn, and the recursion stops when the length drops below eight.

          <          canvas          width="600"          summit="300"          >          </          canvas          >          <          script          >          let          cx          =          certificate.querySelector("canvas").getContext("2d");          function          branch(length,          angle,          scale) {          cx.fillRect(0,          0,          1,          length);          if          (length          <          8)          return;          cx.save();          cx.translate(0,          length);          cx.rotate(-          bending);          branch(length          *          scale,          angle,          calibration);          cx.rotate(two          *          angle);          branch(length          *          calibration,          angle,          scale);          cx.restore();   }          cx.translate(300,          0);          branch(60,          0.v,          0.8);          </          script          >        

If the calls to relieve and restore were non there, the 2nd recursive call to branch would end up with the position and rotation created by the first call. Information technology wouldn't be continued to the current branch but rather to the innermost, rightmost branch drawn by the get-go call. The resulting shape might also be interesting, but it is definitely not a tree.

Back to the game

We now know enough about canvas drawing to get-go working on a canvas-based display system for the game from the previous chapter. The new display will no longer exist showing simply colored boxes. Instead, we'll use drawImage to draw pictures that represent the game'southward elements.

We define another display object type called CanvasDisplay, supporting the same interface as DOMDisplay from Chapter sixteen, namely, the methods syncState and articulate.

This object keeps a little more information than DOMDisplay. Rather than using the curl position of its DOM element, it tracks its ain viewport, which tells u.s. what role of the level nosotros are currently looking at. Finally, it keeps a flipPlayer belongings so that fifty-fifty when the player is standing still, it keeps facing the management it final moved in.

          course          CanvasDisplay          {          constructor(parent,          level) {          this.canvas          =          document.createElement("canvas");          this.sheet.width          =          Math.min(600,          level.width          *          scale);          this.canvas.height          =          Math.min(450,          level.height          *          scale);          parent.appendChild(this.canvas);          this.cx          =          this.canvas.getContext("2d");          this.flipPlayer          =          faux;          this.viewport          =          {          left:          0,          top:          0,          width:          this.canvas.width          /          scale,          height:          this.canvas.meridian          /          scale          };   }          clear() {          this.canvas.remove();   } }

The syncState method first computes a new viewport and then draws the game scene at the appropriate position.

          CanvasDisplay.prototype.syncState          =          part(state) {          this.updateViewport(state);          this.clearDisplay(state.status);          this.drawBackground(state.level);          this.drawActors(state.actors); };

Contrary to DOMDisplay, this display way does have to redraw the background on every update. Considering shapes on a canvass are just pixels, after we draw them there is no good manner to move them (or remove them). The only way to update the canvas display is to clear it and redraw the scene. Nosotros may besides have scrolled, which requires the groundwork to be in a different position.

The updateViewport method is similar to DOMDisplay's scrollPlayerIntoView method. It checks whether the role player is likewise close to the edge of the screen and moves the viewport when this is the case.

          CanvasDisplay.prototype.updateViewport          =          office(land) {          allow          view          =          this.viewport,          margin          =          view.width          /          3;          permit          thespian          =          state.player;          let          center          =          actor.pos.plus(role player.size.times(0.5));          if          (centre.x          <          view.left          +          margin) {          view.left          =          Math.max(center.10          -          margin,          0);   }          else          if          (center.10          >          view.left          +          view.width          -          margin) {          view.left          =          Math.min(center.x          +          margin          -          view.width,          country.level.width          -          view.width);   }          if          (center.y          <          view.top          +          margin) {          view.top          =          Math.max(center.y          -          margin,          0);   }          else          if          (heart.y          >          view.top          +          view.height          -          margin) {          view.summit          =          Math.min(center.y          +          margin          -          view.height,          country.level.height          -          view.height);   } };

The calls to Math.max and Math.min ensure that the viewport does non end up showing space outside of the level. Math.max(x, 0) makes sure the resulting number is not less than goose egg. Math.min similarly guarantees that a value stays below a given jump.

When clearing the display, we'll employ a slightly unlike color depending on whether the game is won (brighter) or lost (darker).

          CanvasDisplay.prototype.clearDisplay          =          function(status) {          if          (status          ==          "won") {          this.cx.fillStyle          =          "rgb(68, 191, 255)";   }          else          if          (status          ==          "lost") {          this.cx.fillStyle          =          "rgb(44, 136, 214)";   }          else          {          this.cx.fillStyle          =          "rgb(52, 166, 251)";   }          this.cx.fillRect(0,          0,          this.canvas.width,          this.canvass.top); };

To draw the groundwork, we run through the tiles that are visible in the current viewport, using the same trick used in the touches method from the previous chapter.

          let          otherSprites          =          document.createElement("img");          otherSprites.src          =          "img/sprites.png";          CanvasDisplay.prototype.drawBackground          =          function(level) {          let          {left,          meridian,          width,          acme}          =          this.viewport;          let          xStart          =          Math.floor(left);          permit          xEnd          =          Math.ceil(left          +          width);          let          yStart          =          Math.floor(elevation);          let          yEnd          =          Math.ceil(top          +          tiptop);          for          (let          y          =          yStart;          y          <          yEnd;          y          ++) {          for          (let          x          =          xStart;          x          <          xEnd;          x          ++) {          permit          tile          =          level.rows[y][10];          if          (tile          ==          "empty")          continue;          let          screenX          =          (x          -          left)          *          scale;          let          screenY          =          (y          -          elevation)          *          scale;          permit          tileX          =          tile          ==          "lava"          ?          scale          :          0;          this.cx.drawImage(otherSprites,          tileX,          0,          scale,          scale,          screenX,          screenY,          scale,          scale);     }   } };

Tiles that are not empty are drawn with drawImage. The otherSprites epitome contains the pictures used for elements other than the player. It contains, from left to right, the wall tile, the lava tile, and the sprite for a money.

Sprites for our game

Background tiles are xx by xx pixels since we will apply the same scale that we used in DOMDisplay. Thus, the offset for lava tiles is 20 (the value of the calibration binding), and the offset for walls is 0.

We don't bother waiting for the sprite image to load. Calling drawImage with an image that hasn't been loaded yet volition only practice nothing. Thus, nosotros might fail to depict the game properly for the commencement few frames, while the paradigm is still loading, but that is non a serious problem. Since we proceed updating the screen, the right scene will appear as soon as the loading finishes.

The walking character shown before volition be used to represent the player. The code that draws it needs to pick the right sprite and management based on the thespian'due south current motion. The first eight sprites contain a walking animation. When the player is moving along a floor, we cycle through them based on the current time. We want to switch frames every lx milliseconds, so the time is divided by 60 commencement. When the player is standing still, we draw the ninth sprite. During jumps, which are recognized past the fact that the vertical speed is not zero, we use the tenth, rightmost sprite.

Because the sprites are slightly wider than the player object—24 instead of 16 pixels to allow some space for anxiety and arms—the method has to adjust the 10-coordinate and width past a given corporeality (playerXOverlap).

          let          playerSprites          =          document.createElement("img");          playerSprites.src          =          "img/player.png";          const          playerXOverlap          =          4;          CanvasDisplay.prototype.drawPlayer          =          part(histrion,          10,          y,          width,          summit){          width          +=          playerXOverlap          *          ii;          ten          -=          playerXOverlap;          if          (player.speed.x          !=          0) {          this.flipPlayer          =          player.speed.x          <          0;   }          let          tile          =          eight;          if          (player.speed.y          !=          0) {          tile          =          9;   }          else          if          (player.speed.ten          !=          0) {          tile          =          Math.flooring(Date.now()          /          60)          %          8;   }          this.cx.relieve();          if          (this.flipPlayer) {          flipHorizontally(this.cx,          x          +          width          /          2);   }          permit          tileX          =          tile          *          width;          this.cx.drawImage(playerSprites,          tileX,          0,          width,          height,          x,          y,          width,          height);          this.cx.restore(); };

The drawPlayer method is called by drawActors, which is responsible for drawing all the actors in the game.

          CanvasDisplay.prototype.drawActors          =          part(actors) {          for          (let          actor          of          actors) {          let          width          =          actor.size.x          *          calibration;          let          height          =          actor.size.y          *          scale;          permit          x          =          (actor.pos.x          -          this.viewport.left)          *          scale;          let          y          =          (actor.pos.y          -          this.viewport.top)          *          scale;          if          (thespian.type          ==          "player") {          this.drawPlayer(histrion,          x,          y,          width,          superlative);     }          else          {          allow          tileX          =          (actor.type          ==          "coin"          ?          two          :          1)          *          scale;          this.cx.drawImage(otherSprites,          tileX,          0,          width,          height,          x,          y,          width,          height);     }   } };

When drawing something that is not the player, we look at its blazon to find the offset of the right sprite. The lava tile is found at offset 20, and the coin sprite is found at 40 (ii times scale).

We accept to subtract the viewport's position when calculating the player's position since (0,0) on our sheet corresponds to the summit left of the viewport, not the top left of the level. We could too have used translate for this. Either mode works.

This document plugs the new display into runGame:

          <          body          >          <          script          >          runGame(GAME_LEVELS,          CanvasDisplay);          </          script          >          </          body          >        

Choosing a graphics interface

Then when you need to generate graphics in the browser, you lot can cull betwixt obviously HTML, SVG, and sail. At that place is no unmarried best approach that works in all situations. Each option has strengths and weaknesses.

Manifestly HTML has the advantage of existence elementary. Information technology also integrates well with text. Both SVG and canvas allow y'all to draw text, but they won't help you position that text or wrap information technology when it takes upward more 1 line. In an HTML-based picture, information technology is much easier to include blocks of text.

SVG can be used to produce crisp graphics that expect good at whatever zoom level. Unlike HTML, it is designed for drawing and is thus more suitable for that purpose.

Both SVG and HTML build upwardly a data structure (the DOM) that represents your picture. This makes information technology possible to alter elements subsequently they are drawn. If yous need to repeatedly change a small-scale part of a large moving picture in response to what the user is doing or as function of an animation, doing it in a canvas tin exist needlessly expensive. The DOM also allows the states to register mouse consequence handlers on every element in the picture (even on shapes fatigued with SVG). Yous can't practise that with canvas.

But canvas's pixel-oriented approach can be an advantage when drawing a huge number of tiny elements. The fact that information technology does not build up a data structure only but repeatedly draws onto the same pixel surface gives canvas a lower cost per shape.

At that place are too effects, such as rendering a scene one pixel at a time (for example, using a ray tracer) or postprocessing an paradigm with JavaScript (blurring or distorting information technology), that can be realistically handled merely by a pixel-based approach.

In some cases, you may desire to combine several of these techniques. For example, you might draw a graph with SVG or canvas but evidence textual data by positioning an HTML element on top of the picture.

For nondemanding applications, it actually doesn't thing much which interface yous choose. The brandish we built for our game in this affiliate could have been implemented using whatever of these three graphics technologies since it does not demand to draw text, handle mouse interaction, or piece of work with an extraordinarily large number of elements.

Summary

In this chapter we discussed techniques for cartoon graphics in the browser, focusing on the <canvas> element.

A canvas node represents an area in a certificate that our plan may describe on. This drawing is done through a drawing context object, created with the getContext method.

The 2D drawing interface allows us to fill and stroke diverse shapes. The context's fillStyle property determines how shapes are filled. The strokeStyle and lineWidth properties command the way lines are drawn.

Rectangles and pieces of text tin be drawn with a single method telephone call. The fillRect and strokeRect methods depict rectangles, and the fillText and strokeText methods draw text. To create custom shapes, nosotros must first build up a path.

Calling beginPath starts a new path. A number of other methods add lines and curves to the current path. For example, lineTo can add a straight line. When a path is finished, it can exist filled with the fill method or stroked with the stroke method.

Moving pixels from an paradigm or some other canvas onto our canvas is done with the drawImage method. By default, this method draws the whole source image, but by giving it more parameters, y'all can re-create a specific surface area of the epitome. We used this for our game by copying private poses of the game grapheme out of an image that contained many such poses.

Transformations allow you to draw a shape in multiple orientations. A 2D drawing context has a electric current transformation that can be changed with the interpret, scale, and rotate methods. These will affect all subsequent drawing operations. A transformation state can be saved with the save method and restored with the restore method.

When showing an blitheness on a sail, the clearRect method tin can exist used to clear part of the canvass before redrawing it.

Exercises

Shapes

Write a program that draws the following shapes on a canvas:

  1. A trapezoid (a rectangle that is wider on one side)

  2. A red diamond (a rectangle rotated 45 degrees or ¼π radians)

  3. A zigzagging line

  4. A screw made up of 100 directly line segments

  5. A yellow star

The shapes to draw

When drawing the final two, you may want to refer to the explanation of Math.cos and Math.sin in Chapter fourteen, which describes how to get coordinates on a circle using these functions.

I recommend creating a function for each shape. Pass the position, and optionally other backdrop such as the size or the number of points, as parameters. The alternative, which is to hard-lawmaking numbers all over your code, tends to make the code needlessly hard to read and change.

          <          canvass          width="600"          superlative="200"          >          </          canvas          >          <          script          >          let          cx          =          document.querySelector("canvass").getContext("2d");              </          script          >        

The trapezoid (one) is easiest to describe using a path. Pick suitable heart coordinates and add each of the four corners around the middle.

The diamond (2) can be drawn the straightforward way, with a path, or the interesting manner, with a rotate transformation. To apply rotation, you lot will take to apply a flim-flam like to what nosotros did in the flipHorizontally role. Because you want to rotate around the centre of your rectangle and non around the bespeak (0,0), you must start translate to there, so rotate, and and then interpret back.

Brand sure you reset the transformation afterward drawing any shape that creates 1.

For the zigzag (3) information technology becomes impractical to write a new call to lineTo for each line segment. Instead, y'all should use a loop. You tin can have each iteration draw either ii line segments (right and so left again) or one, in which instance you must use the evenness (% two) of the loop index to decide whether to go left or right.

You'll also demand a loop for the spiral (4). If you lot depict a serial of points, with each point moving further forth a circle around the screw'due south center, you lot get a circle. If, during the loop, you vary the radius of the circumvolve on which you are putting the current indicate and become around more than than once, the issue is a spiral.

The star (5) depicted is built out of quadraticCurveTo lines. You could likewise draw one with directly lines. Divide a circle into eight pieces for a star with eight points, or even so many pieces you lot desire. Draw lines between these points, making them curve toward the heart of the star. With quadraticCurveTo, you can employ the center every bit the control point.

The pie nautical chart

Earlier in the chapter, we saw an example programme that drew a pie nautical chart. Modify this programme so that the proper noun of each category is shown next to the slice that represents information technology. Try to find a pleasing-looking way to automatically position this text that would work for other information sets as well. You may assume that categories are big enough to leave ample room for their labels.

You might need Math.sin and Math.cos again, which are described in Chapter xiv.

          <          canvas          width="600"          tiptop="300"          >          </          sheet          >          <          script          >          let          cx          =          certificate.querySelector("canvass").getContext("second");          permit          total          =          results          .reduce((sum, {count})          =>          sum          +          count,          0);          let          currentAngle          =          -          0.5          *          Math.PI;          let          centerX          =          300,          centerY          =          150;              for          (let          issue          of          results) {          allow          sliceAngle          =          (issue.count          /          full)          *          ii          *          Math.PI;          cx.beginPath();          cx.arc(centerX,          centerY,          100,          currentAngle,          currentAngle          +          sliceAngle);          currentAngle          +=          sliceAngle;          cx.lineTo(centerX,          centerY);          cx.fillStyle          =          result.color;          cx.make full();   }          </          script          >        

You will need to call fillText and set the context'due south textAlign and textBaseline properties in such a way that the text ends upward where y'all desire it.

A sensible way to position the labels would be to put the text on the line going from the center of the pie through the eye of the slice. You don't want to put the text directly against the side of the pie but rather move the text out to the side of the pie by a given number of pixels.

The angle of this line is currentAngle + 0.5 * sliceAngle. The following code finds a position on this line 120 pixels from the center:

            let            middleAngle            =            currentAngle            +            0.5            *            sliceAngle;            let            textX            =            Math.cos(middleAngle)            *            120            +            centerX;            allow            textY            =            Math.sin(middleAngle)            *            120            +            centerY;

For textBaseline, the value "centre" is probably appropriate when using this arroyo. What to use for textAlign depends on which side of the circumvolve nosotros are on. On the left, it should be "correct", and on the right, it should be "left", so that the text is positioned away from the pie.

If you lot are not certain how to find out which side of the circle a given angle is on, look to the explanation of Math.cos in Chapter 14. The cosine of an angle tells the states which x-coordinate it corresponds to, which in turn tells united states of america exactly which side of the circumvolve we are on.

A bouncing ball

Use the requestAnimationFrame technique that we saw in Affiliate fourteen and Chapter xvi to draw a box with a bouncing brawl in it. The ball moves at a abiding speed and bounces off the box'southward sides when information technology hits them.

          <          canvas          width="400"          height="400"          >          </          sheet          >          <          script          >          let          cx          =          document.querySelector("sheet").getContext("2d");          let          lastTime          =          goose egg;          function          frame(time) {          if          (lastTime          !=          nothing) {          updateAnimation(Math.min(100,          time          -          lastTime)          /          m);     }          lastTime          =          time;          requestAnimationFrame(frame);   }          requestAnimationFrame(frame);          function          updateAnimation(step) {        }          </          script          >        

A box is easy to draw with strokeRect. Ascertain a binding that holds its size or define two bindings if your box'southward width and superlative differ. To create a circular ball, start a path and phone call arc(x, y, radius, 0, vii), which creates an arc going from zero to more than a whole circle. Then fill the path.

To model the ball'south position and speed, you lot can use the Vec class from Chapter 16 (which is available on this folio). Requite it a starting speed, preferably 1 that is not purely vertical or horizontal, and for every frame multiply that speed by the amount of time that elapsed. When the ball gets besides close to a vertical wall, capsize the x component in its speed. Likewise, capsize the y component when it hits a horizontal wall.

Afterwards finding the ball's new position and speed, use clearRect to delete the scene and redraw it using the new position.

Precomputed mirroring

One unfortunate matter about transformations is that they slow down the drawing of bitmaps. The position and size of each pixel has to exist transformed, and though it is possible that browsers will go cleverer almost transformation in the futurity, they currently crusade a measurable increment in the time it takes to draw a bitmap.

In a game like ours, where we are cartoon only a single transformed sprite, this is a nonissue. Merely imagine that we need to draw hundreds of characters or thousands of rotating particles from an explosion.

Think of a way to allow united states to draw an inverted character without loading additional image files and without having to make transformed drawImage calls every frame.

The key to the solution is the fact that we tin can use a canvas element as a source image when using drawImage. It is possible to create an extra <sail> element, without adding information technology to the document, and draw our inverted sprites to it, once. When cartoon an actual frame, we just copy the already inverted sprites to the main sail.

Some care would be required because images do not load instantly. We practice the inverted drawing only once, and if we do it before the image loads, it won't draw annihilation. A "load" handler on the epitome can be used to describe the inverted images to the extra canvas. This canvas can be used equally a drawing source immediately (it'll simply be blank until we depict the character onto it).

kelsobacquithetty56.blogspot.com

Source: https://eloquentjavascript.net/17_canvas.html

0 Response to "Draw Circle on Canvas Onclick"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel