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 if (frameIndex < this._frames[0].getFrameIndex()){ 204 if(this._currentKeyFrameIndex >= this._frames[0].getFrameIndex()) 205 needEnterFrame = true; 206 207 this._fromIndex = 0; 208 this._toIndex = 0; 209 210 from = to = this._frames[0]; 211 this._currentKeyFrameIndex = 0; 212 this._betweenDuration = this._frames[0].getFrameIndex(); 213 break; 214 }else if(frameIndex >= this._frames[length - 1].getFrameIndex()){ 215 this._fromIndex = length - 1; 216 this._toIndex = 0; 217 218 from = to = this._frames[length - 1]; 219 this._currentKeyFrameIndex = this._frames[length - 1].getFrameIndex(); 220 this._betweenDuration = 0; 221 break; 222 } 223 224 var target = -1; 225 var low = 0, 226 high = length - 1, 227 mid = 0; 228 while(low <= high){ 229 mid = Math.ceil(( low + high )/2); 230 if(frameIndex >= this._frames[mid].getFrameIndex() && frameIndex < this._frames[mid + 1].getFrameIndex()) 231 { 232 target = mid; 233 break; 234 } 235 if(this._frames[mid].getFrameIndex()>frameIndex) 236 high = mid - 1; 237 else 238 low = mid + 1; 239 } 240 241 this._fromIndex = target; 242 243 if(length > 1) 244 this._toIndex = (target + 1) | 0; 245 else 246 this._toIndex = (target) | 0; 247 248 from = this._frames[this._fromIndex]; 249 to = this._frames[this._toIndex]; 250 251 from = this._frames[target]; 252 to = this._frames[target+1]; 253 254 if(target === 0 && this._currentKeyFrameIndex < from.getFrameIndex()) 255 needEnterFrame = true; 256 257 this._currentKeyFrameIndex = from.getFrameIndex(); 258 this._betweenDuration = to.getFrameIndex() - from.getFrameIndex(); 259 } while (0); 260 261 if(needEnterFrame || this._currentKeyFrame != from) { 262 this._currentKeyFrame = from; 263 this._currentKeyFrame.onEnter(to); 264 } 265 266 }, 267 268 _updateCurrentKeyFrame: function(frameIndex){ 269 if(frameIndex > 60) 270 var a = 0; 271 //! If play to current frame's front or back, then find current frame again 272 if (frameIndex < this._currentKeyFrameIndex || frameIndex >= this._currentKeyFrameIndex + this._betweenDuration) 273 { 274 var from = null; 275 var to = null; 276 277 do 278 { 279 var length = this._frames.length; 280 281 if (frameIndex < this._frames[0].getFrameIndex()) 282 { 283 from = to = this._frames[0]; 284 this._currentKeyFrameIndex = 0; 285 this._betweenDuration = this._frames[0].getFrameIndex(); 286 break; 287 } 288 else if(frameIndex >= this._frames[length - 1].getFrameIndex()) 289 { 290 var lastFrameIndex = this._frames[length - 1].getFrameIndex(); 291 if(this._currentKeyFrameIndex >= lastFrameIndex) 292 return; 293 frameIndex = lastFrameIndex; 294 } 295 296 do{ 297 this._fromIndex = this._toIndex; 298 from = this._frames[this._fromIndex]; 299 this._currentKeyFrameIndex = from.getFrameIndex(); 300 301 this._toIndex = this._fromIndex + 1; 302 if (this._toIndex >= length) 303 { 304 this._toIndex = 0; 305 } 306 307 to = this._frames[this._toIndex]; 308 309 if (frameIndex === from.getFrameIndex()) 310 break; 311 if(frameIndex > from.getFrameIndex() && frameIndex < to.getFrameIndex()) 312 break; 313 if(from.isEnterWhenPassed()) 314 from.onEnter(to); 315 }while (true); 316 317 this._betweenDuration = to.getFrameIndex() - from.getFrameIndex(); 318 319 } while (0); 320 321 this._currentKeyFrame = from; 322 this._currentKeyFrame.onEnter(to); 323 } 324 } 325 326 }); 327 328 /** 329 * Create the Timeline 330 * 331 * @deprecated v3.0, please use new ccs.Timeline() instead. 332 * @returns {ccs.Timeline} 333 */ 334 ccs.Timeline.create = function(){ 335 return new ccs.Timeline(); 336 };