Webgl Three Js Draw Text Box on Screen

Drawing graphics

Graphics on the Spider web

As nosotros talked about in our HTML Multimedia and embedding module, the Web was originally merely text, which was very tiresome, and so images were introduced — get-go via the <img> chemical element and subsequently via CSS properties such equally background-image, and SVG.

This however was however not enough. While you could utilize CSS and JavaScript to animate (and otherwise manipulate) SVG vector images — every bit they are represented by markup — in that location was still no way to do the aforementioned for bitmap images, and the tools bachelor were rather express. The Web still had no way 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 amend when browsers began to support the <canvas> element and associated Canvas API — Apple invented it in around 2004, and other browsers followed past implementing it in the years that followed. As you lot'll encounter below, sail provides many useful tools for creating 2D animations, games, data visualizations, and other types of app, especially when combined with some of the other APIs the web platform provides.

The beneath case shows a simple 2nd canvas-based bouncing balls blitheness that nosotros originally met in our Introducing JavaScript objects module:

Around 2006–2007, Mozilla started work on an experimental 3D sheet implementation. This became WebGL, which gained traction amid browser vendors, and was standardized around 2009–2010. WebGL allows yous to create real 3D graphics inside your web browser; the below example shows a simple rotating WebGL cube:

This article volition focus mainly on 2D sail, as raw WebGL lawmaking is very complex. We volition however testify how to use a WebGL library to create a 3D scene more hands, and you can find a tutorial covering raw WebGL elsewhere — see Getting started with WebGL.

Notation: Basic canvas functionality is supported well beyond browsers, with the exception of IE 8 and below for 2d canvass, and IE xi and below for WebGL.

Active learning: Getting started with a <canvas>

If you lot want to create a 2D or 3D scene on a web page, you demand to offset with an HTML <canvas> element. This element is used to define the area on the folio into which the image volition be drawn. This is as simple equally including the chemical element on the folio:

                                                                            <canvas                    width                                          =                      "320"                                        height                                          =                      "240"                                        >                                                                              </sail                    >                                                

This volition create a sail on the page with a size of 320 by 240 pixels.

Inside the canvas tags, you can put some fallback content, which is shown if the user's browser doesn't back up canvass.

                                                                            <canvas                    width                                          =                      "320"                                        superlative                                          =                      "240"                                        >                                                                              <p                    >                  Your browser doesn't support canvas. Boo hoo!                                          </p                    >                                                                              </canvas                    >                                                

Of course, the in a higher place message is really unhelpful! In a real example you lot'd desire to relate the fallback content to the canvas content. For example, if you were rendering a constantly updating graph of stock prices, the fallback content could be a static image of the latest stock graph, with alt text proverb what the prices are in text.

Creating and sizing our canvas

Let'south start by creating our own sheet that we draw future experiments on to.

  1. Starting time brand a local copy of the 0_canvas_start directory. It contains 3 files:
    • "index.html"
    • "script.js"
    • "style.css"
  2. Open "alphabetize.html", and add together the post-obit code into it, simply beneath the opening <trunk> tag:
                                                                                                <canvas                        class                                                  =                          "myCanvas"                                                >                                                                                              <p                        >                      Add suitable fallback here.                                                  </p                        >                                                                                              </canvass                        >                                                            
    Nosotros take added a grade to the <canvas> element so it volition be easier to select if we take multiple canvases on the page, but we accept removed the width and acme attributes for now (you lot could add together them back in if yous wanted, but we will prepare them using JavaScript in a below section). Canvases with no explicit width and meridian default to 300 pixels wide by 150 pixels high.
  3. Now open up "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;                                      
    Here we have stored a reference to the canvas in the canvas constant. In the 2d line we set both a new constant width and the canvas' width property equal to Window.innerWidth (which gives us the viewport width). In the 3rd line we set both a new abiding summit and the canvas' height property equal to Window.innerHeight (which gives united states of america the viewport height). And then at present nosotros have a canvas that fills the entire width and height of the browser window! You lot'll also see that we are chaining assignments together with multiple equals signs — this is allowed in JavaScript, and it is a expert technique if you want to make multiple variables all equal to the same value. We wanted to make the canvas width and height easily accessible in the width/height variables, as they are useful values to have available for later (for example, if you want to draw something exactly halfway beyond the width of the canvas).

Note: Y'all should generally set the size of the epitome using HTML attributes or DOM properties, as explained higher up. You lot could utilise CSS, but the trouble then is that the sizing is done after the canvas has rendered, and only like whatsoever other image (the rendered canvass is simply an prototype), the image could become pixelated/distorted.

Getting the canvas context and concluding setup

We need to do one final thing before we tin consider our canvas template finished. To draw onto the sail we need to go a special reference to the drawing area called a context. This is washed using the HTMLCanvasElement.getContext() method, which for basic usage takes a single string as a parameter representing the type of context you lot want to retrieve.

In this example we want a 2nd canvas, and then add together the following JavaScript line below the others in "script.js":

                                  const                  ctx                  =                  sheet.                  getContext                  (                  '2nd'                  )                  ;                              

Note: other context values you could cull include webgl for WebGL, webgl2 for WebGL 2, etc., only we won't need those in this commodity.

Then that'south it — our canvas is at present primed and ready for cartoon on! The ctx variable at present contains a CanvasRenderingContext2D object, and all drawing operations on the canvas will involve manipulating this object.

Let's exercise one concluding thing before nosotros movement on. Nosotros'll colour the sail background black to give you a first taste of the canvas API. Add together the following lines at the bottom of your JavaScript:

                ctx.fillStyle                  =                  'rgb(0, 0, 0)'                  ;                  ctx.                  fillRect                  (                  0                  ,                  0                  ,                  width,                  height)                  ;                              

