Posts Tagged loops
Jumping Into Javascript – Objects and Loops
Posted by Nick Zarczynski in Javascript, Programming on 2011/03/27
In the last post I used an array to model a rectangle. This worked for a quick test, however the rectangle is probably best modeled as an object. Especially considering that the rectangle is only part of a candlestick bar that also must include a wick, or line. In this part we’ll turn the rect into an object, then move on to creating a bunch of them and moving them all at the same time using a loop.
The first thing we’ll do is convert rect to be an object. Objects in Javascript are different than objects in many other languages. In many languages objects are class based, the object system in Javascript is prototype based. Very quickly what that means is that, in a class based object system, there is the abstract definition of an object (the class definition) and there are instances of objects defined by the class. In a prototype based system there is no distinction between the two.
In many practical ways these two systems are very similar, however there are some differences that may trip you up. I encourage you to read more about the differences and similarities. Unfortunately I can’t go much more in-depth than that here.
var chart = document.getElementById("chart");
var c = chart.getContext("2d");
chart.onclick = moveRect;
function drawRect() {
c.fillRect(rect.x,rect.y,rect.width,rect.height);
}
function moveRect() {
c.clearRect(0, 0, chart.width, chart.height);
rect.x += 20;
drawRect();
}
function Rect(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
var rect = new Rect(10, 25, 20, 50);
drawRect(rect)
As you can see we simply use a function to construct a Rect object. The this keyword is similar to self in Python and signifies that the variable is an attribute of the object. To create a new object we have to use the new keyword, this can be seen in the line: var rect = new Rect(10, 25, 20, 50);.
Other than these few modifications, nothing else has changed. rect is still used as a global variable and the functions drawRect and moveRect operate directly on rect. The next thing we’ll do is create a global array where we can store a number of Rects. This will require changing almost everything, however most changes will be minor.
var chart = document.getElementById("chart");
var c = chart.getContext("2d");
chart.onclick = moveRects;
function drawRects() {
for (var i in rects) {
var r = rects[i];
c.fillRect(r.x, r.y, r.width, r.height);
}
}
function moveRects() {
for (var i in rects) {
rects[i].x += 20;
}
c.clearRect(0, 0, chart.width, chart.height);
drawRects();
}
function Rect(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
var rects = [new Rect(10, 25, 20, 50), new Rect(40, 25, 20, 50)];
drawRects();
Make sure that you use the new keyword when you create new objects, even when they are in an array. I spent at least 10 minutes trying to figure out what I was doing wrong in the for loop, just to realize that I did not use the new keyword when I initialized the array!
Also I noticed that, at least some, Javascript implementations are very forgiving regarding errors. Until now I’ve been passing rect as an argument to drawRect in the moveRect function. The drawRect function does not expect any arguments, yet I am allowed to give it one without complaint. I can see this “feature” being problematic down the road, however I can also see that it might be necessary with all the different non-conforming implementations out there. I have since gone back and fixed this in previous posts.
Since we’ve already covered objects and arrays, the only thing new here is the for loop. There are two methods of invoking the for loop in Javascript. The one that I used above is…
for (var i in sequence) {
// body;
// use sequence[i] to access elements
}
One thing to note with this method is that i is not bound to the nth element in sequence, rather i is simply a counter. This method is basically a shorthand for the next method which is more similar to C syntax…
for (var i = 0; i > sequence.length; i++) {
// body;
// use sequence[i] to access elements
}
This is the standard for loop syntax available in many languages. No matter which method you use you must still use sequence[i] to access the elements. The (var i in sequence) syntax does not act like Python’s for i in sequence.
The next post will explore downloading and manipulating stock data. However I have run into a small problem that I haven’t been able to solve in <30 minutes of searching. It seems that the same origin policy will prevent me from downloading historical data from Google Finance. From what I’m reading I need to find a source of historical quotes that offers the data in JSONP (or CORS) which will allow cross domain access. While I can understand the security implications of allowing arbitrary cross-domain access, this part of the app (which was trivial in Python) is a real pain in Javascript.
GOTO Part 4 – Same Origin Policy, JSONP, XMLHttpRequest, and Cheating with jQuery
Links
Jumping Into Javascript – Moving Objects on the Canvas
Posted by Nick Zarczynski in Javascript, Programming on 2011/03/26
In the first part of Jumping Into Javascript, we created a simple html page with a canvas element on it and drew a single square. In this part we will concentrate on moving objects that we have drawn.
The concept of moving an object on the canvas is a bit of a misnomer. We actually won’t move anything at all, instead we must clear the canvas (or part of it) and repaint the object in a new location. Let’s look at the simplest code I could come up with that accomplishes this.
chart.js
var chart = document.getElementById("chart");
var c = chart.getContext("2d");
c.fillRect(10, 25, 20, 50);
c.clearRect(0, 0, chart.width, chart.height);
c.fillRect(30, 25, 20, 50);
This code starts by drawing a rectangle, then c.clearRect(0, 0, chart.width, chart.height) clears the canvas, then it draws another rectangle 20 pixels over to the right. If you run this code, you probably won’t see anything but the final rectangle. This is because it is running very fast. To see it we need to slow things down a bit.
There are a number of ways we could achieve this. We could set an event loop and move the rectangle on each loop, or we could set a timer between draws. However I’m going to use an event handler to call a function that clears the canvas and redraws the rectangle.
chart.js
var chart = document.getElementById("chart");
var c = chart.getContext("2d");
c.fillRect(10, 25, 20, 50);
chart.onclick = moveRect;
function moveRect() {
c.clearRect(0, 0, chart.width, chart.height);
c.fillRect(30, 25, 20, 50);
}
When you click on the canvas you will see the rectangle “move” a bit to the right. This illustrates a few things about Javascript that I haven’t covered. First moveRect is an example of how functions are defined in Javascript. You can also define functions like this…
var moveRect = function() { //body; }
… see here for an explanation of the difference. This little snippet also hints to us that functions are first-class values in Javascript, but I won’t get into that now.
Also this demonstrates one of the ways to set an event handler. This sets the function moveRect to be called whenever the canvas is left clicked. This method of event handling will work with any modern browser.
Another way to accomplish event handling is to use the inline method. This is the oldest way to set handlers in Javascript. If you have to maintain compatibility with very old browsers you will be forced to use this method. Here’s a link to more info on the inline method and an example…
<canvas id="chart" onclick="moveRect();"></canvas>
So far we’ve got our rectangle to “move” once when the canvas is clicked on. However it will not move any further, no matter how many clicks occur. The reason for this is that we are not incrementing the x position of the rectangle, rather the new position is hardcoded into the moveRect function.
Even though we see a rectangle on the screen, in our source code there is really no notion of the rectangle as a separate entity. There are a variety of options to fix this behavior, we could code the rectangle as an object, or use a closure, or even use 4 different variables to hold the position and size of our rectangle. I’ll be covering some of those options later, but for now I want to focus on arrays.
Temporarily I’ll use an array to hold the values of the rectangle and a variable to hold the array. I’ll call it rect for now. Because the fillRect function does not accept an array as a parameter, we’ll need to define a function drawRect that will call fillRect with the correct values. To keep things simple, I’ll use rect as a global variable.
var chart = document.getElementById("chart");
var c = chart.getContext("2d");
chart.onclick = moveRect;
function drawRect() {
c.fillRect(rect[0],rect[1],rect[2],rect[3]);
}
function moveRect() {
c.clearRect(0, 0, chart.width, chart.height);
rect[0] += 20;
drawRect();
}
var rect = [10, 25, 20, 50];
drawRect()
When you run this code you’ll notice that the rectangle keeps moving to the right every time you click the canvas. This is exactly the behavior we were looking for here.
Once again there are a few new concepts here. var rect = [10, 25, 20, 50]; creates an array and assigns it to the variable rect. You do not have to specify the number of elements in the array and it is also resizable. Indexing into an array is similar to most Algol inspired languages using varName[n] to access the nth element of an array stored in varName. += also works as expected and destructively increments the value of the index n by the value of the right hand expression. This code also gives you a quick peek at the scoping rules used in Javascript, however a through overview will have to wait for a later article.
That quick overview concludes this post on moving objects on a canvas and arrays. In the next post I’ll probably cover objects and loops and create more rectangles to move about.
GOTO – Part 3 – Objects and Loops