// -------------------------- Field -------------------------- // function Field( elem, particleCount, maxDisplacement ) { this.elem = elem; particleCount = particleCount || 100; this.width = this.elem.offsetWidth; this.height = this.elem.offsetHeight; this.spacing = Math.floor( Math.sqrt( document.body.offsetWidth * document.body.offsetHeight / particleCount ) ); this.maxDisplacement = maxDisplacement || this.spacing; this.cols = Math.floor( this.width / this.spacing ); this.rows = Math.floor( this.height / this.spacing ); // make some particles this.particles = []; var particle, x, y; var frag = document.createDocumentFragment(); for ( var row=0; row < this.rows; row++ ) { for ( var col=0; col < this.cols; col++ ) { x = ( col + 0.5 ) * this.spacing; y = ( row + 0.5 ) * this.spacing; particle = new Particle( x, y, this.spacing, this.maxDisplacement ); this.particles.push( particle ); frag.appendChild( particle.elem ); } } this.elem.appendChild( frag ); this.elem.addEventListener( 'mousemove', this, false ); } // allows for mousemoveHandler to be triggered after mousemove event Field.prototype.handleEvent = function( event ) { var handler = event.type + 'Handler'; if ( this[ handler ] ) { this[ handler ]( event ); } }; Field.prototype.mousemoveHandler = function( event ) { var point = { x: event.pageX, y: event.pageY }; this.eachParticleDo( 'reachFor', point ); }; Field.prototype.eachParticleDo = function( methodName ) { var particle; // pass in any other arguments after methodName var args = Array.prototype.slice.call( arguments, 1 ); for ( var i=0, len = this.particles.length; i < len; i++ ) { particle = this.particles[i]; // first argument, particle, is what this will be inside function // second argument is the arguments for that function particle[ methodName ].apply( particle, args ); } }; // -------------------------- Particle -------------------------- // function Particle( x, y, spacing, maxDisplacement ) { this.spacing = spacing; this.maxDisplacement = maxDisplacement; this.elem = document.createElement('div'); this.size = this.spacing * 0.5; this.elem.style.width = this.size + 'px'; this.elem.style.height = this.size + 'px'; this.elem.style.borderRadius = this.size * 0.5 + 'px'; this.elem.style.position = 'absolute'; this.origin = { x: x, y: y }; this.position( x, y ); this.setColor('black'); } Particle.prototype.setColor = function( color ) { this.elem.style.backgroundColor = color; }; Particle.prototype.position = function( x, y ) { this.x = x; this.y = y; this.elem.style.left = ( this.x - this.size * 0.5 ) + 'px'; this.elem.style.top = ( this.y - this.size * 0.5 ) + 'px'; }; Particle.prototype.reachFor = function( point ) { var dx = point.x - this.origin.x; var dy = point.y - this.origin.y; var distance = Math.sqrt( dx * dx + dy * dy ); distance = Math.min( distance, this.maxDisplacement ); var angle = Math.atan2( dy, dx ); var x = this.origin.x + Math.cos( angle ) * distance; var y = this.origin.y + Math.sin( angle ) * distance; this.position( x, y ); }; // -------------------------- init -------------------------- // function init() { var fieldArea = document.getElementById('field-area'); window.myField = new Field( fieldArea, 100 ); } window.addEventListener( 'load', init, false );