Here nosotros are setting a make full color using the sail' fillStyle property (this takes color values just like CSS properties do), then cartoon a rectangle that covers the unabridged area of the canvas with thefillRect method (the outset two parameters are the coordinates of the rectangle's top left mitt corner; the concluding ii are the width and height you lot want the rectangle fatigued at — we told y'all those width and peak variables would be useful)!

OK, our template is done and it'south time to move on.

2nd canvas nuts

As nosotros said above, all drawing operations are washed by manipulating a CanvasRenderingContext2D object (in our instance, ctx). Many operations demand to be given coordinates to pinpoint exactly where to draw something — the top left of the canvass is point (0, 0), the horizontal (x) centrality runs from left to right, and the vertical (y) axis runs from tiptop to bottom.

Drawing shapes tends to be done using the rectangle shape primitive, or by tracing a line along a sure path and so filling in the shape. Below we'll show how to practise both.

Uncomplicated rectangles

Permit's start with some unproblematic rectangles.

  1. First of all, take a copy of your newly coded sheet template (or brand a local copy of the 1_canvas_template directory if you didn't follow the above steps).
  2. Next, add together the following lines to the bottom of your JavaScript:
                        ctx.fillStyle                      =                      'rgb(255, 0, 0)'                      ;                      ctx.                      fillRect                      (                      50                      ,                      50                      ,                      100                      ,                      150                      )                      ;                                      
    If you save and refresh, you lot should see a red rectangle has appeared on your canvas. Its top left corner is 50 pixels away from the top and left of the canvas edge (as defined past the kickoff two parameters), and it is 100 pixels wide and 150 pixels tall (as divers by the third and fourth parameters).
  3. Let's add another rectangle into the mix — a light-green one this fourth dimension. Add the following at the bottom of your JavaScript:
                        ctx.fillStyle                      =                      'rgb(0, 255, 0)'                      ;                      ctx.                      fillRect                      (                      75                      ,                      75                      ,                      100                      ,                      100                      )                      ;                                      
    Save and refresh, and you'll see your new rectangle. This raises an important point: graphics operations like cartoon rectangles, lines, and then along are performed in the order in which they occur. Call up of it like painting a wall, where each coat of paint overlaps and may even hibernate what'south underneath. You can't do anything to change this, so you accept to recollect carefully near the guild in which you draw the graphics.
  4. Note that yous can depict semi-transparent graphics by specifying a semi-transparent color, for example by using rgba(). The a value defines what'southward called the "alpha channel, " or the corporeality of transparency the color has. The college its value, the more information technology will obscure whatsoever's behind it. Add the following to your lawmaking:
                        ctx.fillStyle                      =                      'rgba(255, 0, 255, 0.75)'                      ;                      ctx.                      fillRect                      (                      25                      ,                      100                      ,                      175                      ,                      50                      )                      ;                                      
  5. Now try drawing some more than rectangles of your ain; have fun!

Strokes and line widths

And so far nosotros've looked at drawing filled rectangles, but you lot can also draw rectangles that are just outlines (chosen strokes in graphic blueprint). To set the color you want for your stroke, you employ the strokeStyle holding; drawing a stroke rectangle is done using strokeRect.

  1. Add together the following to the previous example, again below the previous JavaScript lines:
                        ctx.strokeStyle                      =                      'rgb(255, 255, 255)'                      ;                      ctx.                      strokeRect                      (                      25                      ,                      25                      ,                      175                      ,                      200                      )                      ;                                      
  2. The default width of strokes is 1 pixel; you can adjust the lineWidth holding value to modify this (information technology takes a number representing the number of pixels wide the stroke is). Add the post-obit line in between the previous two lines:

Now you should see that your white outline has get much thicker! That'south it for now. At this point your example should wait similar this:

Drawing paths

If y'all 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 movement along on your sheet to trace the shape yous want to describe. Sheet includes functions for cartoon straight lines, circles, Bézier curves, and more.

Permit's start the section off by making a fresh re-create of our canvas template (1_canvas_template), in which to draw the new example.

We'll be using some mutual methods and properties beyond all of the beneath sections:

  • beginPath() — start cartoon a path at the point where the pen currently is on the canvass. On a new canvas, the pen starts out at (0, 0).
  • moveTo() — motility the pen to a different signal on the canvas, without recording or tracing the line; the pen "jumps" to the new position.
  • fill() — draw 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 too use features like lineWidth and fillStyle/strokeStyle with paths too as rectangles.

A typical, unproblematic path-drawing operation would look something like so:

                ctx.fillStyle                  =                  'rgb(255, 0, 0)'                  ;                  ctx.                  beginPath                  (                  )                  ;                  ctx.                  moveTo                  (                  50                  ,                  50                  )                  ;                  // draw your path                  ctx.                  fill                  (                  )                  ;                              

Drawing lines

Allow's draw an equilateral triangle on the canvas.

  1. Outset of all, add together the post-obit helper function to the bottom of your code. This converts caste values to radians, which is useful because whenever y'all need to provide an angle value in JavaScript, information technology will nearly always be in radians, but humans usually think in degrees.
                                              role                      degToRad                      (                      degrees                      )                      {                      render                      degrees                      *                      Math.                      PI                      /                      180                      ;                      }                                      
  2. Next, beginning off your path by adding the following below your previous improver; hither nosotros prepare a color for our triangle, beginning drawing a path, and so move the pen to (50, 50) without drawing annihilation. That'due south where we'll start drawing our triangle.
                        ctx.fillStyle                      =                      'rgb(255, 0, 0)'                      ;                      ctx.                      beginPath                      (                      )                      ;                      ctx.                      moveTo                      (                      50                      ,                      50                      )                      ;                                      
  3. Now add together the following lines at the bottom of your script:
                        ctx.                      lineTo                      (                      150                      ,                      50                      )                      ;                      const                      triHeight                      =                      50                      *                      Math.                      tan                      (                      degToRad                      (                      60                      )                      )                      ;                      ctx.                      lineTo                      (                      100                      ,                      50                      +                      triHeight)                      ;                      ctx.                      lineTo                      (                      50                      ,                      fifty                      )                      ;                      ctx.                      fill                      (                      )                      ;                                      
    Let's run through this in club: Get-go we draw a line across to (150, 50) — our path at present goes 100 pixels to the right along the x axis. Second, we work out the height of our equilateral triangle, using a bit of simple trigonometry. Basically, we are drawing the triangle pointing downwardly. The angles in an equilateral triangle are always 60 degrees; to work out the tiptop nosotros can split information technology downwardly the eye into two correct-angled triangles, which will each have angles of 90 degrees, 60 degrees, and xxx degrees. In terms of the sides:
    • The longest side is called the hypotenuse
    • The side next to the threescore degree bending is called the next — which nosotros know is fifty pixels, equally it is one-half of the line we simply drew.
    • The side opposite the 60 degree bending is chosen the opposite, which is the summit of the triangle nosotros desire to calculate.
    I of the basic trigonometric formulae states that the length of the adjacent multiplied by the tangent of the angle is equal to the opposite, hence we come up with 50 * Math.tan(degToRad(60)). We utilize our degToRad() function to convert lx degrees to radians, every bit Math.tan() expects an input value in radians.
  4. With the top calculated, we draw another line to (100, fifty + triHeight). The Ten coordinate is unproblematic; information technology must be halfway between the previous two X values we set. The Y value on the other hand must exist fifty plus the triangle height, equally we know the top of the triangle is 50 pixels from the superlative of the sail.
  5. The next line draws a line back to the starting signal of the triangle.
  6. Terminal of all, nosotros run ctx.make full() to end the path and make full in the shape.

Drawing circles

Now let's look at how to depict a circle in canvas. This is accomplished using the arc() method, which draws all or part of a circle at a specified point.

  1. Permit's add an arc to our canvas — add the following to the lesser of your code:
                        ctx.fillStyle                      =                      'rgb(0, 0, 255)'                      ;                      ctx.                      beginPath                      (                      )                      ;                      ctx.                      arc                      (                      150                      ,                      106                      ,                      50                      ,                      degToRad                      (                      0                      )                      ,                      degToRad                      (                      360                      )                      ,                      imitation                      )                      ;                      ctx.                      fill up                      (                      )                      ;                                      
    arc() takes six parameters. The first ii specify the position of the arc'south center (Ten and Y, respectively). The tertiary is the circle's radius, the fourth and fifth are the start and end angles at which to draw the circle (and so specifying 0 and 360 degrees gives us a full circle), and the 6th parameter defines whether the circumvolve should be fatigued counterclockwise (anticlockwise) or clockwise (false is clockwise).

    Notation: 0 degrees is horizontally to the correct.

  2. Let's try adding another arc:
                        ctx.fillStyle                      =                      'yellow'                      ;                      ctx.                      beginPath                      (                      )                      ;                      ctx.                      arc                      (                      200                      ,                      106                      ,                      50                      ,                      degToRad                      (                      -                      45                      )                      ,                      degToRad                      (                      45                      )                      ,                      truthful                      )                      ;                      ctx.                      lineTo                      (                      200                      ,                      106                      )                      ;                      ctx.                      fill                      (                      )                      ;                                      
    The pattern here is very similar, simply with two differences:
    • We accept ready the last parameter of arc() to true, significant 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 effectually the 270 degrees not inside this portion. If you were to change true to false so re-run the code, only the 90 caste slice of the circle would be drawn.
    • Earlier calling make full(), nosotros draw a line to the middle of the circle. This ways that we become the rather nice Pac-Man-way cutout rendered. If you removed this line (try information technology!) so re-ran the code, you'd get just an border of the circle chopped off betwixt the start and end betoken of the arc. This illustrates some other important point of the canvas — if you try to fill an incomplete path (i.e. 1 that is not airtight), the browser fills in a straight line between the start and terminate point then fills it in.

That's it for now; your concluding example should wait like this:

Notation: The finished code is available on GitHub equally 3_canvas_paths.

Note: To observe out more virtually advanced path drawing features such as Bézier curves, cheque out our Drawing shapes with sail tutorial.

Text

Canvas besides has features for drawing text. Allow's explore these briefly. Start past making another fresh copy of our sheet template (1_canvas_template) in which to draw the new instance.

Text is drawn using ii methods:

  • fillText() — draws filled text.
  • strokeText() — draws outline (stroke) text.

Both of these have 3 properties in their bones usage: the text string to draw and the X and Y coordinates of the point to start cartoon the text at. This works out as the bottom left corner of the text box (literally, the box surrounding the text you draw), which might confuse you as other drawing operations tend to start from the acme left corner — bear this in mind.

There are also a number of properties to assist command text rendering such as font, which lets y'all specify font family, size, etc. It takes as its value the aforementioned syntax as the CSS font property.

Try adding the post-obit block to the bottom of your JavaScript:

                ctx.strokeStyle                  =                  'white'                  ;                  ctx.lineWidth                  =                  1                  ;                  ctx.font                  =                  '36px arial'                  ;                  ctx.                  strokeText                  (                  'Canvas text'                  ,                  50                  ,                  fifty                  )                  ;                  ctx.fillStyle                  =                  'ruddy'                  ;                  ctx.font                  =                  '48px georgia'                  ;                  ctx.                  fillText                  (                  'Canvas text'                  ,                  fifty                  ,                  150                  )                  ;                              

Here we draw 2 lines of text, i outline and the other stroke. The final instance should look like so:

Note: The finished code is available on GitHub as 4_canvas_text.

Have a play and see what you can come upwards with! Y'all can find more than data on the options available for canvas text at Drawing text.

Drawing images onto canvas

It is possible to render external images onto your canvass. These can be unproblematic images, frames from videos, or the content of other canvases. For the moment we'll simply wait at the case of using some unproblematic images on our canvas.

  1. As earlier, brand another fresh copy of our canvas template (1_canvas_template) in which to draw the new example. Images are drawn onto sail using the drawImage() method. The simplest version takes three parameters — a reference to the epitome you want to render, and the X and Y coordinates of the image's elevation left corner.
  2. Allow'southward get-go by getting an image source to embed in our sheet. Add the following lines to the bottom of your JavaScript:
                                              const                      image                      =                      new                      Image                      (                      )                      ;                      epitome.src                      =                      'firefox.png'                      ;                                      
    Here we create a new HTMLImageElement object using the Image() 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 its src attribute to equal our Firefox logo paradigm. At this betoken, the browser starts loading the image.
  3. Nosotros could now attempt to embed the paradigm using drawImage(), but nosotros demand to make sure the image file has been loaded start, otherwise the code will fail. We can attain this using the load event, which volition only be fired when the image has finished loading. Add the post-obit block below the previous ane:
                        image.                      addEventListener                      (                      'load'                      ,                      (                      )                      =>                      ctx.                      drawImage                      (prototype,                      20                      ,                      20                      ,                      185                      ,                      175                      ,                      50                      ,                      50                      ,                      185                      ,                      175                      )                      )                      ;                                      
    If you load your instance in the browser now, you should meet the image embedded in the canvas.
  4. Just at that place'southward more! What if nosotros want to display only a part of the prototype, or to resize it? We can do both with the more complex version of drawImage(). Update your ctx.drawImage() line like so:
                        ctx.                      drawImage                      (epitome,                      20                      ,                      20                      ,                      185                      ,                      175                      ,                      50                      ,                      50                      ,                      185                      ,                      175                      )                      ;                                      
    • The first parameter is the paradigm reference, as before.
    • Parameters 2 and 3 ascertain the coordinates of the peak left corner of the area you want to cutting out of the loaded image, relative to the peak-left corner of the prototype itself. Nil to the left of the showtime parameter or to a higher place the 2d will be fatigued.
    • Parameters 4 and 5 ascertain the width and height of the area we want to cut out from the original image nosotros loaded.
    • Parameters half-dozen and 7 define the coordinates at which you want to draw the top-left corner of the cutting-out portion of the image, relative to the meridian-left corner of the canvas.
    • Parameters 8 and nine define the width and acme to draw the cut-out area of the paradigm. In this instance, we accept specified the aforementioned dimensions as the original slice, just you lot could resize it past specifying unlike values.

The last example should expect like and so:

Note: The finished code is available on GitHub as 5_canvas_images.

Loops and animations

We have so far covered some very basic uses of 2d canvas, but really you won't feel the full ability of sail unless you update or animate it in some way. After all, sheet does provide scriptable images! If you aren't going to modify anything, and so you might too merely utilize static images and save yourself all the work.

Creating a loop

Playing with loops in canvas is rather fun — yous can run canvas commands within a for (or other type of) loop merely like any other JavaScript code.

Permit's build a simple example.

  1. Brand another fresh re-create of our sail template (1_canvas_template) and open it in your code editor.
  2. Add the following line to the bottom of your JavaScript. This contains a new method, translate(), which moves the origin point of the canvass:
                        ctx.                      translate                      (width/                      2                      ,                      height/                      2                      )                      ;                                      
    This causes the coordinate origin (0, 0) to be moved to the middle of the sail, rather than being at the pinnacle left corner. This is very useful in many situations, like this one, where we want our design to be drawn relative to the heart of the canvas.
  3. Now add the following lawmaking to the lesser of the JavaScript:
                                              function                      degToRad                      (                      degrees                      )                      {                      return                      degrees                      *                      Math.                      PI                      /                      180                      ;                      }                      role                      rand                      (                      min,                        max                      )                      {                      render                      Math.                      flooring                      (Math.                      random                      (                      )                      *                      (max-min+                      one                      )                      )                      +                      (min)                      ;                      }                      allow                      length                      =                      250                      ;                      let                      moveOffset                      =                      20                      ;                      for                      (                      let                      i                      =                      0                      ;                      i                      <                      length;                      i++                      )                      {                      }                                      
    Here we are implementing the aforementioned degToRad() office we saw in the triangle example above, a rand() part that returns a random number between given lower and upper bounds, length and moveOffset variables (which we'll find out more than about afterward), and an empty for loop.
  4. The idea here is that we'll draw something on the canvas within the for loop, and iterate on information technology each time so we can create something interesting. Add the post-obit code within your for loop:
                        ctx.fillStyle                      =                                              `                        rgba(                                                  ${                          255                          -length}                                                ,0,                                                  ${                          255                          -length}                                                ,0.nine)                        `                                            ;                      ctx.                      beginPath                      (                      )                      ;                      ctx.                      moveTo                      (moveOffset,moveOffset)                      ;                      ctx.                      lineTo                      (moveOffset+length,moveOffset)                      ;                      const                      triHeight                      =                      length/                      two                      *                      Math.                      tan                      (                      degToRad                      (                      lx                      )                      )                      ;                      ctx.                      lineTo                      (moveOffset+                      (length/                      2                      )                      ,moveOffset+triHeight)                      ;                      ctx.                      lineTo                      (moveOffset,moveOffset)                      ;                      ctx.                      fill                      (                      )                      ;                      length--                      ;                      moveOffset                      +=                      0.7                      ;                      ctx.                      rotate                      (                      degToRad                      (                      5                      )                      )                      ;                                      
    And so on each iteration, we:
    • Set the fillStyle to be a shade of slightly transparent purple, which changes each time based on the value of length. As you'll see later the length gets smaller each fourth dimension the loop runs, so the effect hither is that the color gets brighter with each successive triangle drawn.
    • Brainstorm the path.
    • Motility the pen to a coordinate of (moveOffset, moveOffset); This variable defines how far we want to move each fourth dimension we draw a new triangle.
    • Describe a line to a coordinate of (moveOffset+length, moveOffset). This draws a line of length length parallel to the 10 axis.
    • Summate the triangle's acme, equally before.
    • Draw a line to the downward-pointing corner of the triangle, and then describe a line back to the outset of the triangle.
    • Telephone call fill() to make full in the triangle.
    • Update the variables that describe the sequence of triangles, so we can be ready to describe the adjacent 1. We decrease the length value past one, so the triangles get smaller each time; increment moveOffset by a small corporeality then each successive triangle is slightly further away, and use another new function, rotate(), which allows united states of america to rotate the entire canvas! We rotate information technology past 5 degrees before cartoon the next triangle.

That'due south it! The final case should expect like so:

At this indicate, we'd similar to encourage you lot to play with the example and make information technology your own! For example:

  • Depict rectangles or arcs instead of triangles, or even embed images.
  • Play with the length and moveOffset values.
  • Introduce some random numbers using that rand() office we included above but didn't use.

Animations

The loop example nosotros built in a higher place was fun, merely really yous need a constant loop that keeps going and going for any serious canvas applications (such as games and real time visualizations). If you lot call back of your canvas every bit being like a picture show, you really desire the display to update on each frame to evidence the updated view, with an ideal refresh rate of 60 frames per 2nd then that motility appears nice and smooth to the human centre.

There are a few JavaScript functions that volition permit you to run functions repeatedly, several times a 2nd, the best ane for our purposes here existence window.requestAnimationFrame(). It takes one parameter — the proper noun of the part you desire to run for each frame. The next time the browser is set up to update the screen, your function volition get chosen. If that function draws the new update to your animation, then calls requestAnimationFrame() once again just before the stop of the function, the animation loop will keep to run. The loop ends when you stop calling requestAnimationFrame() or if you call window.cancelAnimationFrame() after calling requestAnimationFrame() only earlier the frame is called.

Note: It'south good exercise to call cancelAnimationFrame() from your master code when you're done using the animation, to ensure that no updates are still waiting to exist run.

The browser works out complex details such as making the animation run at a consistent speed, and not wasting resources animating things that can't exist seen.

To see how it works, let'due south apace look over again at our Bouncing Balls case (see it live, and also run across the source code). The code for the loop that keeps everything moving looks like this:

                                  function                  loop                  (                  )                  {                  ctx.fillStyle                  =                  'rgba(0, 0, 0, 0.25)'                  ;                  ctx.                  fillRect                  (                  0                  ,                  0                  ,                  width,                  height)                  ;                  for                  (                  const                  ball                  of                  balls)                  {                  ball.                  draw                  (                  )                  ;                  ball.                  update                  (                  )                  ;                  ball.                  collisionDetect                  (                  )                  ;                  }                  requestAnimationFrame                  (loop)                  ;                  }                  loop                  (                  )                  ;                              

We run the loop() office in one case at the lesser of the code to start the bicycle, cartoon the get-go animation frame; the loop() office and so takes accuse of calling requestAnimationFrame(loop) to run the adjacent frame of the animation, once more and over again.

Annotation that on each frame we are completely clearing the sail and redrawing everything. For every brawl present we draw it, update its position, and check to see if it is colliding with any other balls. Once you lot've drawn a graphic to a canvas, there's no way to manipulate that graphic individually like y'all tin can with DOM elements. Yous tin can't motility each ball around on the canvas, because one time information technology's drawn, it'southward part of the canvas, and is not an individual attainable chemical element or object. Instead, you lot have 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 canvass necessary.

Optimizing animation of graphics is an entire specialty of programming, with lots of clever techniques available. Those are beyond what we demand for our case, though!

In general, the procedure of doing a sheet blitheness involves the following steps:

  1. Articulate the canvass contents (e.thousand. with fillRect() or clearRect()).
  2. Save state (if necessary) using save() — this is needed when you want to save settings you lot've updated on the canvas before standing, which is useful for more advanced applications.
  3. Describe the graphics you are animating.
  4. Restore the settings yous saved in step 2, using restore()
  5. Call requestAnimationFrame() to schedule drawing of the next frame of the animation.

Notation: We won't cover save() and restore() here, simply they are explained nicely in our Transformations tutorial (and the ones that follow it).

A uncomplicated character animation

Now allow'due south create our own simple animation — nosotros'll go a grapheme from a certain rather crawly retro reckoner game to walk across the screen.

  1. Brand another fresh copy of our canvas template (1_canvas_template) and open information technology in your code editor.
  2. At the bottom of the JavaScript, add the following line to in one case once again brand the coordinate origin sit in the heart of the canvas:
                        ctx.                      translate                      (width/                      two                      ,                      height/                      2                      )                      ;                                      
  3. At present let'southward create a new HTMLImageElement object, set its src to the image nosotros desire to load, and add an onload event handler that will cause the describe() role to burn when the image is loaded:
                                              const                      image                      =                      new                      Image                      (                      )                      ;                      image.src                      =                      'walk-right.png'                      ;                      image.onload                      =                      depict;                                      
  4. At present nosotros'll add some variables to proceed track of the position the sprite is to be fatigued on the screen, and the sprite number we want to display.
                                              let                      sprite                      =                      0                      ;                      let                      posX                      =                      0                      ;                                      
    Let's explain the spritesheet prototype (which nosotros have respectfully borrowed from Mike Thomas' Create a sprite sheet walk wheel using CSS blitheness). The image looks like this: Information technology contains vi sprites that make up the whole walking sequence — each ane is 102 pixels broad and 148 pixels high. To display each sprite cleanly we will have to employ drawImage() to chop out a single sprite image from the spritesheet and display just that role, like nosotros did above with the Firefox logo. The 10 coordinate of the slice will have to be a multiple of 102, and the Y coordinate will always exist 0. The piece size will e'er be 102 by 148 pixels.
  5. Now let's insert an empty draw() part at the bottom of the code, ready for filling up with some code:
  6. The remainder of the code in this section goes inside describe(). First, add the following line, which clears the sheet to set for drawing each frame. Detect that nosotros take to specify the acme-left corner of the rectangle equally -(width/2), -(height/2) because we specified the origin position as width/2, summit/two earlier on.
                        ctx.                      fillRect                      (                      -                      (width/                      ii                      )                      ,                      -                      (superlative/                      2                      )                      ,                      width,                      height)                      ;                                      
  7. Next, we'll draw our image using drawImage — the 9-parameter version. Add together the following:
                        ctx.                      drawImage                      (image,                      (sprite*                      102                      )                      ,                      0                      ,                      102                      ,                      148                      ,                      0                      +posX,                      -                      74                      ,                      102                      ,                      148                      )                      ;                                      
    Equally you can see:
    • We specify image every bit the image to embed.
    • Parameters 2 and 3 specify the acme-left corner of the slice to cut out of the source paradigm, with the Ten value every bit sprite multiplied by 102 (where sprite is the sprite number between 0 and 5) and the Y value always 0.
    • Parameters iv and 5 specify the size of the slice to cutting out — 102 pixels past 148 pixels.
    • Parameters 6 and 7 specify the acme-left corner of the box into which to draw the slice on the sheet — the X position is 0 + posX, pregnant that we tin can alter the drawing position by altering the posX value.
    • Parameters viii and ix specify the size of the image on the canvas. We only desire to keep its original size, so we specify 102 and 148 every bit the width and height.
  8. Now we'll alter the sprite value later on each describe — well, after some of them anyhow. Add the following block to the bottom of the draw() function:
                                              if                      (posX                      %                      13                      ===                      0                      )                      {                      if                      (sprite                      ===                      five                      )                      {                      sprite                      =                      0                      ;                      }                      else                      {                      sprite++                      ;                      }                      }                                      
    We are wrapping the whole cake in if (posX % 13 === 0) { ... }. Nosotros apply the modulo (%) operator (also known every bit the remainder operator) to check whether the posX value can exist exactly divided by 13 with no residue. If then, we move on to the next sprite past incrementing sprite (wrapping to 0 after we're done with sprite #v). This effectively means that we are simply updating the sprite on every 13th frame, or roughly virtually 5 frames a 2d (requestAnimationFrame() calls us at upwards to 60 frames per second if possible). We are deliberately slowing downwardly the frame charge per unit because we only accept vi sprites to work with, and if we display one every 60th of a second, our character volition move manner likewise fast! Inside the outer block we utilize an if ... else statement to cheque whether the sprite value is at 5 (the last sprite, given that the sprite numbers run from 0 to five). If we are showing the concluding sprite already, we reset sprite back to 0; if not we just increment information technology past 1.
  9. Next we demand to work out how to change the posX value on each frame — add the post-obit lawmaking block but below your final i.
                                              if                      (posX                      >                      width/                      2                      )                      {                      let                      newStartPos                      =                      -                      (                      (width/                      two                      )                      +                      102                      )                      ;                      posX                      =                      Math.                      ceil                      (newStartPos)                      ;                      console.                      log                      (posX)                      ;                      }                      else                      {                      posX                      +=                      two                      ;                      }                                      
    We are using another if ... else statement to see if the value of posX has become greater than width/ii, which means our character has walked off the correct edge of the screen. If so, we calculate a position that would put the graphic symbol just to the left of the left side of the screen. If our grapheme hasn't yet walked off the border of the screen, nosotros increase posX by ii. This will make him move a picayune bit to the right the next time we describe him.
  10. Finally, nosotros demand to make the animation loop past calling requestAnimationFrame() at the bottom of the depict() function:
                        window.                      requestAnimationFrame                      (draw)                      ;                                      

That'due south it! The final example should look like and then:

A simple cartoon application

As a concluding animation case, we'd like to bear witness you a very uncomplicated drawing application, to illustrate how the animation loop can be combined with user input (like mouse movement, in this case). We won't get you lot to walk through and build this 1; we'll just explore the most interesting parts of the code.

The example can be institute on GitHub every bit 8_canvas_drawing_app, and yous can play with it live below:

Let's await at the about interesting parts. Kickoff of all, we keep rail of the mouse'south X and Y coordinates and whether it is beingness clicked or not with iii variables: curX, curY, and pressed. When the mouse moves, nosotros burn a part set as the onmousemove event handler, which captures the electric current 10 and Y values. We also use onmousedown and onmouseup effect handlers to modify the value of pressed to true when the mouse button is pressed, and back to false again when it is released.

                                  let                  curX;                  let                  curY;                  allow                  pressed                  =                  false                  ;                  // update mouse pointer coordinates                  document.                  addEventListener                  (                  'mousemove'                  ,                  east                  =>                  {                  curX                  =                  (window.Event)                  ?                  e.pageX                  :                  e.clientX                  +                  (document.documentElement.scrollLeft                  ?                  certificate.documentElement.scrollLeft                  :                  document.torso.scrollLeft)                  ;                  curY                  =                  (window.Event)                  ?                  e.pageY                  :                  e.clientY                  +                  (document.documentElement.scrollTop                  ?                  document.documentElement.scrollTop                  :                  certificate.body.scrollTop)                  ;                  }                  )                  ;                  sheet.                  addEventListener                  (                  'mousedown'                  ,                  (                  )                  =>                  pressed                  =                  true                  )                  ;                  sail.                  addEventListener                  (                  'mouseup'                  ,                  (                  )                  =>                  pressed                  =                  false                  )                  ;                              

When the "Clear canvass" button is pressed, we run a elementary function that clears the whole canvas back to black, the same mode nosotros've seen before:

                clearBtn.                  addEventListener                  (                  'click'                  ,                  (                  )                  =>                  {                  ctx.fillStyle                  =                  'rgb(0,0,0)'                  ;                  ctx.                  fillRect                  (                  0                  ,                  0                  ,width,height)                  ;                  }                  )                  ;                              

The drawing loop is pretty uncomplicated this time around — if pressed is true, we depict a circle with a fill style equal to the value in the color picker, and a radius equal to the value set in the range input. We have to depict the circle 85 pixels above where we measured information technology from, because the vertical measurement is taken from the height of the viewport, but we are drawing the circle relative to the superlative of the canvass, which starts below the 85 pixel-high toolbar. If we drew information technology with simply curY as 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                  )                  ,                  false                  )                  ;                  ctx.                  make full                  (                  )                  ;                  }                  requestAnimationFrame                  (describe)                  ;                  }                  draw                  (                  )                  ;                              

