github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/website/source/assets/javascripts/app/Engine.js (about)

     1  (function(
     2  	Base,
     3  	Vector,
     4  	Logo,
     5  	Grid,
     6  	Chainable
     7  ){
     8  
     9  var sqrt, pow, Engine;
    10  
    11  if (!window.requestAnimationFrame) {
    12  	window.requestAnimationFrame = (function(){
    13  		return  window.requestAnimationFrame   ||
    14  			window.webkitRequestAnimationFrame ||
    15  			window.mozRequestAnimationFrame    ||
    16  			function( callback ){
    17  				window.setTimeout(callback, 1000 / 60);
    18  			};
    19  	})();
    20  }
    21  
    22  sqrt = Math.sqrt;
    23  pow  = Math.pow;
    24  
    25  Engine = Base.extend({
    26  
    27  	scale: window.devicePixelRatio || 1,
    28  	// scale:1,
    29  
    30  	shapes     : [],
    31  	particles  : [],
    32  	particlesA : [],
    33  	particlesB : [],
    34  
    35  	_deferredParticles: [],
    36  
    37  	ticks: [],
    38  
    39  	starGeneratorRate: 600,
    40  
    41  	mouse: {
    42  		x: -9999,
    43  		y: -9999
    44  	},
    45  
    46  	constructor: function(canvas, background, tagLine){
    47  		this.canvas     = canvas;
    48  		this.background = background;
    49  		this.tagLine    = tagLine;
    50  
    51  		if (!this.canvas.getContext) {
    52  			return null;
    53  		}
    54  
    55  		this.context = this.canvas.getContext('2d');
    56  
    57  		this.setupEvents();
    58  		this.setupStarfield();
    59  		this.setupTessellation();
    60  		this.setupMisc();
    61  
    62  		this.startEngine();
    63  	},
    64  
    65  	startEngine: function(){
    66  		var parent = this.canvas.parentNode;
    67  
    68  		this.background.className += ' show';
    69  		this.canvas.style.opacity = 1;
    70  
    71  		// We have to pass the engine into Chainable to
    72  		// enable the timers to properly attach to the
    73  		// run/render loop
    74  		new Chainable(this)
    75  			.wait(1000)
    76  			.then(function(){
    77  				this.starGeneratorRate = 200;
    78  			}, this)
    79  			.wait(500)
    80  			.then(function(){
    81  				parent.className += ' state-one';
    82  			})
    83  			.wait(150)
    84  			.then(function(){
    85  				parent.className += ' state-two';
    86  			})
    87  			.wait(150)
    88  			.then(function(){
    89  				parent.className += ' state-three';
    90  			})
    91  			.wait(500)
    92  			.then(function(){
    93  				parent.className += ' state-four';
    94  			})
    95  			.wait(100)
    96  			.then(function(){
    97  				this.showShapes = true;
    98  			}, this)
    99  			.wait(1000)
   100  			.then(function(){
   101  				this.logo.startBreathing();
   102  				this.showGrid = true;
   103  			}, this)
   104  			.wait(1000)
   105  			.then(function(){
   106  				this.typewriter.start();
   107  			}, this);
   108  
   109  		this.render();
   110  	},
   111  
   112  
   113  	setupMisc: function(){
   114  		this.last = Date.now() / 1000;
   115  		this.render = this.render.bind(this);
   116  
   117  		this.typewriter = new Engine.Typewriter(this.tagLine);
   118  	},
   119  
   120  	setupEvents: function(){
   121  		this.resize = this.resize.bind(this);
   122  		this.resize();
   123  		window.addEventListener('resize', this.resize, false);
   124  
   125  		this._handleScroll = this._handleScroll.bind(this);
   126  		this._handleScroll();
   127  		window.addEventListener('scroll', this._handleScroll, false);
   128  
   129  		this._handleMouseCoords = this._handleMouseCoords.bind(this);
   130  		window.addEventListener('mousemove', this._handleMouseCoords, false);
   131  	},
   132  
   133  	setupStarfield: function(){
   134  		this.particles = [];
   135  		// this.generateParticles(50, true);
   136  		this.generateParticles(400);
   137  	},
   138  
   139  	setupTessellation: function(canvas){
   140  		var size, offset;
   141  		this.shapes = [];
   142  		if (window.innerWidth < 570) {
   143  			size = 300;
   144  			offset = 0;
   145  		} else {
   146  			size = 360;
   147  			offset = 40;
   148  		}
   149  
   150  		this.logo = new Engine.Shape(
   151  			-(size / 2),
   152  			-(size / 2 + offset),
   153  			size,
   154  			size,
   155  			Logo.points,
   156  			Logo.polygons
   157  		);
   158  
   159  		this.grid = new Engine.Shape.Puller(this.width, this.height, Grid);
   160  	},
   161  
   162  
   163  	getAverageTickTime: function(){
   164  		var sum = 0, s;
   165  
   166  		for (s = 0; s < this.ticks.length; s++) {
   167  			sum += this.ticks[s];
   168  		}
   169  
   170  		window.console.log('Average Tick Time:', sum / this.ticks.length);
   171  	},
   172  
   173  	getLongestTick: function(){
   174  		var max = 0, index, s;
   175  
   176  		for (s = 0; s < this.ticks.length; s++) {
   177  			if (this.ticks[s] > max) {
   178  				max = this.ticks[s];
   179  				index = s;
   180  			}
   181  		}
   182  
   183  		window.console.log('Max tick was:', max, 'at index:', index);
   184  	},
   185  
   186  	render: function(){
   187  		var scale = this.scale, p, particle, index;
   188  
   189  		if (this.paused) {
   190  			return;
   191  		}
   192  
   193  		if (this.scrollY > this.height) {
   194  			window.requestAnimationFrame(this.render);
   195  			return;
   196  		}
   197  
   198  		this.context.clearRect(
   199  			-(this.width  / 2) * scale,
   200  			-(this.height / 2) * scale,
   201  			this.width  * scale,
   202  			this.height * scale
   203  		);
   204  
   205  		this.now = Date.now() / 1000;
   206  		this.tick = Math.min(this.now - this.last, 0.017);
   207  
   208  		// We must attach the chainable timer to the engine
   209  		// run/render loop or else things can get pretty
   210  		// out of wack
   211  		if (this.updateChainTimer) {
   212  			this.updateChainTimer(this.tick);
   213  		}
   214  
   215  		// Update all particles... may need to be optimized
   216  		for (p = 0; p < this.particles.length; p++) {
   217  			this.particles[p].update(this);
   218  		}
   219  
   220  		// Batch render particles based on color
   221  		// to prevent unneeded context state change
   222  		this.context.fillStyle = '#8750c2';
   223  		for (p = 0; p < this.particlesA.length; p++) {
   224  			particle = this.particlesA[p];
   225  
   226  			if (particle.radius < 0.25) {
   227  				continue;
   228  			}
   229  			this.context.fillRect(
   230  				particle.pos.x * scale >> 0,
   231  				particle.pos.y * scale >> 0,
   232  				particle.radius * scale,
   233  				particle.radius * scale
   234  			);
   235  		}
   236  
   237  		this.context.fillStyle = '#b976ff';
   238  		for (p = 0; p < this.particlesB.length; p++) {
   239  			particle = this.particlesB[p];
   240  
   241  			if (particle.radius < 0.25) {
   242  				continue;
   243  			}
   244  			this.context.fillRect(
   245  				particle.pos.x * scale >> 0,
   246  				particle.pos.y * scale >> 0,
   247  				particle.radius * scale,
   248  				particle.radius * scale
   249  			);
   250  		}
   251  
   252  		this.particlesA.length = 0;
   253  		this.particlesB.length = 0;
   254  
   255  		// Remove destroyed particles
   256  		for (p = 0; p < this._deferredParticles.length; p++) {
   257  			index = this.particles.indexOf(this._deferredParticles.pop());
   258  			if (index >= 0) {
   259  				this.particles.splice(index, 1);
   260  			}
   261  		}
   262  
   263  		if (this.showGrid) {
   264  			this.grid
   265  				.update(this)
   266  				.draw(this.context, scale, this);
   267  		}
   268  
   269  		if (this.showShapes) {
   270  			this.logo
   271  				.update(this)
   272  				.draw(this.context, scale, this);
   273  		}
   274  
   275  		this.typewriter.update(this);
   276  
   277  		this.last = this.now;
   278  
   279  		this.generateParticles(this.starGeneratorRate * this.tick >> 0);
   280  
   281  		window.requestAnimationFrame(this.render);
   282  	},
   283  
   284  	generateParticles: function(num, fixed){
   285  		var p;
   286  
   287  		for (p = 0; p < num; p++) {
   288  			if (fixed) {
   289  				this.particles.push(new Engine.Particle.Fixed(this.width, this.height));
   290  			} else {
   291  				this.particles.push(new Engine.Particle(this.width, this.height));
   292  			}
   293  		}
   294  	},
   295  
   296  	resize: function(){
   297  		var scale = this.scale,
   298  			size, offset;
   299  
   300  		if (window.innerWidth < 570) {
   301  			this.height = 560;
   302  		} else {
   303  			this.height = 700;
   304  		}
   305  
   306  		this.width  = window.innerWidth;
   307  
   308  		this.canvas.width  = this.width  * scale;
   309  		this.canvas.height = this.height * scale;
   310  
   311  		this.context.translate(
   312  			this.width  / 2 * scale >> 0,
   313  			this.height / 2 * scale >> 0
   314  		);
   315  		this.context.lineJoin = 'bevel';
   316  
   317  		if (this.grid) {
   318  			this.grid.resize(this.width, this.height);
   319  		}
   320  
   321  		if (this.logo) {
   322  			if (this.height === 560) {
   323  				size = 300;
   324  				offset = 0;
   325  			} else {
   326  				size = 360;
   327  				offset = 40;
   328  			}
   329  			this.logo.resize(size, offset);
   330  		}
   331  	},
   332  
   333  	_handleMouseCoords: function(event){
   334  		this.mouse.x = event.pageX;
   335  		this.mouse.y = event.pageY;
   336  	},
   337  
   338  	_handleScroll: function(){
   339  		this.scrollY = window.scrollY;
   340  	},
   341  
   342  	pause: function(){
   343  		this.paused = true;
   344  	},
   345  
   346  	resume: function(){
   347  		if (!this.paused) {
   348  			return;
   349  		}
   350  		this.paused = false;
   351  		this.render();
   352  	},
   353  
   354  	getSnapshot: function(){
   355  		window.open(this.canvas.toDataURL('image/png'));
   356  	}
   357  
   358  });
   359  
   360  Engine.map = function(val, istart, istop, ostart, ostop) {
   361  	return ostart + (ostop - ostart) * ((val - istart) / (istop - istart));
   362  };
   363  
   364  Engine.getRandomFloat = function(min, max) {
   365  	return Math.random() * (max - min) + min;
   366  };
   367  
   368  Engine.getRandomInt = function(min, max) {
   369  	return Math.floor(Math.random() * (max - min + 1) + min);
   370  };
   371  
   372  Engine.clone = function(ref) {
   373  	var clone = {}, key;
   374  	for (key in ref) {
   375  		clone[key] = ref[key];
   376  	}
   377  	return clone;
   378  };
   379  
   380  window.Engine = Engine;
   381  
   382  })(
   383  	window.Base,
   384  	window.Vector,
   385  	window.Logo,
   386  	window.Grid,
   387  	window.Chainable
   388  );