/*! * jQuery Active Background Animator * https://github.com/repraze/active-background-animator * * Copyright 2015, - Thomas Dubosc (http://repraze.com) * Released under the MIT license */ (function( $ ) { $.fn.activeBackgroundAnimator = function(data,options) { // options var settings = $.extend({ test: "yay" }, options ); //check if element is in view function isScrolledIntoView(elem){ var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); var elemTop = $(elem).offset().top; var elemBottom = elemTop + $(elem).height(); return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); } function Canvas(canvas){ this.canvas = canvas[0]; this.context = this.canvas.getContext("2d"); this.ratio = this.canvas.width/this.canvas.height; //surface is nb of 100px (100*100=10000) this.surface = this.canvas.width*this.canvas.height/10000; this.width = function(w){ if(!w){ return this.canvas.width; } if(w!=this.canvas.width){ this.canvas.width = w; this.ratio = this.canvas.width/this.canvas.height; this.surface = this.canvas.width*this.canvas.height/10000; } return this; } this.height = function(h){ if(!h){ return this.canvas.height; } if(h!=this.canvas.height){ this.canvas.height = h; this.ratio = this.canvas.width/this.canvas.height; this.surface = this.canvas.width*this.canvas.height/10000; } return this; } this.clear = function(bounds){ if(!bounds){ this.context.clearRect(0, 0, this.width(), this.height()); } else{ var x = bounds.x || 0; var y = bounds.y || 0; var width = bounds.width || this.width(); var height = bounds.height || this.height(); this.context.clearRect(x, y, width, height); } } } function Animator(container,layers,settings){ this.init = function(){ //create the canvas for the background and a div to sit on top of it var canvas = $('',{'class':'aba-canvas','style':'position:absolute;top:0;left:0;width:100%;height:100%;z-index:0;'}); var overlay = $('
',{'class':'aba-overlay','style':'position:relative;z-index:1;'}); //create object selectors this.container = container; this.canvas = new Canvas(canvas); //move everything from the container to the overlay overlay.html(this.container.html()); this.container.html(""); //add the new elements to container this.container.append(canvas); this.container.append(overlay); //a little styling this.container.css( "position", "relative" ); this.canvas.width(this.container.outerWidth()); this.canvas.height(this.container.outerHeight()); //set and init animations this.animations = layers; this.initLayers(); var self = this; requestAnimationFrame(function(date){self.run(date)}); } this.initLayers = function(){ for(var i=0; i < this.animations.length; i++) { this.animations[i].init(this.canvas); } } this.run = function(date){ var self = this; if(!this.lastFrameDate){ this.lastFrameDate = date; } else{ if(this.canvas.width()!=this.container.outerWidth()||this.canvas.height()!=this.container.outerHeight()){ //update canvas dim this.canvas.width(this.container.outerWidth()); this.canvas.height(this.container.outerHeight()); //reinit? this.initLayers(); } //check if should render, for speed of page if(isScrolledIntoView(this.container)){ var dt = Math.min(0.05,(date-this.lastFrameDate)/1000); this.update(dt); this.render(this.canvas.context); } this.lastFrameDate = date; } requestAnimationFrame(function(date){self.run(date)}); } this.update = function(t){ for(var i=0; i < this.animations.length; i++) { this.animations[i].update(t); } } this.render = function(ctx){ ctx.save; //ctx.clearRect(0, 0, this.canvas.width(), this.canvas.height()); this.canvas.clear(); for(var i=0; i < this.animations.length; i++) { this.animations[i].render(ctx); } ctx.restore(); } } //create the background if(data){ //put in array if single object if(Object.prototype.toString.call( data ) !== '[object Array]'){ data = [data]; } //check if data exist if(data.length>0){ //parse data for(var i=0;ithis.bounds.x+this.bounds.w || point.y>this.bounds.y+this.bounds.h); } this.insert = function(object){ if(this.inBounds(object.getPosition())){ this._insert(object); } return false; }, this._insert = function(object){ var inserted = false; //if subtrees if(this.leaf){ //if it is not full if(this.objects.length < this.capacity){ this.objects.push(object); object.qtNode = this; inserted = true; } else{ //subdivide this tree this._divide(); //insert newly added object inserted = this._insert(object); } } else{ for(var i = 0 ; i<4; i++){ if(this.quadtrees[i].insert(object)){ inserted = true; break; } } if(inserted=== false) { this.objects.push(object); object.qtNode=this; inserted=true; } } return inserted; } this._divide = function(){ this.leaf = false; this.quadtrees = []; var b = this.bounds; var half = {w:b.w/2.0,h:b.h/2.0} //create sub quadtrees for(var row = 0 ; row<2 ; row++){ for(var col = 0 ; col<2 ; col++){ var B = {x: b.x+col*half.w, w: half.w, y: b.y+row*half.h, h: half.h} this.quadtrees.push(new QuadTree(B,this.capacity,this)); } } // We populate subtrees with current node's objects for(var i = this.objects.length - 1 ; i >=0 ; --i){ for(var tId = 0 ; tId<4; tId++){ if(this.quadtrees[tId].insert(this.objects[i])){ this.objects.splice(i,1); break; } } } } this._merge = function(){ for(var i = 0; i< 4; i++){ if(!this.quadtrees[i].leaf || this.quadtrees[i].objects.length>0) return false;// Merging is not possible } //do Merge : this.quadtrees = []; this.leaf = true; return true; } this.getNeighbors = function(pt,objs){ objs = typeof objs !== 'undefined' ? objs : []; if(this.inBounds(pt)){ for(var objId = 0 ; objId < this.objects.length; objId++) objs.push(this.objects[objId]); if(!this.leaf){ for(var treeId = 0; treeId <4 ; treeId++){ this.quadtrees[treeId].getNeighbors(pt,objs); } } } return objs; } this.remove = function(object){ var objIdx = object.qtNode.objects.indexOf(object); if(objIdx!==-1){ object.qtNode.objects.splice(objIdx,1); if(object.qtNode.leaf && object.qtNode.parent!==null) object.qtNode.parent._merge(); return true; } else return false; }, this.move = function(object){ var baseNode = object.qtNode; if(baseNode.inBounds(object.getPosition())){ if(baseNode.leaf)//nothing to do return true; else{ //look if the object is contained in a child var subNode = null; for(var i=0; i<4; i++){ if(baseNode.quadtrees[i].inBounds(object.getPosition())){ subNode = baseNode.quadtrees[i]; break; } } if(subNode !== null){ object.qtNode.remove(object); subNode._insert(object); } } } else if(this.inBounds(object.getPosition())) { baseNode.remove(object); var inserted = false; for(node = baseNode; !inserted && node!==null; node = node.parent){ inserted=node.insert(object); } } } } //useful methods for animation creation var aba = { Position : function(x,y){ this.x = x||0; this.y = y||0; this.set = function(vec){ this.x = vec.x; this.y = vec.y; return this; } this.clone = function(){ return new aba.Position(this.x,this.y); } this.add = function(p){ this.x += p.x; this.y += p.y; return this; } this.sub = function(p){ this.x -= p.x; this.y -= p.y; return this; } this.mul = function(f){ this.x *= f; this.y *= f; return this; } this.div = function(f){ this.x /= f; this.y /= f; return this; } this.equals = function(p){ if(this.x==p.x && this.y==p.y){ return true; } return false; } this.between = function(p,f){ return this.clone().add(p.clone().sub(this).mul(f)); } this.distanceTo = function(p){ return this.clone().sub(p).length(); } this.distanceTo = function(p){ var x = this.x-p.x; var y = this.y-p.y; return Math.sqrt(x*x+y*y); } this.sqDistanceTo = function(p){ var x = this.x-p.x; var y = this.y-p.y; return x*x+y*y; } this.length = function(){ return Math.sqrt(this.y*this.y+this.x*this.x); } this.sqLength = function(){ return this.y*this.y+this.x*this.x; } this.angle = function(){ return Math.atan2(this.y,this.x); } }, Vector : function(vel,ang){ this.velocity = vel; this.angle = ang; this.getPosition = function(){ return new aba.Position(Math.cos(this.angle)*this.velocity,Math.sin(this.angle)*this.velocity); } this.clone = function(){ return new aba.Vector(this.velocity,this.angle); } this.add = function(v){ var np = this.getPosition(); np.add(v.getPosition()); this.velocity = np.length(); this.angle = np.angle(); return this; } this.sub = function(v){ var np = this.getPosition(); np.sub(v.getPosition()); this.velocity = np.length(); this.angle = np.angle(); return this; } this.getProjectionVelocity = function(a){ var vc = this.clone(); vc.angle -= a; return Math.cos(vc.angle)*vc.velocity; } }, randomIntFromInterval : function(min,max){ return Math.floor(Math.random()*(max-min+1)+min); }, _animations : [], getAnimation(name){ if(!aba._animations[name]){ console.error("animation "+name+" not defined"); return false; } return aba._animations[name]; }, registerAnimation : function(name,anim,overwrite=false){ if(typeof name != 'string' || typeof anim != "function"){ console.error("incorrect registration"); return; } if(!aba._animations[name] || overwrite){ aba._animations[name] = anim; } else{ console.error("animation already defined"); return; } }, };