Note: The <input> range and color types are supported fairly well across browsers, with the exception of Internet Explorer versions less than ten; too Safari doesn't yet support color. If your browser doesn't support these inputs, they will fall back to unproblematic text fields and you'll merely have to enter valid color/number values yourself.

WebGL

It'due south now time to get out 2D behind, and take a quick look at 3D sail. 3D canvas content is specified using the WebGL API, which is a completely separate API from the 2d sail API, even though they both render onto <sheet> elements.

WebGL is based on OpenGL (Open Graphics Library), and allows you to communicate directly with the reckoner'southward GPU. Every bit such, writing raw WebGL is closer to low level languages such as C++ than regular JavaScript; it is quite circuitous just incredibly powerful.

Using a library

Because of its complication, about people write 3D graphics code using a third political party JavaScript library such as Three.js, PlayCanvas, or Babylon.js. Near of these piece of work in a similar way, providing functionality to create archaic and custom shapes, position viewing cameras and lighting, covering surfaces with textures, and more than. They handle the WebGL for y'all, letting y'all work on a higher level.

Yes, using one of these means learning some other new API (a 3rd party one, in this example), only they are a lot simpler than coding raw WebGL.

Recreating our cube

Let'south expect at a simple example of how to create something with a WebGL library. We'll choose Three.js, equally it is 1 of the almost popular ones. In this tutorial nosotros'll create the 3D spinning cube we saw earlier.

  1. To start with, make a local re-create of threejs-cube/index.html in a new binder, then salvage a copy of metal003.png in the same folder. This is the image we'll utilize as a surface texture for the cube afterwards.
  2. Next, create a new file called script.js, over again in the aforementioned folder as before.
  3. Next, you need to download the 3.min.js library and save information technology in the same directory every bit before.
  4. Now we've got three.js attached to our page, we tin start to write JavaScript that makes employ of information technology into script.js. Let's starting time by creating a new scene — add together the following into your chief.js file:
                                              const                      scene                      =                      new                      THREE.Scene                      (                      )                      ;                                      
    The Scene() constructor creates a new scene, which represents the whole 3D earth we are trying to display.
  5. Next, nosotros need a camera so we tin can run across the scene. In 3D imagery terms, the camera represents a viewer's position in the world. To create a photographic camera, add together the following lines adjacent:
                                              const                      camera                      =                      new                      Iii.PerspectiveCamera                      (                      75                      ,                      window.innerWidth                      /                      window.innerHeight,                      0.one                      ,                      thou                      )                      ;                      photographic camera.position.z                      =                      5                      ;                                      
    The PerspectiveCamera() constructor takes four arguments:
    • The field of view: How wide the expanse in front of the photographic camera is that should be visible onscreen, in degrees.
    • The attribute ratio: Usually, this is the ratio of the scene's width divided by the scene's height. Using another value will distort the scene (which might exist what you want, but usually isn't).
    • The most aeroplane: How close to the camera objects can be earlier we cease rendering them to the screen. Call up about how when you lot move your fingertip closer and closer to the space between your eyes, eventually you tin't run into it anymore.
    • The far plane: How far away things are from the camera before they are no longer rendered.
    Nosotros also ready the camera'southward position to be 5 distance units out of the Z axis, which, similar in CSS, is out of the screen towards you, the viewer.
  6. The third 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 we'll non utilize it till later. Add the following lines next:
                                              const                      renderer                      =                      new                      THREE.WebGLRenderer                      (                      )                      ;                      renderer.                      setSize                      (window.innerWidth,                      window.innerHeight)                      ;                      document.body.                      appendChild                      (renderer.domElement)                      ;                                      
    The kickoff line creates a new renderer, the second line sets the size at which the renderer will draw the camera'due south view, and the tertiary line appends the <canvass> element created by the renderer to the certificate's <trunk>. Now anything the renderer draws will be displayed in our window.
  7. Next, we want to create the cube we'll display on the sheet. Add the following clamper of lawmaking at the lesser of your JavaScript:
                                              let                      cube;                      const                      loader                      =                      new                      THREE.TextureLoader                      (                      )                      ;                      loader.                      load                      (                      'metal003.png'                      ,                      texture                      =>                      {                      texture.wrapS                      =                      THREE                      .RepeatWrapping;                      texture.wrapT                      =                      3                      .RepeatWrapping;                      texture.repeat.                      set                      (                      ii                      ,                      2                      )                      ;                      const                      geometry                      =                      new                      THREE.BoxGeometry                      (                      2.4                      ,                      2.4                      ,                      ii.4                      )                      ;                      const                      material                      =                      new                      Iii.MeshLambertMaterial                      (                      {                      map                      :                      texture                      }                      )                      ;                      cube                      =                      new                      Iii.Mesh                      (geometry,                      material)                      ;                      scene.                      add                      (cube)                      ;                      draw                      (                      )                      ;                      }                      )                      ;                                      
    In that location's a scrap more to have in here, so let's become through it in stages:
    • We first create a cube global variable so nosotros can access our cube from anywhere in the code.
    • Next, nosotros create a new TextureLoader object, then call load() on it. load() takes two parameters in this case (although it can accept more): the texture we want to load (our PNG), and a part that will run when the texture has loaded.
    • Inside this role we use backdrop of the texture object to specify that nosotros want a 2 x 2 repeat of the epitome wrapped effectually all sides of the cube. Adjacent, nosotros create a new BoxGeometry object and a new MeshLambertMaterial object, and bring them together in a Mesh to create our cube. An object typically requires a geometry (what shape it is) and a cloth (what its surface looks like).
    • Last of all, we add our cube to the scene, then call our draw() function to start off the animation.
  8. Before we become to defining describe(), we'll add together a couple of lights to the scene, to liven things upwards a chip; add together the following blocks next:
                                              const                      lite                      =                      new                      THREE.AmbientLight                      (                      'rgb(255,255,255)'                      )                      ;                      // soft white light                      scene.                      add together                      (light)                      ;                      const                      spotLight                      =                      new                      THREE.SpotLight                      (                      'rgb(255,255,255)'                      )                      ;                      spotLight.position.                      prepare                      (                      100                      ,                      thou                      ,                      one thousand                      )                      ;                      spotLight.castShadow                      =                      true                      ;                      scene.                      add together                      (spotLight)                      ;                                      
    An AmbientLight object is a kind of soft light that lightens the whole scene a bit, similar the sun when y'all are exterior. The SpotLight object, on the other mitt, is a directional beam of light, more than like a flashlight/torch (or a spotlight, in fact).
  9. Last of all, permit'south add our describe() office to the bottom of the code:
                                              function                      draw                      (                      )                      {                      cube.rotation.10                      +=                      0.01                      ;                      cube.rotation.y                      +=                      0.01                      ;                      renderer.                      render                      (scene,                      camera)                      ;                      requestAnimationFrame                      (draw)                      ;                      }                                      
    This is fairly intuitive; on each frame, we rotate our cube slightly on its X and Y axes, so return the scene as viewed by our camera, then finally call requestAnimationFrame() to schedule drawing our adjacent frame.

Let's accept some other quick expect at what the finished product should look like:

You can discover the finished code on GitHub.

Annotation: In our GitHub repo yous can as well find another interesting 3D cube instance — Three.js Video Cube (see information technology live also). This uses getUserMedia() to accept a video stream from a estimator spider web cam and project it onto the side of the cube as a texture!

Summary

At this signal, y'all should accept a useful idea of the basics of graphics programming using Sail and WebGL and what you can do with these APIs, as well as a good idea of where to go for farther information. Accept fun!

Meet too

Hither nosotros accept covered just the real basics of canvass — there is and then much more to learn! The beneath articles will have you farther.

  • Canvas tutorial — A very detailed tutorial series explaining what y'all should know about 2D canvas in much more detail than was covered here. Essential reading.
  • WebGL tutorial — A series that teaches the basics of raw WebGL programming.
  • Building upwardly a bones demo with Three.js — basic Three.js tutorial. Nosotros too take equivalent guides for PlayCanvas or Babylon.js.
  • Game development — the landing page for spider web games development on MDN. There are some really useful tutorials and techniques available here related to 2d and 3D canvas — see the Techniques and Tutorials menu options.

Examples

In this module

  • Introduction to spider web APIs
  • Manipulating documents
  • Fetching data from the server
  • Third party APIs
  • Drawing graphics
  • Video and audio APIs
  • Customer-side storage

slagleungazintonat.blogspot.com

Source: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics

0 Response to "Webgl Three Js Draw Text Box on Screen"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel