| home / programming / javascript / gr / column7 / 1 | [previous][next] |
|
|
The next task is to solve the problem of rendering stars on the browser page. Some readers will recall an article I wrote earlier this year titled “JavaScript OO Vector Graphics Package.” In that article, I presented a simple graphics drawing framework that allows shapes to be drawn on the web page using small pixel sized <div> elements. Here, I make use of that graphics package. To keep the code modular, I am assuming the graphics framework code will be held in a separate file called JSGraphics.js. This file can be referenced using the src attribute of the <script> tag
<script src="JSGraphics.js"></script>
For the purposes of this article, I have made some changes to the original JSGraphics code:
| Graphics.prototype.createPlotElement = function(x,y,w,h) { |
|||
| // detect canvas if ( !this.oCanvas ) { |
|||
| switch ( typeof(this.canvas) ) { |
|||
| case "string": this.oCanvas = document.getElementById(this.canvas); break; case "object": this.oCanvas = this.canvas; break; default: this.oCanvas = document.body; break; |
|||
| } | |||
| } // rest of createPlotElement function ... |
|||
} Point.prototype.move = function(x,y) |
|||
| this.oDiv.style.left = x + "px"; this.oDiv.style.top = y + "px"; |
|||
| } Point.prototype.setSize = function(nSize) { |
|||
| this.oDiv.style.width = nSize + "px"; this.oDiv.style.height = nSize + "px"; |
|||
| } | |||
The change to the createPlotElement allows the user to pass a DIV object directly to the Graphics constructor, rather than give it the ID and have it search the document. To make the starfield simulation as efficient as possible, I needed to provide a means to moving a point element without having to unplot and redraw it, so a move() method has been added to the Point prototype. To make the stars appear bigger as they come closer, I also added a setSize() method that controls the width and height of the underlying <div> element.
The start() method of the StarFieldSaver object first checks whether the screen saver is active; it would be quite confusing if it were to be started more than once. After this test is passed, it checks whether it has been initialized by checking an object called this.oCanvas. The canvas is a <div> element and is used by the JSGraphics code to render the graphics objects. If the canvas has not been allocated, the init() method is called. After this, the visibility style of the canvas is set to visible because as we will see later, it is set to hidden when the screen saver is stopped. Last of all, the tick() method is called to start the animation.
| StarFieldSaver.prototype.start = function() { |
|
| if ( this.bActive ) return; this.bActive = true; if ( !this.oCanvas ) this.init(); this.oCanvas.style.visibility = "visible"; this.tick(); |
|
| } | |
The init() method does nothing special: The canvas <div> element is created and configured, a Graphics object is created and its pen color set to white and an array is created to store the points used to render the stars.
| StarFieldSaver.prototype.init = function() { |
|
this.oCanvas = document.createElement("div"); this.g = new Graphics(this.oCanvas); |
|
| } | |
Now for the animation; it all happens in one method called tick(). As the name suggests, this method is called repeatedly at small intervals to drive the starfield animation.
| StarFieldSaver.prototype.tick = function() { |
|
| // resize canvas to fit the browser window. this.oCanvas.style.left = document.body.scrollLeft + "px"; this.oCanvas.style.top = document.body.scrollTop + "px"; this.oCanvas.style.width = document.body.clientWidth + "px"; this.oCanvas.style.height = document.body.clientHeight + "px"; this.width = document.body.clientWidth; this.height = document.body.clientHeight; |
|
The first task is resize, so the canvas is positioned to the current scroll position and width. This must be done each time tick() is called, so the user can resize the browser while the screen saver is still running.
| var cx = this.width / 2; var cy = this.height / 2; for ( var i in this.aPoints ) { |
||
| var p = this.aPoints[i]; var dx = p.x - cx; var dy = p.y - cy; // move point outwards p.x += dx / 50; p.y += dy / 50; |
||
The next step is to move each star point away from the center by a small distance to give the impression of motion. In the code above, each star is moved by a distance proportional to its distance from the center, so the stars will speed up as they appear closer to the viewer. The X and Y properties of the Point objects are added after the Point object has been created and are used to track the point location on the canvas.
| // if it has gone off the screen... if ( (p.x < 0) || (p.x > this.width - Math.ceil(p.n/50)) || |
|||
| (p.y < 0) || (p.y > this.height - Math.ceil(p.n/50)) ) | |||
| { | |||
| // replace star somwhere // p.n is the point’s iteration number – used to determine |
|||
| } | |||
If a star falls off the canvas, it needs to be repositioned on the canvas again. Any random point on the canvas will do.
| p.move(Math.floor(p.x),Math.floor(p.y)); // resize as they get closer |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
After its position has been altered, the Point object is moved. To make the stars grow as they come towards the viewer, a property (n) is added to each point to keep track of how many ticks have passed since it was first seen. Every fifty ticks, the star will increase in size by one pixel.
| // if we have less stars than we need, make some more. while ( this.aPoints.length < this.nStars ) { |
||
| var x = Math.random() * this.width; var y = Math.random() * this.height; var p = this.g.drawPoint(Math.floor(x), Math.floor(y)); p.setSize(1); p.x = x; p.y = y; p.n = 0; this.aPoints.push(p); |
||
| } | ||
The first time tick() is called, the array of points will be empty. If this is the case, the points are generated and placed randomly on the canvas.
| var pThis = this; var f = function(){pThis.tick();} this.timerID = window.setTimeout(f,this.speed); |
|
| } |
Finally, the tick() method is scheduled again using the speed setting to control the rate of ticks.
| StarFieldSaver.prototype.stop = function() { |
||
if ( !this.bActive ) return; for ( var i in this.aPoints ) |
||
| this.aPoints[i].undraw(); | ||
} this.bActive = false; |
||
| } | ||
To stop the simulation, the stop() method sets the visibility of the canvas
to hidden and removes each star by calling the undraw() method. Even though
the parent note is hidden, the stars can still be visible.
| home / programming / javascript / gr / column7 / 1 | [previous][next] |
Created: March 27, 2003
Revised: June 8, 2004
URL: http://webreference.com/programming/javascript/gr/column7/1