1 /**************************************************************************** 2 Copyright (c) 2013-2014 Chukong Technologies Inc. 3 4 http://www.cocos2d-x.org 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 THE SOFTWARE. 23 ****************************************************************************/ 24 25 /** 26 * timeline object 27 * @class 28 * @extend ccs.Class 29 */ 30 ccs.Timeline = ccs.Class.extend({ 31 32 //{ccs.Frame} 33 _frames: null, 34 //{ccs.Frame} 35 _currentKeyFrame: null, 36 //{Number} 37 _currentKeyFrameIndex: null, 38 //{Number} 39 _fromIndex: null, 40 //{Number} 41 _toIndex: null, 42 //{Number} 43 _betweenDuration: null, 44 //{Number} 45 _actionTag: null, 46 //{ccs.ActionTimeline} 47 _ActionTimeline: null, 48 //{cc.Node} 49 _node: null, 50 51 ctor: function(){ 52 this._frames = []; 53 this._currentKeyFrame = null; 54 this._currentKeyFrameIndex = 0; 55 this._fromIndex = 0; 56 this._toIndex = 0; 57 this._betweenDuration = 0; 58 this._actionTag = 0; 59 this._ActionTimeline = null; 60 this._node = null; 61 }, 62 63 _gotoFrame: function(frameIndex){ 64 if(this._frames.length === 0) 65 return; 66 67 this._binarySearchKeyFrame(frameIndex); 68 this._apply(frameIndex); 69 }, 70 71 _stepToFrame: function(frameIndex){ 72 if(this._frames.length === 0) 73 return; 74 75 this._updateCurrentKeyFrame(frameIndex); 76 this._apply(frameIndex); 77 }, 78 79 /** 80 * Get the frame list 81 * @returns {ccs.Frame} 82 */ 83 getFrames: function(){ 84 return this._frames; 85 }, 86 87 /** 88 * push frame to frame list 89 * @param {ccs.Frame} frame 90 */ 91 addFrame: function(frame){ 92 this._frames.push(frame); 93 frame.setTimeline(this) 94 }, 95 96 /** 97 * insert the frame to frame list 98 * @param {ccs.Frame} frame 99 * @param {Number} index 100 */ 101 insertFrame: function(frame, index){ 102 this._frames.splice(index, 0, frame); 103 frame.setTimeline(this); 104 105 }, 106 107 /** 108 * remove frame 109 * @param {ccs.Frame} frame 110 */ 111 removeFrame: function(frame){ 112 cc.arrayRemoveObject(this._frames, frame); 113 frame.setTimeline(null); 114 }, 115 116 /** 117 * Set the action tag 118 * @param {Number} tag 119 */ 120 setActionTag: function(tag){ 121 this._actionTag = tag; 122 }, 123 124 /** 125 * Gets the action tag 126 * return {Number} 127 */ 128 getActionTag: function(){ 129 return this._actionTag; 130 }, 131 132 /** 133 * Set the node 134 * @param {cc.Node} node 135 */ 136 setNode: function(node){ 137 for (var i=0; i<this._frames.length; i++){ 138 var frame = this._frames[i]; 139 frame.setNode(node); 140 } 141 }, 142 143 /** 144 * Gets the node 145 * return {cc.Node} 146 */ 147 getNode: function(){ 148 return this._node; 149 }, 150 151 /** 152 * Set the action timeline 153 * @param {ccs.ActionTimeline} action 154 */ 155 setActionTimeline: function(action){ 156 this._ActionTimeline = action; 157 }, 158 159 /** 160 * get the action timeline 161 * return {cc.Action} 162 */ 163 getActionTimeline: function(){ 164 return this._ActionTimeline; 165 }, 166 167 /** 168 * to copy object with deep copy. 169 * returns a clone of action. 170 * @return {ccs.Timeline} 171 */ 172 clone: function(){ 173 var timeline = new ccs.Timeline(); 174 timeline._actionTag = this._actionTag; 175 176 for (var i=0;i<this._frames.length;i++) 177 { 178 var frame = this._frames[i]; 179 var newFrame = frame.clone(); 180 timeline.addFrame(newFrame); 181 } 182 183 return timeline; 184 185 }, 186 187 _apply: function(frameIndex){ 188 if (this._currentKeyFrame) 189 { 190 var currentPercent = this._betweenDuration <= 0 ? 0 : (frameIndex - this._currentKeyFrameIndex) / this._betweenDuration; 191 this._currentKeyFrame.apply(currentPercent); 192 } 193 }, 194 195 _binarySearchKeyFrame: function(frameIndex){ 196 var from = null; 197 var to = null; 198 199 var length = this._frames.length; 200 var needEnterFrame = false; 201 202 do 203 { 204 if (frameIndex <= this._frames[0].getFrameIndex()) 205 { 206 if(this._currentKeyFrameIndex >= this._frames[0].getFrameIndex()) 207 needEnterFrame = true; 208 209 this._fromIndex = 0; 210 211 if(length > 1) 212 this._toIndex = 1; 213 else 214 this._toIndex = 0; 215 216 from = to = this._frames[0]; 217 this._currentKeyFrameIndex = 0; 218 this._betweenDuration = this._frames[0].getFrameIndex(); 219 break; 220 } 221 else if(frameIndex >= this._frames[length - 1].getFrameIndex()) 222 { 223 this._fromIndex = length - 1; 224 this._toIndex = 0; 225 226 from = to = this._frames[length - 1]; 227 this._currentKeyFrameIndex = this._frames[length - 1].getFrameIndex(); 228 this._betweenDuration = 0; 229 break; 230 } 231 232 var target = -1; 233 var low = 0, 234 high = length - 1, 235 mid = 0; 236 while(low <= high){ 237 mid = Math.ceil(( low + high )/2); 238 if(frameIndex >= this._frames[mid].getFrameIndex() && frameIndex < this._frames[mid + 1].getFrameIndex()) 239 { 240 target = mid; 241 break; 242 } 243 if(this._frames[mid].getFrameIndex()>frameIndex) 244 high = mid - 1; 245 else 246 low = mid + 1; 247 } 248 249 this._fromIndex = target; 250 251 if(length > 1) 252 this._toIndex = (target + 1) | 0; 253 else 254 this._toIndex = (target) | 0; 255 256 from = this._frames[this._fromIndex]; 257 to = this._frames[this._toIndex]; 258 259 from = this._frames[target]; 260 to = this._frames[target+1]; 261 262 if(target === 0 && this._currentKeyFrameIndex < from.getFrameIndex()) 263 needEnterFrame = true; 264 265 this._currentKeyFrameIndex = from.getFrameIndex(); 266 this._betweenDuration = to.getFrameIndex() - from.getFrameIndex(); 267 } while (0); 268 269 if(needEnterFrame || this._currentKeyFrame != from) { 270 this._currentKeyFrame = from; 271 this._currentKeyFrame.onEnter(to); 272 } 273 274 }, 275 276 _updateCurrentKeyFrame: function(frameIndex){ 277 if(frameIndex > 60) 278 var a = 0; 279 //! If play to current frame's front or back, then find current frame again 280 if (frameIndex < this._currentKeyFrameIndex || frameIndex >= this._currentKeyFrameIndex + this._betweenDuration) 281 { 282 var from = null; 283 var to = null; 284 285 do 286 { 287 var length = this._frames.length; 288 289 if (frameIndex < this._frames[0].getFrameIndex()) 290 { 291 from = to = this._frames[0]; 292 this._currentKeyFrameIndex = 0; 293 this._betweenDuration = this._frames[0].getFrameIndex(); 294 break; 295 } 296 else if(frameIndex >= this._frames[length - 1].getFrameIndex()) 297 { 298 var lastFrameIndex = this._frames[length - 1].getFrameIndex(); 299 if(this._currentKeyFrameIndex >= lastFrameIndex) 300 return; 301 frameIndex = lastFrameIndex; 302 } 303 304 do{ 305 this._fromIndex = this._toIndex; 306 from = this._frames[this._fromIndex]; 307 this._currentKeyFrameIndex = from.getFrameIndex(); 308 309 this._toIndex = this._fromIndex + 1; 310 if (this._toIndex >= length) 311 { 312 this._toIndex = 0; 313 } 314 315 to = this._frames[this._toIndex]; 316 317 if (frameIndex === from.getFrameIndex()) 318 break; 319 if(frameIndex > from.getFrameIndex() && frameIndex < to.getFrameIndex()) 320 break; 321 if(from.isEnterWhenPassed()) 322 from.onEnter(to); 323 }while (true); 324 325 this._betweenDuration = to.getFrameIndex() - from.getFrameIndex(); 326 327 } while (0); 328 329 this._currentKeyFrame = from; 330 this._currentKeyFrame.onEnter(to); 331 } 332 } 333 334 }); 335 336 /** 337 * Create the Timeline 338 * 339 * @deprecated v3.0, please use new ccs.Timeline() instead. 340 * @returns {ccs.Timeline} 341 */ 342 ccs.Timeline.create = function(){ 343 return new ccs.Timeline(); 344 };