When do I draw? (some comments on code organization)

by Mike Gleicher on August 28, 2015

in 559 Graphics,JavaScript

Warning: this is from 2015. Some of it is obsolete – there are newer (and better) ways to do things as JavaScript has evolved.

One thing which is weird about JavaScript (in the web browser at least) is that your program doesn’t have a beginning. It is embedded on a web page, and attached to things. This brings up questions of when your code actually gets run. It is particularly important for drawing – since you want to draw at the right time.

This is a bit of web programming detail – it is more about how web browsers work than graphics or JavaScript. You need to worry about this so your web programs work.

Draw on Start

There’s a problem that when you write some JavaScript code on a web page, there’s an issue of when it actually runs. Consider the most trivial example.

JS Bin on jsbin.com

You would think that the script should run after the canvas gets created. And most of the time it does. But since web browsers are smart, they might not finish step 1 before step 2. So the canvas might not be set up before the script gets run. Or worse, you might not want to put the script in the middle of the body – you might want to load it at the top of the program (especially if you are loading it from another file).

Here’s something that plain does not work: just sticking the code into the top part of your html page (the block – which I call the header, although that might technically be a misnomer). It’s not unreasonable to want to put it there – especially if you are loading the script from a file, you might want to have the browser start fetching that file before it needs it. However, if the code runs at the beginning of the web page, it almost certainly will run before the canvas gets created, and will fail.

As with most things web, there are lots of different ways to deal with this “when do I run my script” issue. The one that I use is to have the code execute when the window is finished loading. To do this, I set the window’s “onload” function to be the code I want to execute.

JS Bin on jsbin.com

As you can see, this isn’t too much different than the first example. The big difference is that I’ve put my drawing code in the header of the HTML file. However, rather than running it, I instead define a function with the code inside and assign this to the window’s “onload” attribute. This way, they code gets run when the window is done loading, so that I know that all the HTML objects (like the canvas) have been created.

If you’re a web pro, you probably would have reasons to prefer an even fancier method. But this usually involves learning about some other library (jQuery’s “ready” function seems to be the preferred approach). For this class, onload functions are probably good enough.

I had the code execute with the window’s onload. Some people put it on the body’s onload. I am not sure if it makes a difference. It might make a difference if you put stuff after the body (but still on the web page).

Draw on Update

Here I’m going to do something simple: I want to make a slider (on the web page) and have the X position of the square be determined by the value of the slider. Making a simple slider is easy: there is a “range” object that we can put on the web page. Then, in our script, we can find the slider (just like we found the canvas), and ask it for what it’s value is.

JS Bin on jsbin.com

One trick: we can’t draw the rectangle until after the slider has been made (since we need to ask the slider where we’re supposed to draw). Our “onload” trick from above can help with this.

There’s another trick: when the slider changes, we need to redraw the picture. Remember, we’re using an immediate mode API. When we draw the square, we are actually drawing the picture. If we want the square in a different place, we need to redraw the picture (for now, the whole picture – there are ways around that, but it’s a longer story).

JS Bin on jsbin.com

So what I’ll do is to make the thing that draws my picture into a function (I’ve called it “draw”). I’ll call it during the onload function. But I’ll also want to call the very same draw function each time the slider is moved. Notice the line where I “attach” the draw function to the slider, so it gets called when the slider gets moved.

http://www.impressivewebs.com/onchange-vs-oninput-for-range-sliders/

Some comments on JavaScript Style and Efficiency

Let’s think about this code for a minute – and see how some of JavaScript’s features are leading us into potential problems as this program grows more complicated…

First, notice that draw is a global variable. We can only define one draw function. If we load another module, and it wants to use the name draw, it will over-write this one. For the draw function, this might not be such a big deal – but we probably want to avoid having too many global variables.

Second, notice that on every update, we have to search the whole document for the canvas and the slider. If the document was big, this could be time consuming: and we want draw to be fast, since we want the screen to update as we move the mouse. An obvious thing to do would be to find the canvas and slider once, store them in a global variable. Of course, this makes a yucky global variable. There is also the problem that this has to happen within the onload function (because we can find them until we’re sure the page is ready).

There is a nice way to address both of these problems, using the basic features of JavaScript (the fact that it is a lexically scoped language that supports closures). This is a common paradigm of JavaScript programming that you will see a lot: use scope and closures to build other useful abstractions and achieve things (like modularity) we like.

The idea is that we put everything into the function that gets called onload. Note that in this example, I am also giving that function a name – but we could have created it anonymously as in the previous example. The more important thing to note is how all the things we might have declared as global, are instead placed inside this function – so they can only be seen inside this function.

JS Bin on jsbin.com

Notice how this solves both of the issues above. The draw function is now not global – nothing outside of the setup function can see it. Similarly, I can create other variables (canvas, slider) that are similarly hidden. Since they are inside the setup function (which only gets run at “onload”) they happen at the right time and place. And that I only search for them once when the page loads – not each time the slider moves.

Take time to understand this example – it gives you a sense of a real JavaScript programming idiom. It shows why programming in JavaScript might be different than some other language you’re used to.

Draw All the Time

Suppose I want to just keep redrawing. This doesn’t make sense if the picture isn’t changing, but if the picture is animating (changing on each frame) then I want to keep redrawing. As soon as I finish drawing, I want to draw again (or maybe wait a little while). The trick is that we want to give the web browser a chance to do other stuff too.

JS Bin on jsbin.com

Again, there are probably many ways to do this. The one I learned uses a feature called “requestAnimationFrame.” The idea is that this schedules a redraw at some time in the future. It’s a method of window.

Notice that I commented out the extra call to “draw” when the slider changes. Because the things are being redrawn all the time, we don’t need to force a redraw when the slider changes. In fact, if we ask for an extra draw when the slider changes, this will add another request for a redraw, and before we know it, we’ll have way to many requests, and things will go crazy.

http://creativejs.com/resources/requestanimationframe/
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

One problem with requestAnimationFrame is that it tries to go fast (60 frames per second). So a different possibility is to schedule the redraw a certain time in the future (some number of milliseconds) use “setTimeout”. You can look up how to do that. You can also look up debates as to why one is better than the other.

What you should have learned

This isn’t really graphics, but its stuff that is useful for writing graphics programs. Hopefully, you got a sense of when you can make drawing happen in your web-based graphics program: this will also be useful when we start using other APIs than Canvas. You saw simple ways to:

  1. Make sure your pictures get drawn onto a web page
  2. Add a slider to your program so you can change things about your picture
  3. Make animation
  4. Use common JavaScript programming idioms to organize your code.

Now you can use this to make something less boring!

Print Friendly, PDF & Email

Previous post:

Next post: