1 // ========================================================================== 2 // NodeGraph.animator 3 // 4 // License: PaperCube is open source software released under 5 // the MIT License (see license.js) 6 // ========================================================================== 7 8 require('core'); 9 10 /** @class NodeGraph.animator 11 12 This is the NodeGraph animation engine. This runs every 40 milliseconds. 13 14 @extends SC.Object 15 @author Peter Bergstrom 16 @version 1.0 17 @copyright 2008-2009 Peter Bergström. 18 @static 19 */ 20 NodeGraph.animator = SC.Object.create( 21 /** @scope NodeGraph.animator.prototype */ { 22 23 /** 24 The queue of animation subjects. 25 26 @property {Array} 27 */ 28 _queue: [], 29 30 /** 31 A reference to a view's main SVG element. Used to "snap" the element to prevent rendering/display bugs. 32 33 @property {DOM Element} 34 */ 35 _svgMain: null, 36 37 /** 38 The height of the svg element passed in. 39 40 @property {Integer} 41 */ 42 _svgMainHeight: 0, 43 44 /** 45 Add an array of subjects to be animated. 46 47 Removes all the previous elements and starts the timer if is has been stopped. 48 49 @param arr {Array} The array of animation subjects. 50 @param {DOM Element} svgMain A reference to a view's main SVG element. Used to "snap" the element to prevent rendering/display bugs. 51 @param svgMainHeight {Integer} The height of the svg element passed in. 52 */ 53 addSubjects: function(arr, svgMain, svgMainHeight) 54 { 55 // Save the svg element and height if needed. 56 this._svgMain = svgMain; 57 this._svgMainHeight = svgMainHeight; 58 59 // Add the subjects. 60 var arrLen = arr.length; 61 for(var i=0; i<arrLen; i++) 62 { 63 var subject = arr[i]; 64 this.addSubject(subject.elm, subject.props, subject.duration, subject.isSVG); 65 } 66 }, 67 68 /** 69 Add a subject to the queue. 70 71 @param {DOM Element} elm The subject to be animated. 72 @param props {Object} The properties that are to be animated. 73 @param duration {Integer} The duration in milliseconds of the element. 74 @param isSVG {boolean} Determines if it is an SVG element or not. 75 */ 76 addSubject: function(elm, props, duration, isSVG) 77 { 78 79 // Fix the properties so that they are whole numbers. 80 var fixed = {}; 81 for(var key in props) 82 { 83 if(key.indexOf('opacity') == -1) 84 { 85 fixed[key] = {start: Math.floor(props[key].start), end: Math.floor(props[key].end)}; 86 } 87 else 88 { 89 fixed[key] = {start: props[key].start, end: props[key].end}; 90 } 91 } 92 93 // Start time. 94 var start = Date.now(); 95 96 97 var subject = {isCompleted: NO}; 98 99 // Animation function. Called once during a heart beat. 100 subject.animate = function() 101 { 102 103 // Calculate the percentage completed. 104 var pct = Math.min(100,parseInt((100*(Date.now()-start)/duration),0)); 105 var done = NO; 106 if(pct >= 100) 107 { 108 done = YES; 109 this.isCompleted = YES; 110 } 111 112 // Loop through the properties and then based on the percentage, set the props. 113 for(var propertyName in props) 114 { 115 var s = fixed[propertyName].start; 116 var e = fixed[propertyName].end; 117 var attrVal = (done) ? e : Math.floor(s + (e - s)*pct/100); 118 119 if(isSVG) 120 { 121 elm.setAttributeNS(null, propertyName, attrVal); 122 } 123 else 124 { 125 if(propertyName == 'opacity') 126 { 127 elm.style.opacity = attrVal; 128 } 129 else 130 { 131 elm.style[propertyName] = attrVal + "px"; 132 } 133 } 134 } 135 }; 136 137 // Push the subject on the queue. 138 this._queue.push(subject); 139 140 }, 141 142 /** 143 Runs the animation one frame, this is called by the SC Timer object. 144 145 @returns {Boolean} Returns NO if the queue is null. 146 */ 147 runAnimation: function() 148 { 149 var q = this._queue; 150 if(!q) return NO; 151 var qLen = q.length; 152 if(qLen == 0) return NO; 153 154 var newQ = []; 155 var delQ = []; 156 157 // Iterate through each element and if it is completed, add it to be deleted. 158 for(var i=0; i<qLen; i++) 159 { 160 q[i].animate(); 161 162 if(!q[i].isCompleted) 163 { 164 newQ.push(q[i]); 165 } 166 else 167 { 168 delQ.push(q[i]); 169 } 170 } 171 172 // Delete what needs to be deleted. 173 qLen = delQ.length; 174 for(var i=0; i<qLen; i++) 175 { 176 delete delQ[i]; 177 } 178 179 // Snap the SVG element. 180 if(this._svgMain) 181 { 182 this._svgMain.setAttributeNS(null, 'height', this._svgMainHeight+1); 183 this._svgMain.setAttributeNS(null, 'height', this._svgMainHeight); 184 } 185 186 // Set the new queue. 187 this._queue = newQ; 188 }, 189 190 init: function() { 191 sc_super(); 192 193 setInterval('NodeGraph.animator.runAnimation()', 50); 194 195 } 196 }); 197 198 199