Draw Circles on a Canvas With a Button Click
Drawing graphics
Graphics on the Web
As we talked nearly in our HTML Multimedia and embedding module, the Web was originally just text, which was very boring, so images were introduced — first via the <img>
element and after via CSS properties such equally background-prototype
, and SVG.
This nonetheless was still not enough. While yous could apply CSS and JavaScript to animate (and otherwise manipulate) SVG vector images — equally they are represented by markup — there was nonetheless no way to practice the same for bitmap images, and the tools available were rather express. The Web all the same had no style to effectively create animations, games, 3D scenes, and other requirements commonly handled by lower level languages such as C++ or Java.
The situation started to improve when browsers began to support the <canvas>
element and associated Sheet API — Apple invented it in around 2004, and other browsers followed by implementing it in the years that followed. Every bit you'll see below, canvas provides many useful tools for creating second animations, games, information visualizations, and other types of app, especially when combined with some of the other APIs the web platform provides.
The below example shows a elementary 2D canvas-based bouncing balls animation that we originally met in our Introducing JavaScript objects module:
Effectually 2006–2007, Mozilla started piece of work on an experimental 3D canvass implementation. This became WebGL, which gained traction among browser vendors, and was standardized around 2009–2010. WebGL allows you to create real 3D graphics within your web browser; the beneath example shows a simple rotating WebGL cube:
This article will focus mainly on 2D sheet, equally raw WebGL lawmaking is very complex. We will however show how to use a WebGL library to create a 3D scene more easily, and you can detect a tutorial roofing raw WebGL elsewhere — encounter Getting started with WebGL.
Notation: Bones canvass functionality is supported well across browsers, with the exception of IE eight and below for 2nd sail, and IE xi and below for WebGL.
Agile learning: Getting started with a <sheet>
If y'all want to create a 2nd or 3D scene on a web page, you need to start with an HTML <canvas>
element. This chemical element is used to define the surface area on the folio into which the image will be fatigued. This is as simple every bit including the element on the page:
<canvas width = "320" meridian = "240" > </canvas >
This will create a canvas on the folio with a size of 320 by 240 pixels.
Within the sheet tags, you tin can put some fallback content, which is shown if the user'due south browser doesn't support sail.
<sail width = "320" elevation = "240" > <p > Your browser doesn't support canvas. Boo hoo! </p > </canvas >
Of course, the above bulletin is really unhelpful! In a existent example you'd want to relate the fallback content to the canvas content. For case, if you were rendering a constantly updating graph of stock prices, the fallback content could exist a static prototype of the latest stock graph, with alt text saying what the prices are in text.
Creating and sizing our canvas
Let's showtime by creating our own sheet that we draw hereafter experiments on to.
- First make a local copy of the 0_canvas_start directory. Information technology contains three files:
- "index.html"
- "script.js"
- "style.css"
- Open up "index.html", and add the following code into it, just below the opening
<trunk>
tag:<sail class = "myCanvas" > <p > Add suitable fallback here. </p > </sheet >
class
to the<canvas>
element so it volition exist easier to select if we have multiple canvases on the folio, simply we accept removed thewidth
andsuperlative
attributes for now (y'all could add them back in if you wanted, simply nosotros will ready them using JavaScript in a below section). Canvases with no explicit width and height default to 300 pixels broad by 150 pixels loftier. - At present open "script.js" and add the following lines of JavaScript:
const canvas = document. querySelector ( '.myCanvas' ) ; const width = sheet.width = window.innerWidth; const height = canvas.height = window.innerHeight;
sheet
constant. In the second line we gear up both a new constantwidth
and the canvass'width
property equal toWindow.innerWidth
(which gives us the viewport width). In the third line we fix both a new abidingheight
and the canvas'height
belongings equal toWindow.innerHeight
(which gives us the viewport tiptop). So now we have a canvas that fills the entire width and meridian of the browser window! You'll as well see that we are chaining assignments together with multiple equals signs — this is allowed in JavaScript, and it is a good technique if you want to make multiple variables all equal to the same value. We wanted to brand the canvas width and elevation easily accessible in the width/height variables, equally they are useful values to have bachelor for subsequently (for example, if you want to draw something exactly halfway across the width of the canvas).
Note: Yous should generally set the size of the prototype using HTML attributes or DOM properties, every bit explained above. You could use CSS, but the trouble then is that the sizing is done after the canvas has rendered, and just similar whatsoever other image (the rendered canvas is just an prototype), the image could get pixelated/distorted.
Getting the canvas context and last setup
Nosotros need to do one final thing before nosotros can consider our canvas template finished. To draw onto the canvas we need to get a special reference to the drawing area chosen a context. This is done using the HTMLCanvasElement.getContext()
method, which for basic usage takes a unmarried string as a parameter representing the type of context you want to think.
In this case we want a 2d canvas, so add the following JavaScript line below the others in "script.js":
const ctx = canvas. getContext ( 'second' ) ;
Note: other context values you could cull include webgl
for WebGL, webgl2
for WebGL 2, etc., but we won't need those in this article.
So that's it — our canvas is at present primed and ready for drawing on! The ctx
variable now contains a CanvasRenderingContext2D
object, and all cartoon operations on the canvas will involve manipulating this object.
Let's do ane final matter earlier we motion on. We'll color the canvas background black to give you a first taste of the canvas API. Add the following lines at the bottom of your JavaScript:
ctx.fillStyle = 'rgb(0, 0, 0)' ; ctx. fillRect ( 0 , 0 , width, tiptop) ;
Hither we are setting a make full color using the canvass' fillStyle
property (this takes color values just similar CSS properties practice), then drawing a rectangle that covers the unabridged area of the canvas with thefillRect
method (the start 2 parameters are the coordinates of the rectangle's top left manus corner; the last two are the width and height you want the rectangle drawn at — we told you those width
and pinnacle
variables would be useful)!
OK, our template is done and it's fourth dimension to move on.
2D canvas basics
As we said above, all drawing operations are done by manipulating a CanvasRenderingContext2D
object (in our case, ctx
). Many operations need to be given coordinates to pinpoint exactly where to draw something — the top left of the canvas is point (0, 0), the horizontal (ten) axis runs from left to right, and the vertical (y) axis runs from top to bottom.
Cartoon shapes tends to be done using the rectangle shape primitive, or past tracing a line along a certain path and and so filling in the shape. Beneath we'll testify how to do both.
Simple rectangles
Let's outset with some simple rectangles.
- Starting time of all, accept a copy of your newly coded sheet template (or make a local copy of the 1_canvas_template directory if you didn't follow the above steps).
- Next, add together the post-obit lines to the bottom of your JavaScript:
ctx.fillStyle = 'rgb(255, 0, 0)' ; ctx. fillRect ( fifty , 50 , 100 , 150 ) ;
- Let's add another rectangle into the mix — a greenish one this time. Add together the following at the bottom of your JavaScript:
ctx.fillStyle = 'rgb(0, 255, 0)' ; ctx. fillRect ( 75 , 75 , 100 , 100 ) ;
- Note that y'all tin can describe semi-transparent graphics by specifying a semi-transparent color, for example by using
rgba()
. Thea
value defines what's called the "alpha channel, " or the amount of transparency the color has. The higher its value, the more than it will obscure whatever'south behind it. Add the following to your code:ctx.fillStyle = 'rgba(255, 0, 255, 0.75)' ; ctx. fillRect ( 25 , 100 , 175 , l ) ;
- Now endeavor drawing some more rectangles of your own; have fun!
Strokes and line widths
So far we've looked at drawing filled rectangles, but yous can likewise draw rectangles that are just outlines (chosen strokes in graphic blueprint). To set the color you want for your stroke, you use the strokeStyle
property; drawing a stroke rectangle is done using strokeRect
.
- Add the following to the previous example, once more below the previous JavaScript lines:
ctx.strokeStyle = 'rgb(255, 255, 255)' ; ctx. strokeRect ( 25 , 25 , 175 , 200 ) ;
- The default width of strokes is 1 pixel; you can adjust the
lineWidth
property value to change this (it takes a number representing the number of pixels broad the stroke is). Add the following line in betwixt the previous two lines:
Now you should see that your white outline has become much thicker! That's it for now. At this signal your example should wait like this:
Drawing paths
If you want to draw anything more complex than a rectangle, you need to draw a path. Basically, this involves writing code to specify exactly what path the pen should move along on your sheet to trace the shape you want to depict. Sail includes functions for drawing straight lines, circles, Bézier curves, and more.
Permit's get-go the department off past making a fresh copy of our sheet template (1_canvas_template), in which to draw the new case.
We'll be using some common methods and backdrop across all of the beneath sections:
-
beginPath()
— offset drawing a path at the bespeak where the pen currently is on the canvas. On a new canvas, the pen starts out at (0, 0). -
moveTo()
— move the pen to a different point on the canvas, without recording or tracing the line; the pen "jumps" to the new position. -
fill()
— describe a filled shape by filling in the path you've traced so far. -
stroke()
— depict an outline shape by drawing a stroke along the path you've drawn then far. - You tin can likewise use features like
lineWidth
andfillStyle
/strokeStyle
with paths as well as rectangles.
A typical, simple path-drawing performance would look something like so:
ctx.fillStyle = 'rgb(255, 0, 0)' ; ctx. beginPath ( ) ; ctx. moveTo ( l , fifty ) ; // draw your path ctx. fill ( ) ;
Drawing lines
Allow'southward draw an equilateral triangle on the canvas.
- Starting time of all, add the following helper function to the bottom of your lawmaking. This converts degree values to radians, which is useful because whenever y'all need to provide an angle value in JavaScript, it will almost always be in radians, merely humans normally think in degrees.
part degToRad ( degrees ) { return degrees * Math. PI / 180 ; }
- Next, start off your path by adding the following below your previous addition; here we set up a color for our triangle, start drawing a path, then motility the pen to (50, 50) without drawing annihilation. That's where we'll outset drawing our triangle.
ctx.fillStyle = 'rgb(255, 0, 0)' ; ctx. beginPath ( ) ; ctx. moveTo ( 50 , 50 ) ;
- At present add the following lines at the lesser of your script:
ctx. lineTo ( 150 , 50 ) ; const triHeight = 50 * Math. tan ( degToRad ( 60 ) ) ; ctx. lineTo ( 100 , 50 + triHeight) ; ctx. lineTo ( 50 , 50 ) ; ctx. fill ( ) ;
- The longest side is called the hypotenuse
- The side next to the 60 caste bending is called the adjacent — which we know is 50 pixels, every bit it is half of the line we simply drew.
- The side contrary the 60 degree angle is called the opposite, which is the peak of the triangle we want to summate.
50 * Math.tan(degToRad(60))
. Nosotros use ourdegToRad()
office to convert threescore degrees to radians, every bitMath.tan()
expects an input value in radians. - With the height calculated, nosotros draw another line to
(100, 50 + triHeight)
. The X coordinate is elementary; it must exist halfway betwixt the previous 2 10 values we set up. The Y value on the other manus must exist 50 plus the triangle height, as we know the top of the triangle is fifty pixels from the top of the canvas. - The next line draws a line back to the starting bespeak of the triangle.
- Concluding of all, we run
ctx.fill()
to terminate the path and make full in the shape.
Cartoon circles
Now allow's look at how to draw a circle in canvas. This is achieved using the arc()
method, which draws all or part of a circle at a specified signal.
- Let's add an arc to our canvass — add the following to the bottom of your code:
ctx.fillStyle = 'rgb(0, 0, 255)' ; ctx. beginPath ( ) ; ctx. arc ( 150 , 106 , 50 , degToRad ( 0 ) , degToRad ( 360 ) , false ) ; ctx. fill ( ) ;
arc()
takes half-dozen parameters. The starting time two specify the position of the arc'southward center (Ten and Y, respectively). The tertiary is the circle'due south radius, the fourth and fifth are the beginning and end angles at which to draw the circle (so specifying 0 and 360 degrees gives usa a full circle), and the sixth parameter defines whether the circumvolve should be drawn counterclockwise (anticlockwise) or clockwise (false
is clockwise).Note: 0 degrees is horizontally to the correct.
- Let's try adding another arc:
ctx.fillStyle = 'xanthous' ; ctx. beginPath ( ) ; ctx. arc ( 200 , 106 , l , degToRad ( - 45 ) , degToRad ( 45 ) , truthful ) ; ctx. lineTo ( 200 , 106 ) ; ctx. fill ( ) ;
- We have set the last parameter of
arc()
totrue
, meaning that the arc is drawn counterclockwise, which means that even though the arc is specified as starting at -45 degrees and ending at 45 degrees, we draw the arc around the 270 degrees non inside this portion. If you were to altertrue
tofalse
and and then re-run the code, only the ninety caste slice of the circumvolve would be drawn. - Before calling
fill()
, we draw a line to the center of the circle. This means that we get the rather nice Pac-Man-manner cutout rendered. If you removed this line (try information technology!) then re-ran the lawmaking, you lot'd get just an edge of the circumvolve chopped off between the starting time and end point of the arc. This illustrates another important point of the sail — if yous effort to fill up an incomplete path (i.e. one that is not closed), the browser fills in a directly line between the start and end signal and then fills it in.
- We have set the last parameter of
That'south information technology for now; your concluding example should await similar this:
Notation: The finished code is available on GitHub equally 3_canvas_paths.
Note: To find out more almost avant-garde path drawing features such as Bézier curves, check out our Drawing shapes with canvas tutorial.
Text
Canvass as well has features for drawing text. Let's explore these briefly. Start by making some other fresh re-create of our canvass template (1_canvas_template) in which to draw the new instance.
Text is fatigued using ii methods:
-
fillText()
— draws filled text. -
strokeText()
— draws outline (stroke) text.
Both of these take iii properties in their basic usage: the text string to draw and the X and Y coordinates of the point to start drawing the text at. This works out as the lesser left corner of the text box (literally, the box surrounding the text you draw), which might confuse you equally other cartoon operations tend to start from the pinnacle left corner — bear this in listen.
In that location are as well a number of properties to help control text rendering such as font
, which lets you specify font family, size, etc. It takes every bit its value the aforementioned syntax every bit the CSS font
property.
Endeavor adding the following block to the bottom of your JavaScript:
ctx.strokeStyle = 'white' ; ctx.lineWidth = 1 ; ctx.font = '36px arial' ; ctx. strokeText ( 'Sheet text' , 50 , 50 ) ; ctx.fillStyle = 'crimson' ; ctx.font = '48px georgia' ; ctx. fillText ( 'Sail text' , fifty , 150 ) ;
Here we draw 2 lines of text, one outline and the other stroke. The final example should look like and then:
Notation: The finished lawmaking is bachelor on GitHub as 4_canvas_text.
Have a play and meet what you can come up up with! You can find more data on the options available for canvas text at Drawing text.
Drawing images onto sheet
It is possible to render external images onto your canvas. These can exist uncomplicated images, frames from videos, or the content of other canvases. For the moment we'll but look at the case of using some unproblematic images on our canvas.
- As before, make another fresh copy of our canvas template (1_canvas_template) in which to draw the new example. Images are drawn onto canvass using the
drawImage()
method. The simplest version takes three parameters — a reference to the image you want to return, and the X and Y coordinates of the image'south top left corner. - Allow's start by getting an image source to embed in our canvas. Add the following lines to the lesser of your JavaScript:
const paradigm = new Image ( ) ; image.src = 'firefox.png' ;
HTMLImageElement
object using theImage()
constructor. The returned object is the same type as that which is returned when you grab a reference to an existing<img>
element). We then set itssrc
attribute to equal our Firefox logo image. At this indicate, the browser starts loading the image. - We could now effort to embed the image using
drawImage()
, but we demand to make certain the prototype file has been loaded offset, otherwise the code will fail. We can reach this using theload
issue, which will simply be fired when the paradigm has finished loading. Add the post-obit block beneath the previous one:image. addEventListener ( 'load' , ( ) => ctx. drawImage (paradigm, 20 , 20 , 185 , 175 , 50 , 50 , 185 , 175 ) ) ;
- But there'south more! What if we desire to brandish simply a part of the paradigm, or to resize it? We can do both with the more complex version of
drawImage()
. Update yourctx.drawImage()
line like so:ctx. drawImage (prototype, 20 , 20 , 185 , 175 , 50 , l , 185 , 175 ) ;
- The first parameter is the image reference, every bit earlier.
- Parameters two and 3 ascertain the coordinates of the top left corner of the area yous desire to cut out of the loaded prototype, relative to the top-left corner of the prototype itself. Zilch to the left of the first parameter or above the second will be drawn.
- Parameters 4 and v define the width and height of the area nosotros want to cut out from the original epitome nosotros loaded.
- Parameters half dozen and 7 ascertain the coordinates at which you want to describe the top-left corner of the cut-out portion of the prototype, relative to the top-left corner of the sail.
- Parameters 8 and 9 define the width and superlative to draw the cut-out area of the paradigm. In this case, we have specified the aforementioned dimensions every bit the original slice, just yous could resize it by specifying different values.
The final example should look like and so:
Notation: The finished code is available on GitHub as 5_canvas_images.
Loops and animations
We accept so far covered some very basic uses of 2D canvas, just really you won't experience the full power of canvas unless you update or animate it in some way. After all, canvass does provide scriptable images! If you aren't going to change anything, and so you lot might likewise merely utilise static images and save yourself all the work.
Creating a loop
Playing with loops in canvas is rather fun — you can run sail commands inside a for
(or other type of) loop just like any other JavaScript code.
Allow's build a unproblematic example.
- Make some other fresh re-create of our sheet template (1_canvas_template) and open it in your code editor.
- Add the following line to the bottom of your JavaScript. This contains a new method,
translate()
, which moves the origin signal of the canvas:ctx. translate (width/ 2 , height/ 2 ) ;
- Now add together the post-obit code to the lesser of the JavaScript:
function degToRad ( degrees ) { return degrees * Math. PI / 180 ; } role rand ( min, max ) { return Math. floor (Math. random ( ) * (max-min+ 1 ) ) + (min) ; } let length = 250 ; let moveOffset = twenty ; for ( allow i = 0 ; i < length; i++ ) { }
degToRad()
function we saw in the triangle instance above, arand()
office that returns a random number between given lower and upper bounds,length
andmoveOffset
variables (which we'll find out more virtually later), and an emptyfor
loop. - The idea here is that we'll draw something on the sail within the
for
loop, and iterate on it each time so we can create something interesting. Add the post-obit code inside yourfor
loop:ctx.fillStyle = ` rgba( ${ 255 -length} ,0, ${ 255 -length} ,0.ix) ` ; ctx. beginPath ( ) ; ctx. moveTo (moveOffset,moveOffset) ; ctx. lineTo (moveOffset+length,moveOffset) ; const triHeight = length/ 2 * Math. tan ( degToRad ( 60 ) ) ; ctx. lineTo (moveOffset+ (length/ two ) ,moveOffset+triHeight) ; ctx. lineTo (moveOffset,moveOffset) ; ctx. fill ( ) ; length-- ; moveOffset += 0.7 ; ctx. rotate ( degToRad ( 5 ) ) ;
- Set the
fillStyle
to be a shade of slightly transparent purple, which changes each fourth dimension based on the value oflength
. As you'll run across afterward the length gets smaller each fourth dimension the loop runs, so the effect here is that the color gets brighter with each successive triangle fatigued. - Brainstorm the path.
- Move the pen to a coordinate of
(moveOffset, moveOffset)
; This variable defines how far nosotros want to move each time we draw a new triangle. - Draw a line to a coordinate of
(moveOffset+length, moveOffset)
. This draws a line of lengthlength
parallel to the 10 centrality. - Calculate the triangle's height, every bit before.
- Draw a line to the downwardly-pointing corner of the triangle, so draw a line dorsum to the start of the triangle.
- Call
fill up()
to fill in the triangle. - Update the variables that describe the sequence of triangles, so we can be gear up to depict the next ane. We decrease the
length
value by one, so the triangles go smaller each time; increasemoveOffset
by a small amount so each successive triangle is slightly further away, and use another new function,rotate()
, which allows the states to rotate the entire canvas! We rotate it by 5 degrees before drawing the next triangle.
- Set the
That's information technology! The final example should expect like so:
At this signal, we'd like to encourage you to play with the example and arrive your own! For example:
- Draw rectangles or arcs instead of triangles, or even embed images.
- Play with the
length
andmoveOffset
values. - Introduce some random numbers using that
rand()
function we included above but didn't utilise.
Animations
The loop example we built to a higher place was fun, but actually you need a abiding loop that keeps going and going for any serious canvas applications (such every bit games and real time visualizations). If you call back of your canvas as being similar a picture, you lot actually want the display to update on each frame to show the updated view, with an platonic refresh rate of threescore frames per 2d so that movement appears dainty and smooth to the human heart.
At that place are a few JavaScript functions that will permit you to run functions repeatedly, several times a 2d, the all-time one for our purposes here beingness window.requestAnimationFrame()
. It takes one parameter — the proper noun of the part you lot want to run for each frame. The next time the browser is prepare to update the screen, your function will get called. If that function draws the new update to your blitheness, then calls requestAnimationFrame()
over again just earlier the end of the function, the animation loop volition continue to run. The loop ends when y'all end calling requestAnimationFrame()
or if you call window.cancelAnimationFrame()
after calling requestAnimationFrame()
simply before the frame is called.
Note: Information technology'southward good exercise to call cancelAnimationFrame()
from your main code when you're done using the blitheness, to ensure that no updates are however waiting to be run.
The browser works out complex details such as making the blitheness run at a consistent speed, and not wasting resource animating things that can't be seen.
To run into how information technology works, let's quickly look over again at our Bouncing Balls example (come across it live, and also run into the source lawmaking). The code for the loop that keeps everything moving looks similar this:
function loop ( ) { ctx.fillStyle = 'rgba(0, 0, 0, 0.25)' ; ctx. fillRect ( 0 , 0 , width, height) ; for ( const brawl of assurance) { ball. depict ( ) ; ball. update ( ) ; brawl. collisionDetect ( ) ; } requestAnimationFrame (loop) ; } loop ( ) ;
We run the loop()
function once at the bottom of the lawmaking to start the cycle, cartoon the first animation frame; the loop()
function then takes charge of calling requestAnimationFrame(loop)
to run the next frame of the animation, again and again.
Note that on each frame we are completely clearing the sheet and redrawing everything. For every ball present we describe it, update its position, and check to see if it is colliding with any other balls. Once you've drawn a graphic to a canvas, there's no mode to manipulate that graphic individually similar y'all tin with DOM elements. You can't motility each ball around on the sail, because once it'due south drawn, it'southward part of the canvas, and is not an individual accessible element or object. Instead, you take to erase and redraw, either past erasing the entire frame and redrawing everything, or by having code that knows exactly what parts need to be erased and only erases and redraws the minimum area of the canvas necessary.
Optimizing blitheness of graphics is an entire specialty of programming, with lots of clever techniques bachelor. Those are beyond what we need for our example, though!
In general, the process of doing a canvass blitheness involves the following steps:
- Articulate the canvass contents (e.g. with
fillRect()
orclearRect()
). - Save state (if necessary) using
relieve()
— this is needed when y'all want to save settings you've updated on the sail earlier continuing, which is useful for more advanced applications. - Draw the graphics you lot are animating.
- Restore the settings you saved in step 2, using
restore()
- Call
requestAnimationFrame()
to schedule drawing of the next frame of the blitheness.
Note: We won't cover relieve()
and restore()
here, but they are explained nicely in our Transformations tutorial (and the ones that follow it).
A uncomplicated character animation
Now let's create our ain simple animation — we'll get a character from a certain rather awesome retro computer game to walk across the screen.
- Make another fresh re-create of our canvas template (1_canvas_template) and open it in your code editor.
- At the bottom of the JavaScript, add the following line to once again make the coordinate origin sit in the heart of the canvas:
ctx. translate (width/ two , height/ 2 ) ;
- Now let's create a new
HTMLImageElement
object, set itssrc
to the image nosotros desire to load, and add together anonload
outcome handler that will crusade thedescribe()
function to burn when the prototype is loaded:const image = new Image ( ) ; image.src = 'walk-correct.png' ; epitome.onload = draw;
- Now nosotros'll add some variables to keep track of the position the sprite is to be drawn on the screen, and the sprite number nosotros want to display.
let sprite = 0 ; let posX = 0 ;
drawImage()
to chop out a single sprite image from the spritesheet and display just that part, like nosotros did to a higher place with the Firefox logo. The X coordinate of the slice will have to exist a multiple of 102, and the Y coordinate will always exist 0. The slice size will always be 102 by 148 pixels. - Now let'due south insert an empty
describe()
function at the bottom of the code, ready for filling up with some code: - The rest of the code in this section goes within
draw()
. First, add together the following line, which clears the sail to prepare for drawing each frame. Discover that we have to specify the top-left corner of the rectangle as-(width/two), -(height/2)
because we specified the origin position every bitwidth/2, height/2
earlier on.ctx. fillRect ( - (width/ two ) , - (height/ two ) , width, meridian) ;
- Side by side, we'll draw our image using drawImage — the 9-parameter version. Add the following:
ctx. drawImage (image, (sprite* 102 ) , 0 , 102 , 148 , 0 +posX, - 74 , 102 , 148 ) ;
- We specify
image
as the image to embed. - Parameters 2 and 3 specify the tiptop-left corner of the piece to cut out of the source image, with the X value every bit
sprite
multiplied by 102 (wheresprite
is the sprite number between 0 and 5) and the Y value always 0. - Parameters 4 and 5 specify the size of the slice to cut out — 102 pixels past 148 pixels.
- Parameters 6 and vii specify the tiptop-left corner of the box into which to draw the slice on the canvas — the X position is 0 +
posX
, meaning that we can alter the drawing position past altering theposX
value. - Parameters 8 and 9 specify the size of the image on the canvass. Nosotros just want to go on its original size, so we specify 102 and 148 as the width and meridian.
- We specify
- At present nosotros'll alter the
sprite
value later on each draw — well, later some of them anyhow. Add the post-obit block to the bottom of thedraw()
function:if (posX % thirteen === 0 ) { if (sprite === 5 ) { sprite = 0 ; } else { sprite++ ; } }
if (posX % thirteen === 0) { ... }
. We utilise the modulo (%
) operator (also known equally the remainder operator) to bank check whether theposX
value can exist exactly divided by thirteen with no residue. If so, we move on to the side by side sprite by incrementingsprite
(wrapping to 0 after we're done with sprite #v). This effectively means that we are only updating the sprite on every 13th frame, or roughly nigh v frames a second (requestAnimationFrame()
calls united states at up to sixty frames per second if possible). We are deliberately slowing down the frame charge per unit because we just accept 6 sprites to work with, and if we display one every 60th of a second, our character will motility fashion also fast! Inside the outer block nosotros use anif ... else
statement to cheque whether thesprite
value is at 5 (the last sprite, given that the sprite numbers run from 0 to 5). If nosotros are showing the last sprite already, nosotros resetsprite
dorsum to 0; if not we only increment it by one. - Next nosotros need to work out how to change the
posX
value on each frame — add the following code block simply beneath your terminal one.if (posX > width/ two ) { let newStartPos = - ( (width/ 2 ) + 102 ) ; posX = Math. ceil (newStartPos) ; panel. log (posX) ; } else { posX += two ; }
if ... else
statement to encounter if the value ofposX
has become greater thanwidth/ii
, which means our graphic symbol has walked off the right border of the screen. If so, we calculate a position that would put the character just to the left of the left side of the screen. If our character hasn't yet walked off the edge of the screen, we incrementposX
past ii. This will make him move a little bit to the correct the side by side time we draw him. - Finally, we need to make the animation loop by calling
requestAnimationFrame()
at the bottom of thedraw()
function:window. requestAnimationFrame (draw) ;
That'due south it! The concluding case should look like so:
A simple drawing application
As a final animation example, we'd like to show you a very simple drawing application, to illustrate how the animation loop can be combined with user input (like mouse movement, in this instance). Nosotros won't become you to walk through and build this one; we'll but explore the nearly interesting parts of the lawmaking.
The instance can be found on GitHub as 8_canvas_drawing_app, and you lot tin play with it alive below:
Let'south await at the most interesting parts. First of all, we proceed track of the mouse's X and Y coordinates and whether it is being clicked or non with 3 variables: curX
, curY
, and pressed
. When the mouse moves, we fire a part set as the onmousemove
event handler, which captures the current Ten and Y values. We besides use onmousedown
and onmouseup
consequence handlers to change the value of pressed
to true
when the mouse push is pressed, and back to false
again when it is released.
let curX; let curY; let pressed = fake ; // update mouse pointer coordinates certificate. addEventListener ( 'mousemove' , e => { curX = (window.Event) ? eastward.pageX : e.clientX + (certificate.documentElement.scrollLeft ? document.documentElement.scrollLeft : certificate.torso.scrollLeft) ; curY = (window.Event) ? eastward.pageY : e.clientY + (certificate.documentElement.scrollTop ? certificate.documentElement.scrollTop : document.body.scrollTop) ; } ) ; sail. addEventListener ( 'mousedown' , ( ) => pressed = truthful ) ; canvas. addEventListener ( 'mouseup' , ( ) => pressed = false ) ;
When the "Clear canvass" push button is pressed, nosotros run a elementary role that clears the whole sheet back to blackness, the same way we've seen before:
clearBtn. addEventListener ( 'click' , ( ) => { ctx.fillStyle = 'rgb(0,0,0)' ; ctx. fillRect ( 0 , 0 ,width,acme) ; } ) ;
The cartoon loop is pretty unproblematic this time around — if pressed is true
, we draw a circumvolve with a fill up style equal to the value in the color picker, and a radius equal to the value set in the range input. Nosotros take to describe the circumvolve 85 pixels above where nosotros measured it from, considering the vertical measurement is taken from the top of the viewport, but we are drawing the circle relative to the superlative of the sail, which starts below the 85 pixel-high toolbar. If we drew information technology with merely curY
every bit the y coordinate, it would appear 85 pixels lower than the mouse position.
function draw ( ) { if (pressed) { ctx.fillStyle = colorPicker.value; ctx. beginPath ( ) ; ctx. arc (curX, curY- 85 , sizePicker.value, degToRad ( 0 ) , degToRad ( 360 ) , fake ) ; ctx. fill ( ) ; } requestAnimationFrame (depict) ; } draw ( ) ;
Annotation: The <input>
range
and color
types are supported fairly well across browsers, with the exception of Internet Explorer versions less than ten; also Safari doesn't yet support color
. If your browser doesn't support these inputs, they will autumn back to simple text fields and yous'll only have to enter valid colour/number values yourself.
WebGL
It'southward at present time to exit 2D behind, and take a quick look at 3D canvass. 3D canvas content is specified using the WebGL API, which is a completely divide API from the 2D canvas API, fifty-fifty though they both render onto <canvas>
elements.
WebGL is based on OpenGL (Open Graphics Library), and allows you to communicate directly with the reckoner'southward GPU. As such, writing raw WebGL is closer to depression level languages such equally C++ than regular JavaScript; it is quite circuitous but incredibly powerful.
Using a library
Because of its complexity, almost people write 3D graphics code using a third political party JavaScript library such every bit 3.js, PlayCanvas, or Babylon.js. Most of these piece of work in a similar way, providing functionality to create primitive and custom shapes, position viewing cameras and lighting, covering surfaces with textures, and more. They handle the WebGL for you, letting you work on a higher level.
Yes, using ane of these ways learning another new API (a 3rd party one, in this case), only they are a lot simpler than coding raw WebGL.
Recreating our cube
Let's look at a simple case of how to create something with a WebGL library. We'll choose Three.js, as information technology is one of the most popular ones. In this tutorial we'll create the 3D spinning cube we saw before.
- To beginning with, brand a local copy of threejs-cube/index.html in a new folder, and then relieve a re-create of metal003.png in the same binder. This is the paradigm we'll use equally a surface texture for the cube after on.
- Next, create a new file called
script.js
, again in the aforementioned binder as before. - Next, yous need to download the three.min.js library and save it in the aforementioned directory every bit before.
- Now we've got
three.js
attached to our page, we tin can start to write JavaScript that makes use of it intoscript.js
. Let'due south beginning by creating a new scene — add the following into your chief.js file:const scene = new THREE.Scene ( ) ;
Scene()
constructor creates a new scene, which represents the whole 3D world we are trying to display. - Next, we need a photographic camera and then we can see the scene. In 3D imagery terms, the camera represents a viewer's position in the world. To create a photographic camera, add the following lines adjacent:
const camera = new Iii.PerspectiveCamera ( 75 , window.innerWidth / window.innerHeight, 0.i , 1000 ) ; photographic camera.position.z = 5 ;
PerspectiveCamera()
constructor takes iv arguments:- The field of view: How wide the area in front of the camera is that should be visible onscreen, in degrees.
- The aspect ratio: Usually, this is the ratio of the scene'south width divided past the scene's peak. Using another value volition distort the scene (which might be what you lot want, but usually isn't).
- The most plane: How shut to the camera objects can be before nosotros terminate rendering them to the screen. Think about how when y'all motility your fingertip closer and closer to the infinite between your optics, eventually you can't encounter it anymore.
- The far plane: How far away things are from the camera before they are no longer rendered.
- The 3rd vital ingredient is a renderer. This is an object that renders a given scene, as viewed through a given camera. We'll create one for now using the
WebGLRenderer()
constructor, but nosotros'll non use information technology till later. Add the following lines next:const renderer = new 3.WebGLRenderer ( ) ; renderer. setSize (window.innerWidth, window.innerHeight) ; document.body. appendChild (renderer.domElement) ;
<canvas>
element created by the renderer to the document's<body>
. Now anything the renderer draws will exist displayed in our window. - Next, nosotros want to create the cube we'll display on the sheet. Add the post-obit chunk of code at the bottom of your JavaScript:
let cube; const loader = new THREE.TextureLoader ( ) ; loader. load ( 'metal003.png' , texture => { texture.wrapS = Iii .RepeatWrapping; texture.wrapT = THREE .RepeatWrapping; texture.repeat. prepare ( 2 , 2 ) ; const geometry = new THREE.BoxGeometry ( 2.iv , 2.4 , 2.4 ) ; const material = new Iii.MeshLambertMaterial ( { map : texture } ) ; cube = new THREE.Mesh (geometry, material) ; scene. add (cube) ; describe ( ) ; } ) ;
- We first create a
cube
global variable so nosotros tin can admission our cube from anywhere in the code. - Next, we create a new
TextureLoader
object, then callload()
on it.load()
takes ii parameters in this example (although it tin have more): the texture nosotros desire to load (our PNG), and a function that volition run when the texture has loaded. - Within this function we use properties of the
texture
object to specify that we desire a 2 x ii echo of the prototype wrapped around all sides of the cube. Adjacent, we create a newBoxGeometry
object and a newMeshLambertMaterial
object, and bring them together in aMesh
to create our cube. An object typically requires a geometry (what shape it is) and a material (what its surface looks like). - Last of all, we add together our cube to the scene, then call our
draw()
function to start off the animation.
- We first create a
- Before we go to defining
describe()
, we'll add a couple of lights to the scene, to liven things up a flake; add the following blocks next:const light = new 3.AmbientLight ( 'rgb(255,255,255)' ) ; // soft white light scene. add together (light) ; const spotLight = new 3.SpotLight ( 'rgb(255,255,255)' ) ; spotLight.position. prepare ( 100 , 1000 , 1000 ) ; spotLight.castShadow = truthful ; scene. add together (spotLight) ;
AmbientLight
object is a kind of soft calorie-free that lightens the whole scene a bit, like the sun when you are exterior. TheSpotLight
object, on the other manus, is a directional axle of light, more like a flashlight/torch (or a spotlight, in fact). - Last of all, permit'due south add our
draw()
office to the lesser of the code:function depict ( ) { cube.rotation.x += 0.01 ; cube.rotation.y += 0.01 ; renderer. return (scene, photographic camera) ; requestAnimationFrame (depict) ; }
requestAnimationFrame()
to schedule cartoon our next frame.
Permit's accept some other quick look at what the finished product should look similar:
Yous can find the finished code on GitHub.
Note: In our GitHub repo y'all can likewise find another interesting 3D cube example — 3.js Video Cube (see it alive also). This uses getUserMedia()
to take a video stream from a computer web cam and project it onto the side of the cube as a texture!
Summary
At this betoken, you should have a useful idea of the basics of graphics programming using Canvas and WebGL and what y'all can practise with these APIs, as well as a good thought of where to go for further information. Have fun!
See also
Here we accept covered but the existent basics of canvas — there is and then much more than to learn! The below articles will take you further.
- Canvass tutorial — A very detailed tutorial series explaining what you should know about second canvas in much more detail than was covered here. Essential reading.
- WebGL tutorial — A series that teaches the nuts of raw WebGL programming.
- Edifice upward a bones demo with Three.js — bones Iii.js tutorial. We also have equivalent guides for PlayCanvas or Babylon.js.
- Game development — the landing page for web games development on MDN. There are some really useful tutorials and techniques available here related to 2D and 3D canvas — run across the Techniques and Tutorials menu options.
Examples
In this module
- Introduction to web APIs
- Manipulating documents
- Fetching data from the server
- Tertiary party APIs
- Drawing graphics
- Video and audio APIs
- Customer-side storage
Source: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics
0 Response to "Draw Circles on a Canvas With a Button Click"
Post a Comment