/** * The stars in our starfield! * Stars coordinate system is relative to the CENTER of the canvas * @param {number} x * @param {number} y */ var Star = function(x, y, maxSpeed) { this.x = x; this.y = y; this.slope = y / x; // This only works because our origin is always (0,0) this.opacity = 0; this.speed = Math.max(Math.random() * maxSpeed, 1); }; /** * Compute the distance of this star relative to any other point in space. * * @param {int} originX * @param {int} originY * * @return {float} The distance of this star to the given origin */ Star.prototype.distanceTo = function(originX, originY) { return Math.sqrt(Math.pow(originX - this.x, 2) + Math.pow(originY - this.y, 2)); }; /** * Reinitializes this star's attributes, without re-creating it * * @param {number} x * @param {number} y * * @return {Star} this star */ Star.prototype.resetPosition = function(x, y, maxSpeed) { Star.apply(this, arguments); return this; }; /** * The BigBang factory creates stars (Should be called StarFactory, but that is * a WAY LESS COOL NAME! * @type {Object} */ var BigBang = { /** * Returns a random star within a region of the space. * * @param {number} minX minimum X coordinate of the region * @param {number} minY minimum Y coordinate of the region * @param {number} maxX maximum X coordinate of the region * @param {number} maxY maximum Y coordinate of the region * * @return {Star} The random star */ getRandomStar: function(minX, minY, maxX, maxY, maxSpeed) { var coords = BigBang.getRandomPosition(minX, minY, maxX, maxY); return new Star(coords.x, coords.y, maxSpeed); }, /** * Gets a random (x,y) position within a bounding box * * * @param {number} minX minimum X coordinate of the region * @param {number} minY minimum Y coordinate of the region * @param {number} maxX maximum X coordinate of the region * @param {number} maxY maximum Y coordinate of the region * * @return {Object} An object with random {x, y} positions */ getRandomPosition: function(minX, minY, maxX, maxY) { return { x: Math.floor((Math.random() * maxX) + minX), y: Math.floor((Math.random() * maxY) + minY) }; } }; /** * Constructor function of our starfield. This just prepares the DOM nodes where * the scene will be rendered. * * @param {string} canvasId The DOM Id of the <div> containing a <canvas> tag */ var StarField = function(containerId) { this.container = document.getElementById(containerId); this.canvasElem = this.container.getElementsByTagName('canvas')[0]; this.canvas = this.canvasElem.getContext('2d'); this.width = this.container.offsetWidth; this.height = this.container.offsetHeight; this.starField = []; }; /** * Updates the properties for every star for the next frame to be rendered */ StarField.prototype._updateStarField = function() { var i, star, randomLoc, increment; for (i = 0; i < this.numStars; i++) { star = this.starField[i]; increment = Math.min(star.speed, Math.abs(star.speed / star.slope)); star.x += (star.x > 0) ? increment : -increment; star.y = star.slope * star.x; star.opacity += star.speed / 100; // Recycle star obj if it goes out of the frame if ((Math.abs(star.x) > this.width / 2) || (Math.abs(star.y) > this.height / 2)) { //randomLoc = BigBang.getRandomPosition( // -this.width / 2, -this.height / 2, // this.width, this.height //); randomLoc = BigBang.getRandomPosition( -this.width / 10, -this.height / 10, this.width / 5, this.height / 5 ); star.resetPosition(randomLoc.x, randomLoc.y, this.maxStarSpeed); } } }; /** * Renders the whole starfield (background + stars) * This method could be made more efficient by just blipping each star, * and not redrawing the whole frame */ StarField.prototype._renderStarField = function() { var i, star; // Background this.canvas.fillStyle = "rgba(0, 0, 0, .5)"; this.canvas.fillRect(0, 0, this.width, this.height); // Stars for (i = 0; i < this.numStars; i++) { star = this.starField[i]; this.canvas.fillStyle = "rgba(255, 255, 255, " + star.opacity + ")"; this.canvas.fillRect( star.x + this.width / 2, star.y + this.height / 2, 2, 2); } }; /** * Function that handles the animation of each frame. Update the starfield * positions and re-render */ StarField.prototype._renderFrame = function(elapsedTime) { var timeSinceLastFrame = elapsedTime - (this.prevFrameTime || 0); window.requestAnimationFrame(this._renderFrame.bind(this)); // Skip frames unless at least 30ms have passed since the last one // (Cap to ~30fps) if (timeSinceLastFrame >= 30 || !this.prevFrameTime) { this.prevFrameTime = elapsedTime; this._updateStarField(); this._renderStarField(); } }; /** * Makes sure that the canvas size fits the size of its container */ StarField.prototype._adjustCanvasSize = function(width, height) { // Set the canvas size to match the container ID (and cache values) this.width = this.canvasElem.width = width || this.container.offsetWidth; this.height = this.canvasElem.height = height || this.container.offsetHeight; }; /** * This listener compares the old container size with the new one, and caches * the new values. */ StarField.prototype._watchCanvasSize = function(elapsedTime) { var timeSinceLastCheck = elapsedTime - (this.prevCheckTime || 0), width, height; window.requestAnimationFrame(this._watchCanvasSize.bind(this)); // Skip frames unless at least 333ms have passed since the last check // (Cap to ~3fps) if (timeSinceLastCheck >= 333 || !this.prevCheckTime) { this.prevCheckTime = elapsedTime; width = this.container.offsetWidth; height = this.container.offsetHeight; if (this.oldWidth !== width || this.oldHeight !== height) { this.oldWidth = width; this.oldHeight = height; this._adjustCanvasSize(width, height); } } }; /** * Initializes the scene by resizing the canvas to the appropiate value, and * sets up the main loop. * @param {int} numStars Number of stars in our starfield */ StarField.prototype._initScene = function(numStars) { var i; for (i = 0; i < this.numStars; i++) { this.starField.push( BigBang.getRandomStar(-this.width / 2, -this.height / 2, this.width, this.height, this.maxStarSpeed) ); } // Intervals not stored because I don't plan to detach them later... window.requestAnimationFrame(this._renderFrame.bind(this)); window.requestAnimationFrame(this._watchCanvasSize.bind(this)); }; /** * Kicks off everything! * @param {int} numStars The number of stars to render * @param {int} maxStarSpeed Maximum speed of the stars (pixels / frame) */ StarField.prototype.render = function(numStars, maxStarSpeed) { this.numStars = numStars || 100; this.maxStarSpeed = maxStarSpeed || 3; this._initScene(this.numStars); }; /** * requestAnimationFrame shim layer with setTimeout fallback * @see http://paulirish.com/2011/requestanimationframe-for-smart-animating */ (function() { var lastTime = 0; var vendors = ['ms', 'moz', 'webkit', 'o']; for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame) window.requestAnimationFrame = function(callback, element) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, 16 - (currTime - lastTime)); var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function(id) { clearTimeout(id); }; }()); // Kick off! var starField = new StarField('fullScreen').render(333, 3);
<div class="fullScreen" id="fullScreen"> <canvas id="canvas2d"></canvas> </div>
html, body { overflow: hidden; background: #000; padding: 0px; margin: 0px; width: 100%; height: 100%; } .fullScreen { height: 100%; }