1 /**************************************************************************** 2 Copyright (c) 2008-2010 Ricardo Quesada 3 Copyright (c) 2011-2012 cocos2d-x.org 4 Copyright (c) 2013-2014 Chukong Technologies Inc. 5 6 http://www.cocos2d-x.org 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 ****************************************************************************/ 26 27 /** 28 * <p>cc.Sprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) ) <br/> 29 * 30 * cc.Sprite can be created with an image, or with a sub-rectangle of an image. <br/> 31 * 32 * If the parent or any of its ancestors is a cc.SpriteBatchNode then the following features/limitations are valid <br/> 33 * - Features when the parent is a cc.BatchNode: <br/> 34 * - MUCH faster rendering, specially if the cc.SpriteBatchNode has many children. All the children will be drawn in a single batch. <br/> 35 * 36 * - Limitations <br/> 37 * - Camera is not supported yet (eg: CCOrbitCamera action doesn't work) <br/> 38 * - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl) <br/> 39 * - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property. <br/> 40 * - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property. <br/> 41 * - Parallax scroller is not supported, but can be simulated with a "proxy" sprite. <br/> 42 * 43 * If the parent is an standard cc.Node, then cc.Sprite behaves like any other cc.Node: <br/> 44 * - It supports blending functions <br/> 45 * - It supports aliasing / antialiasing <br/> 46 * - But the rendering will be slower: 1 draw per children. <br/> 47 * 48 * The default anchorPoint in cc.Sprite is (0.5, 0.5). </p> 49 * @class 50 * @extends cc.Node 51 * 52 * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName The string which indicates a path to image file, e.g., "scene1/monster.png". 53 * @param {cc.Rect} rect Only the contents inside rect of pszFileName's texture will be applied for this sprite. 54 * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. 55 * @example 56 * 57 * 1.Create a sprite with image path and rect 58 * var sprite1 = new cc.Sprite("res/HelloHTML5World.png"); 59 * var sprite2 = new cc.Sprite("res/HelloHTML5World.png",cc.rect(0,0,480,320)); 60 * 61 * 2.Create a sprite with a sprite frame name. Must add "#" before frame name. 62 * var sprite = new cc.Sprite('#grossini_dance_01.png'); 63 * 64 * 3.Create a sprite with a sprite frame 65 * var spriteFrame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); 66 * var sprite = new cc.Sprite(spriteFrame); 67 * 68 * 4.Create a sprite with an existing texture contained in a CCTexture2D object 69 * After creation, the rect will be the size of the texture, and the offset will be (0,0). 70 * var texture = cc.textureCache.addImage("HelloHTML5World.png"); 71 * var sprite1 = new cc.Sprite(texture); 72 * var sprite2 = new cc.Sprite(texture, cc.rect(0,0,480,320)); 73 * 74 * @property {Boolean} dirty - Indicates whether the sprite needs to be updated. 75 * @property {Boolean} flippedX - Indicates whether or not the sprite is flipped on x axis. 76 * @property {Boolean} flippedY - Indicates whether or not the sprite is flipped on y axis. 77 * @property {Number} offsetX - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex. 78 * @property {Number} offsetY - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex. 79 * @property {Number} atlasIndex - The index used on the TextureAtlas. 80 * @property {cc.Texture2D} texture - Texture used to render the sprite. 81 * @property {Boolean} textureRectRotated - <@readonly> Indicate whether the texture rectangle is rotated. 82 * @property {cc.TextureAtlas} textureAtlas - The weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode. 83 * @property {cc.SpriteBatchNode} batchNode - The batch node object if this sprite is rendered by cc.SpriteBatchNode. 84 * @property {cc.V3F_C4B_T2F_Quad} quad - <@readonly> The quad (tex coords, vertex coords and color) information. 85 */ 86 cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{ 87 dirty:false, 88 atlasIndex:0, 89 textureAtlas:null, 90 91 _batchNode:null, 92 _recursiveDirty:null, //Whether all of the sprite's children needs to be updated 93 _hasChildren:null, //Whether the sprite contains children 94 _shouldBeHidden:false, //should not be drawn because one of the ancestors is not visible 95 _transformToBatch:null, 96 97 // 98 // Data used when the sprite is self-rendered 99 // 100 _blendFunc:null, //It's required for CCTextureProtocol inheritance 101 _texture:null, //cc.Texture2D object that is used to render the sprite 102 103 // 104 // Shared data 105 // 106 // texture 107 _rect:null, //Rectangle of cc.Texture2D 108 _rectRotated:false, //Whether the texture is rotated 109 110 // Offset Position (used by Zwoptex) 111 _offsetPosition:null, // absolute 112 _unflippedOffsetPositionFromCenter:null, 113 114 _opacityModifyRGB:false, 115 116 // image is flipped 117 _flippedX:false, //Whether the sprite is flipped horizontally or not. 118 _flippedY:false, //Whether the sprite is flipped vertically or not. 119 120 _textureLoaded:false, 121 _className:"Sprite", 122 123 ctor: function (fileName, rect, rotated) { 124 var self = this; 125 cc.Node.prototype.ctor.call(self); 126 self._shouldBeHidden = false; 127 self._offsetPosition = cc.p(0, 0); 128 self._unflippedOffsetPositionFromCenter = cc.p(0, 0); 129 self._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; 130 self._rect = cc.rect(0, 0, 0, 0); 131 132 self._softInit(fileName, rect, rotated); 133 }, 134 135 /** 136 * Returns whether the texture have been loaded 137 * @returns {boolean} 138 */ 139 textureLoaded:function(){ 140 return this._textureLoaded; 141 }, 142 143 /** 144 * Add a event listener for texture loaded event. 145 * @param {Function} callback 146 * @param {Object} target 147 * @deprecated since 3.1, please use addEventListener instead 148 */ 149 addLoadedEventListener:function(callback, target){ 150 this.addEventListener("load", callback, target); 151 }, 152 153 /** 154 * Returns whether or not the Sprite needs to be updated in the Atlas 155 * @return {Boolean} True if the sprite needs to be updated in the Atlas, false otherwise. 156 */ 157 isDirty:function () { 158 return this.dirty; 159 }, 160 161 /** 162 * Makes the sprite to be updated in the Atlas. 163 * @param {Boolean} bDirty 164 */ 165 setDirty:function (bDirty) { 166 this.dirty = bDirty; 167 }, 168 169 /** 170 * Returns whether or not the texture rectangle is rotated. 171 * @return {Boolean} 172 */ 173 isTextureRectRotated:function () { 174 return this._rectRotated; 175 }, 176 177 /** 178 * Returns the index used on the TextureAtlas. 179 * @return {Number} 180 */ 181 getAtlasIndex:function () { 182 return this.atlasIndex; 183 }, 184 185 /** 186 * Sets the index used on the TextureAtlas. 187 * @warning Don't modify this value unless you know what you are doing 188 * @param {Number} atlasIndex 189 */ 190 setAtlasIndex:function (atlasIndex) { 191 this.atlasIndex = atlasIndex; 192 }, 193 194 /** 195 * Returns the rect of the cc.Sprite in points 196 * @return {cc.Rect} 197 */ 198 getTextureRect:function () { 199 return cc.rect(this._rect); 200 }, 201 202 /** 203 * Returns the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode 204 * @return {cc.TextureAtlas} 205 */ 206 getTextureAtlas:function () { 207 return this.textureAtlas; 208 }, 209 210 /** 211 * Sets the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode 212 * @param {cc.TextureAtlas} textureAtlas 213 */ 214 setTextureAtlas:function (textureAtlas) { 215 this.textureAtlas = textureAtlas; 216 }, 217 218 /** 219 * Returns the offset position of the sprite. Calculated automatically by editors like Zwoptex. 220 * @return {cc.Point} 221 */ 222 getOffsetPosition:function () { 223 return cc.p(this._offsetPosition); 224 }, 225 226 _getOffsetX: function () { 227 return this._offsetPosition.x; 228 }, 229 _getOffsetY: function () { 230 return this._offsetPosition.y; 231 }, 232 233 /** 234 * Returns the blend function 235 * @return {cc.BlendFunc} 236 */ 237 getBlendFunc:function () { 238 return this._blendFunc; 239 }, 240 241 /** 242 * Initializes a sprite with a SpriteFrame. The texture and rect in SpriteFrame will be applied on this sprite.<br/> 243 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself, 244 * @param {cc.SpriteFrame} spriteFrame A CCSpriteFrame object. It should includes a valid texture and a rect 245 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 246 */ 247 initWithSpriteFrame:function (spriteFrame) { 248 cc.assert(spriteFrame, cc._LogInfos.Sprite_initWithSpriteFrame); 249 250 if(!spriteFrame.textureLoaded()){ 251 //add event listener 252 this._textureLoaded = false; 253 spriteFrame.addEventListener("load", this._renderCmd._spriteFrameLoadedCallback, this); 254 } 255 256 //TODO 257 var rotated = cc._renderType === cc._RENDER_TYPE_CANVAS ? false : spriteFrame._rotated; 258 var ret = this.initWithTexture(spriteFrame.getTexture(), spriteFrame.getRect(), rotated); 259 this.setSpriteFrame(spriteFrame); 260 261 return ret; 262 }, 263 264 /** 265 * Initializes a sprite with a sprite frame name. <br/> 266 * A cc.SpriteFrame will be fetched from the cc.SpriteFrameCache by name. <br/> 267 * If the cc.SpriteFrame doesn't exist it will raise an exception. <br/> 268 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 269 * @param {String} spriteFrameName A key string that can fected a valid cc.SpriteFrame from cc.SpriteFrameCache 270 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 271 * @example 272 * var sprite = new cc.Sprite(); 273 * sprite.initWithSpriteFrameName("grossini_dance_01.png"); 274 */ 275 initWithSpriteFrameName:function (spriteFrameName) { 276 cc.assert(spriteFrameName, cc._LogInfos.Sprite_initWithSpriteFrameName); 277 var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName); 278 cc.assert(frame, spriteFrameName + cc._LogInfos.Sprite_initWithSpriteFrameName1); 279 return this.initWithSpriteFrame(frame); 280 }, 281 282 /** 283 * Tell the sprite to use batch node render. 284 * @param {cc.SpriteBatchNode} batchNode 285 */ 286 useBatchNode:function (batchNode) { 287 this.textureAtlas = batchNode.getTextureAtlas(); // weak ref 288 this._batchNode = batchNode; 289 }, 290 291 /** 292 * <p> 293 * set the vertex rect.<br/> 294 * It will be called internally by setTextureRect. <br/> 295 * Useful if you want to create 2x images from SD images in Retina Display. <br/> 296 * Do not call it manually. Use setTextureRect instead. <br/> 297 * (override this method to generate "double scale" sprites) 298 * </p> 299 * @param {cc.Rect} rect 300 */ 301 setVertexRect:function (rect) { 302 var locRect = this._rect; 303 locRect.x = rect.x; 304 locRect.y = rect.y; 305 locRect.width = rect.width; 306 locRect.height = rect.height; 307 }, 308 309 /** 310 * Sort all children of this sprite node. 311 * @override 312 */ 313 sortAllChildren:function () { 314 if (this._reorderChildDirty) { 315 var _children = this._children; 316 317 // insertion sort 318 var len = _children.length, i, j, tmp; 319 for(i=1; i<len; i++){ 320 tmp = _children[i]; 321 j = i - 1; 322 323 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller 324 while(j >= 0){ 325 if(tmp._localZOrder < _children[j]._localZOrder){ 326 _children[j+1] = _children[j]; 327 }else if(tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder){ 328 _children[j+1] = _children[j]; 329 }else{ 330 break; 331 } 332 j--; 333 } 334 _children[j+1] = tmp; 335 } 336 337 if (this._batchNode) { 338 this._arrayMakeObjectsPerformSelector(_children, cc.Node._stateCallbackType.sortAllChildren); 339 } 340 341 //don't need to check children recursively, that's done in visit of each child 342 this._reorderChildDirty = false; 343 } 344 345 }, 346 347 /** 348 * Reorders a child according to a new z value. (override cc.Node ) 349 * @param {cc.Node} child 350 * @param {Number} zOrder 351 * @override 352 */ 353 reorderChild:function (child, zOrder) { 354 cc.assert(child, cc._LogInfos.Sprite_reorderChild_2); 355 if(this._children.indexOf(child) === -1){ 356 cc.log(cc._LogInfos.Sprite_reorderChild); 357 return; 358 } 359 360 if (zOrder === child.zIndex) 361 return; 362 363 if (this._batchNode && !this._reorderChildDirty) { 364 this._setReorderChildDirtyRecursively(); 365 this._batchNode.reorderBatch(true); 366 } 367 cc.Node.prototype.reorderChild.call(this, child, zOrder); 368 }, 369 370 /** 371 * Removes a child from the sprite. 372 * @param child 373 * @param cleanup whether or not cleanup all running actions 374 * @override 375 */ 376 removeChild:function (child, cleanup) { 377 if (this._batchNode) 378 this._batchNode.removeSpriteFromAtlas(child); 379 cc.Node.prototype.removeChild.call(this, child, cleanup); 380 }, 381 382 /** 383 * Sets whether the sprite is visible or not. 384 * @param {Boolean} visible 385 * @override 386 */ 387 setVisible:function (visible) { 388 cc.Node.prototype.setVisible.call(this, visible); 389 this._renderCmd.setDirtyRecursively(true); 390 }, 391 392 /** 393 * Removes all children from the container. 394 * @param cleanup whether or not cleanup all running actions 395 * @override 396 */ 397 removeAllChildren:function (cleanup) { 398 var locChildren = this._children, locBatchNode = this._batchNode; 399 if (locBatchNode && locChildren != null) { 400 for (var i = 0, len = locChildren.length; i < len; i++) 401 locBatchNode.removeSpriteFromAtlas(locChildren[i]); 402 } 403 404 cc.Node.prototype.removeAllChildren.call(this, cleanup); 405 this._hasChildren = false; 406 }, 407 408 // 409 // cc.Node property overloads 410 // 411 412 /** 413 * Sets whether ignore anchor point for positioning 414 * @param {Boolean} relative 415 * @override 416 */ 417 ignoreAnchorPointForPosition:function (relative) { 418 if(this._batchNode){ 419 cc.log(cc._LogInfos.Sprite_ignoreAnchorPointForPosition); 420 return; 421 } 422 cc.Node.prototype.ignoreAnchorPointForPosition.call(this, relative); 423 }, 424 425 /** 426 * Sets whether the sprite should be flipped horizontally or not. 427 * @param {Boolean} flippedX true if the sprite should be flipped horizontally, false otherwise. 428 */ 429 setFlippedX:function (flippedX) { 430 if (this._flippedX !== flippedX) { 431 this._flippedX = flippedX; 432 this.setTextureRect(this._rect, this._rectRotated, this._contentSize); 433 this.setNodeDirty(true); 434 } 435 }, 436 437 /** 438 * Sets whether the sprite should be flipped vertically or not. 439 * @param {Boolean} flippedY true if the sprite should be flipped vertically, false otherwise. 440 */ 441 setFlippedY:function (flippedY) { 442 if (this._flippedY !== flippedY) { 443 this._flippedY = flippedY; 444 this.setTextureRect(this._rect, this._rectRotated, this._contentSize); 445 this.setNodeDirty(true); 446 } 447 }, 448 449 /** 450 * <p> 451 * Returns the flag which indicates whether the sprite is flipped horizontally or not. <br/> 452 * <br/> 453 * It only flips the texture of the sprite, and not the texture of the sprite's children. <br/> 454 * Also, flipping the texture doesn't alter the anchorPoint. <br/> 455 * If you want to flip the anchorPoint too, and/or to flip the children too use: <br/> 456 * sprite.setScaleX(sprite.getScaleX() * -1); <p/> 457 * @return {Boolean} true if the sprite is flipped horizontally, false otherwise. 458 */ 459 isFlippedX:function () { 460 return this._flippedX; 461 }, 462 463 /** 464 * <p> 465 * Return the flag which indicates whether the sprite is flipped vertically or not. <br/> 466 * <br/> 467 * It only flips the texture of the sprite, and not the texture of the sprite's children. <br/> 468 * Also, flipping the texture doesn't alter the anchorPoint. <br/> 469 * If you want to flip the anchorPoint too, and/or to flip the children too use: <br/> 470 * sprite.setScaleY(sprite.getScaleY() * -1); <p/> 471 * @return {Boolean} true if the sprite is flipped vertically, false otherwise. 472 */ 473 isFlippedY:function () { 474 return this._flippedY; 475 }, 476 477 // 478 // RGBA protocol 479 // 480 /** 481 * Sets whether opacity modify color or not. 482 * @function 483 * @param {Boolean} modify 484 */ 485 setOpacityModifyRGB: function (modify) { 486 if (this._opacityModifyRGB !== modify) { 487 this._opacityModifyRGB = modify; 488 this._renderCmd._setColorDirty(); 489 } 490 }, 491 492 /** 493 * Returns whether opacity modify color or not. 494 * @return {Boolean} 495 */ 496 isOpacityModifyRGB:function () { 497 return this._opacityModifyRGB; 498 }, 499 500 // Animation 501 502 /** 503 * Changes the display frame with animation name and index.<br/> 504 * The animation name will be get from the CCAnimationCache 505 * @param {String} animationName 506 * @param {Number} frameIndex 507 */ 508 setDisplayFrameWithAnimationName:function (animationName, frameIndex) { 509 cc.assert(animationName, cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_3); 510 511 var cache = cc.animationCache.getAnimation(animationName); 512 if(!cache){ 513 cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName); 514 return; 515 } 516 var animFrame = cache.getFrames()[frameIndex]; 517 if(!animFrame){ 518 cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_2); 519 return; 520 } 521 this.setSpriteFrame(animFrame.getSpriteFrame()); 522 }, 523 524 /** 525 * Returns the batch node object if this sprite is rendered by cc.SpriteBatchNode 526 * @returns {cc.SpriteBatchNode|null} The cc.SpriteBatchNode object if this sprite is rendered by cc.SpriteBatchNode, null if the sprite isn't used batch node. 527 */ 528 getBatchNode:function () { 529 return this._batchNode; 530 }, 531 532 _setReorderChildDirtyRecursively:function () { 533 //only set parents flag the first time 534 if (!this._reorderChildDirty) { 535 this._reorderChildDirty = true; 536 var pNode = this._parent; 537 while (pNode && pNode !== this._batchNode) { 538 pNode._setReorderChildDirtyRecursively(); 539 pNode = pNode.parent; 540 } 541 } 542 }, 543 544 // CCTextureProtocol 545 /** 546 * Returns the texture of the sprite node 547 * @returns {cc.Texture2D} 548 */ 549 getTexture:function () { 550 return this._texture; 551 }, 552 553 _softInit: function (fileName, rect, rotated) { 554 if (fileName === undefined) 555 cc.Sprite.prototype.init.call(this); 556 else if (cc.isString(fileName)) { 557 if (fileName[0] === "#") { 558 // Init with a sprite frame name 559 var frameName = fileName.substr(1, fileName.length - 1); 560 var spriteFrame = cc.spriteFrameCache.getSpriteFrame(frameName); 561 if (spriteFrame) 562 this.initWithSpriteFrame(spriteFrame); 563 else 564 cc.log("%s does not exist", fileName); 565 } else { 566 // Init with filename and rect 567 cc.Sprite.prototype.init.call(this, fileName, rect); 568 } 569 } else if (typeof fileName === "object") { 570 if (fileName instanceof cc.Texture2D) { 571 // Init with texture and rect 572 this.initWithTexture(fileName, rect, rotated); 573 } else if (fileName instanceof cc.SpriteFrame) { 574 // Init with a sprite frame 575 this.initWithSpriteFrame(fileName); 576 } else if ((fileName instanceof HTMLImageElement) || (fileName instanceof HTMLCanvasElement)) { 577 // Init with a canvas or image element 578 var texture2d = new cc.Texture2D(); 579 texture2d.initWithElement(fileName); 580 texture2d.handleLoadedTexture(); 581 this.initWithTexture(texture2d); 582 } 583 } 584 }, 585 586 /** 587 * Returns the quad (tex coords, vertex coords and color) information. 588 * @return {cc.V3F_C4B_T2F_Quad|null} Returns a cc.V3F_C4B_T2F_Quad object when render mode is WebGL, returns null when render mode is Canvas. 589 */ 590 getQuad:function () { 591 return this._renderCmd.getQuad(); 592 }, 593 594 /** 595 * conforms to cc.TextureProtocol protocol 596 * @function 597 * @param {Number|cc.BlendFunc} src 598 * @param {Number} dst 599 */ 600 setBlendFunc: function (src, dst) { 601 var locBlendFunc = this._blendFunc; 602 if (dst === undefined) { 603 locBlendFunc.src = src.src; 604 locBlendFunc.dst = src.dst; 605 } else { 606 locBlendFunc.src = src; 607 locBlendFunc.dst = dst; 608 } 609 this._renderCmd.updateBlendFunc(locBlendFunc); 610 }, 611 612 /** 613 * Initializes an empty sprite with nothing init.<br/> 614 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 615 * @function 616 * @return {Boolean} 617 */ 618 init: function () { 619 var _t = this; 620 if (arguments.length > 0) 621 return _t.initWithFile(arguments[0], arguments[1]); 622 623 cc.Node.prototype.init.call(_t); 624 _t.dirty = _t._recursiveDirty = false; 625 626 _t._blendFunc.src = cc.BLEND_SRC; 627 _t._blendFunc.dst = cc.BLEND_DST; 628 629 _t.texture = null; 630 _t._flippedX = _t._flippedY = false; 631 632 // default transform anchor: center 633 _t.anchorX = 0.5; 634 _t.anchorY = 0.5; 635 636 // zwoptex default values 637 _t._offsetPosition.x = 0; 638 _t._offsetPosition.y = 0; 639 _t._hasChildren = false; 640 641 this._renderCmd._init(); 642 // updated in "useSelfRender" 643 // Atlas: TexCoords 644 _t.setTextureRect(cc.rect(0, 0, 0, 0), false, cc.size(0, 0)); 645 return true; 646 }, 647 648 /** 649 * <p> 650 * Initializes a sprite with an image filename.<br/> 651 * 652 * This method will find pszFilename from local file system, load its content to CCTexture2D,<br/> 653 * then use CCTexture2D to create a sprite.<br/> 654 * After initialization, the rect used will be the size of the image. The offset will be (0,0).<br/> 655 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 656 * </p> 657 * @param {String} filename The path to an image file in local file system 658 * @param {cc.Rect} rect The rectangle assigned the content area from texture. 659 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 660 */ 661 initWithFile:function (filename, rect) { 662 cc.assert(filename, cc._LogInfos.Sprite_initWithFile); 663 664 var tex = cc.textureCache.getTextureForKey(filename); 665 if (!tex) { 666 tex = cc.textureCache.addImage(filename); 667 return this.initWithTexture(tex, rect || cc.rect(0, 0, tex._contentSize.width, tex._contentSize.height)); 668 } else { 669 if (!rect) { 670 var size = tex.getContentSize(); 671 rect = cc.rect(0, 0, size.width, size.height); 672 } 673 return this.initWithTexture(tex, rect); 674 } 675 }, 676 677 /** 678 * Initializes a sprite with a texture and a rect in points, optionally rotated. <br/> 679 * After initialization, the rect used will be the size of the texture, and the offset will be (0,0).<br/> 680 * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. 681 * @function 682 * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture A pointer to an existing CCTexture2D object. You can use a CCTexture2D object for many sprites. 683 * @param {cc.Rect} [rect] Only the contents inside rect of this texture will be applied for this sprite. 684 * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. 685 * @param {Boolean} [counterclockwise=true] Whether or not the texture rectangle rotation is counterclockwise (texture package is counterclockwise, spine is clockwise). 686 * @return {Boolean} true if the sprite is initialized properly, false otherwise. 687 */ 688 initWithTexture: function (texture, rect, rotated, counterclockwise) { 689 var _t = this; 690 cc.assert(arguments.length !== 0, cc._LogInfos.CCSpriteBatchNode_initWithTexture); 691 692 rotated = rotated || false; 693 texture = this._renderCmd._handleTextureForRotatedTexture(texture, rect, rotated, counterclockwise); 694 695 if (!cc.Node.prototype.init.call(_t)) 696 return false; 697 698 _t._batchNode = null; 699 _t._recursiveDirty = false; 700 _t.dirty = false; 701 _t._opacityModifyRGB = true; 702 703 _t._blendFunc.src = cc.BLEND_SRC; 704 _t._blendFunc.dst = cc.BLEND_DST; 705 706 _t._flippedX = _t._flippedY = false; 707 708 // default transform anchor: center 709 _t.setAnchorPoint(0.5, 0.5); 710 711 // zwoptex default values 712 _t._offsetPosition.x = 0; 713 _t._offsetPosition.y = 0; 714 _t._hasChildren = false; 715 716 this._renderCmd._init(); 717 718 var locTextureLoaded = texture.isLoaded(); 719 _t._textureLoaded = locTextureLoaded; 720 721 if (!locTextureLoaded) { 722 _t._rectRotated = rotated; 723 if (rect) { 724 _t._rect.x = rect.x; 725 _t._rect.y = rect.y; 726 _t._rect.width = rect.width; 727 _t._rect.height = rect.height; 728 } 729 if(_t.texture) 730 _t.texture.removeEventListener("load", _t); 731 texture.addEventListener("load", _t._renderCmd._textureLoadedCallback, _t); 732 _t.texture = texture; 733 return true; 734 } 735 736 if (!rect) 737 rect = cc.rect(0, 0, texture.width, texture.height); 738 739 this._renderCmd._checkTextureBoundary(texture, rect, rotated); 740 741 _t.setTexture(texture); 742 _t.setTextureRect(rect, rotated); 743 744 // by default use "Self Render". 745 // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" 746 _t.setBatchNode(null); 747 return true; 748 }, 749 750 /** 751 * Updates the texture rect of the CCSprite in points. 752 * @function 753 * @param {cc.Rect} rect a rect of texture 754 * @param {Boolean} [rotated] Whether or not the texture is rotated 755 * @param {cc.Size} [untrimmedSize] The original pixels size of the texture 756 */ 757 setTextureRect: function (rect, rotated, untrimmedSize, needConvert) { 758 var _t = this; 759 _t._rectRotated = rotated || false; 760 _t.setContentSize(untrimmedSize || rect); 761 762 _t.setVertexRect(rect); 763 _t._renderCmd._setTextureCoords(rect, needConvert); 764 765 var relativeOffsetX = _t._unflippedOffsetPositionFromCenter.x, relativeOffsetY = _t._unflippedOffsetPositionFromCenter.y; 766 if (_t._flippedX) 767 relativeOffsetX = -relativeOffsetX; 768 if (_t._flippedY) 769 relativeOffsetY = -relativeOffsetY; 770 var locRect = _t._rect; 771 _t._offsetPosition.x = relativeOffsetX + (_t._contentSize.width - locRect.width) / 2; 772 _t._offsetPosition.y = relativeOffsetY + (_t._contentSize.height - locRect.height) / 2; 773 774 // rendering using batch node 775 if (_t._batchNode) { 776 // update dirty, don't update _recursiveDirty 777 _t.dirty = true; 778 } else { 779 // self rendering 780 // Atlas: Vertex 781 this._renderCmd._resetForBatchNode(); 782 } 783 }, 784 785 // BatchNode methods 786 /** 787 * Updates the quad according the the rotation, position, scale values. 788 * @function 789 */ 790 updateTransform: function(){ 791 this._renderCmd.updateTransform(); 792 }, 793 794 /** 795 * Add child to sprite (override cc.Node) 796 * @function 797 * @param {cc.Sprite} child 798 * @param {Number} localZOrder child's zOrder 799 * @param {String} [tag] child's tag 800 * @override 801 */ 802 addChild: function (child, localZOrder, tag) { 803 cc.assert(child, cc._LogInfos.CCSpriteBatchNode_addChild_2); 804 805 if (localZOrder == null) 806 localZOrder = child._localZOrder; 807 if (tag == null) 808 tag = child.tag; 809 810 if(this._renderCmd._setBatchNodeForAddChild(child)){ 811 //cc.Node already sets isReorderChildDirty_ so this needs to be after batchNode check 812 cc.Node.prototype.addChild.call(this, child, localZOrder, tag); 813 this._hasChildren = true; 814 } 815 }, 816 817 // Frames 818 /** 819 * Sets a new sprite frame to the sprite. 820 * @function 821 * @param {cc.SpriteFrame|String} newFrame 822 */ 823 setSpriteFrame: function (newFrame) { 824 var _t = this; 825 if(cc.isString(newFrame)){ 826 newFrame = cc.spriteFrameCache.getSpriteFrame(newFrame); 827 cc.assert(newFrame, cc._LogInfos.Sprite_setSpriteFrame) 828 } 829 830 this.setNodeDirty(true); 831 832 var frameOffset = newFrame.getOffset(); 833 _t._unflippedOffsetPositionFromCenter.x = frameOffset.x; 834 _t._unflippedOffsetPositionFromCenter.y = frameOffset.y; 835 836 // update rect 837 var pNewTexture = newFrame.getTexture(); 838 var locTextureLoaded = newFrame.textureLoaded(); 839 if (!locTextureLoaded) { 840 _t._textureLoaded = false; 841 newFrame.addEventListener("load", function (sender) { 842 _t._textureLoaded = true; 843 var locNewTexture = sender.getTexture(); 844 if (locNewTexture !== _t._texture) 845 _t.texture = locNewTexture; 846 _t.setTextureRect(sender.getRect(), sender.isRotated(), sender.getOriginalSize()); 847 _t.dispatchEvent("load"); 848 _t.setColor(_t.color); 849 }, _t); 850 }else{ 851 // update texture before updating texture rect 852 if (pNewTexture !== _t._texture) 853 _t.texture = pNewTexture; 854 _t.setTextureRect(newFrame.getRect(), newFrame.isRotated(), newFrame.getOriginalSize()); 855 } 856 this._renderCmd._updateForSetSpriteFrame(pNewTexture); 857 }, 858 859 /** 860 * Sets a new display frame to the sprite. 861 * @param {cc.SpriteFrame|String} newFrame 862 * @deprecated 863 */ 864 setDisplayFrame: function(newFrame){ 865 cc.log(cc._LogInfos.Sprite_setDisplayFrame); 866 this.setSpriteFrame(newFrame); 867 }, 868 869 /** 870 * Returns whether or not a cc.SpriteFrame is being displayed 871 * @function 872 * @param {cc.SpriteFrame} frame 873 * @return {Boolean} 874 */ 875 isFrameDisplayed: function(frame){ 876 return this._renderCmd.isFrameDisplayed(frame); 877 }, 878 879 /** 880 * Returns the current displayed frame. 881 * @deprecated since 3.4, please use getSpriteFrame instead 882 * @return {cc.SpriteFrame} 883 */ 884 displayFrame: function () { 885 return this.getSpriteFrame(); 886 }, 887 888 /** 889 * Returns the current displayed frame. 890 * @return {cc.SpriteFrame} 891 */ 892 getSpriteFrame: function () { 893 return new cc.SpriteFrame(this._texture, 894 cc.rectPointsToPixels(this._rect), 895 this._rectRotated, 896 cc.pointPointsToPixels(this._unflippedOffsetPositionFromCenter), 897 cc.sizePointsToPixels(this._contentSize)); 898 }, 899 900 /** 901 * Sets the batch node to sprite 902 * @function 903 * @param {cc.SpriteBatchNode|null} spriteBatchNode 904 * @example 905 * var batch = new cc.SpriteBatchNode("Images/grossini_dance_atlas.png", 15); 906 * var sprite = new cc.Sprite(batch.texture, cc.rect(0, 0, 57, 57)); 907 * batch.addChild(sprite); 908 * layer.addChild(batch); 909 */ 910 setBatchNode:function (spriteBatchNode) { 911 var _t = this; 912 _t._batchNode = spriteBatchNode; // weak reference 913 914 // self render 915 if (!_t._batchNode) { 916 _t.atlasIndex = cc.Sprite.INDEX_NOT_INITIALIZED; 917 _t.textureAtlas = null; 918 _t._recursiveDirty = false; 919 _t.dirty = false; 920 921 this._renderCmd._resetForBatchNode(); 922 } else { 923 // using batch 924 _t._transformToBatch = cc.affineTransformIdentity(); 925 _t.textureAtlas = _t._batchNode.getTextureAtlas(); // weak ref 926 } 927 }, 928 929 // CCTextureProtocol 930 /** 931 * Sets the texture of sprite 932 * @function 933 * @param {cc.Texture2D|String} texture 934 */ 935 setTexture: function (texture) { 936 if(!texture) 937 return this._renderCmd._setTexture(null); 938 939 var oldTexture = this._texture; 940 if(cc.isString(texture)){ 941 texture = cc.textureCache.addImage(texture); 942 943 if(!texture._textureLoaded){ 944 texture.addEventListener("load", function(){ 945 this._renderCmd._setTexture(texture); 946 this._changeRectWithTexture(texture, oldTexture); 947 this.setColor(this._realColor); 948 this._textureLoaded = true; 949 }, this); 950 }else{ 951 this._renderCmd._setTexture(texture); 952 this._changeRectWithTexture(texture, oldTexture); 953 this.setColor(this._realColor); 954 this._textureLoaded = true; 955 } 956 }else{ 957 // CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteSheet 958 cc.assert(texture instanceof cc.Texture2D, cc._LogInfos.Sprite_setTexture_2); 959 this._changeRectWithTexture(texture, oldTexture); 960 this._renderCmd._setTexture(texture); 961 } 962 }, 963 964 _changeRectWithTexture: function(texture, oldTexture){ 965 var textureRect = cc.rect(0, 0, texture._contentSize.width, texture._contentSize.height), 966 oldTextureContentSize = oldTexture ? oldTexture._contentSize : cc.size(), 967 nodeContentSize = this._contentSize; 968 969 var textureWidth = textureRect.width, 970 textureHeight = textureRect.height, 971 oldTextureWidth = oldTextureContentSize.width, 972 oldTextureHeight = oldTextureContentSize.height, 973 nodeWidth = nodeContentSize.width, 974 nodeHeight = nodeContentSize.height; 975 976 if(!textureRect || (!textureWidth && !textureHeight)) return; 977 var nodeRect = this._rect; 978 if( 979 oldTexture && 980 // If the contentSize does not exist, Set the contentSize 981 (nodeWidth !== 0 && nodeHeight !== 0) && 982 // To satisfy the above two, But height/width does not exist, Set the contentSize 983 (nodeRect.height !== 0 || nodeRect.width !== 0) 984 // The remaining direct return 985 ){ 986 //Sprite in updateColor, Will generate a new texture without URL 987 //To replace this texture, no need to update rect 988 if(texture.url){ 989 if( 990 nodeWidth !== oldTextureWidth && nodeHeight !== oldTextureHeight && 991 oldTextureWidth === textureWidth && oldTextureHeight === textureHeight 992 ) 993 return; 994 }else{ 995 return; 996 } 997 } 998 textureRect.x = textureRect.x || 0; 999 textureRect.y = textureRect.y || 0; 1000 textureRect.width = textureRect.width || 0; 1001 textureRect.height = textureRect.height || 0; 1002 this.setTextureRect(textureRect); 1003 }, 1004 1005 _createRenderCmd: function(){ 1006 if(cc._renderType === cc._RENDER_TYPE_CANVAS) 1007 return new cc.Sprite.CanvasRenderCmd(this); 1008 else 1009 return new cc.Sprite.WebGLRenderCmd(this); 1010 } 1011 }); 1012 1013 /** 1014 * Create a sprite with image path or frame name or texture or spriteFrame. 1015 * @deprecated since v3.0, please use new construction instead 1016 * @see cc.Sprite 1017 * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName The string which indicates a path to image file, e.g., "scene1/monster.png". 1018 * @param {cc.Rect} rect Only the contents inside rect of pszFileName's texture will be applied for this sprite. 1019 * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. 1020 * @return {cc.Sprite} A valid sprite object 1021 */ 1022 cc.Sprite.create = function (fileName, rect, rotated) { 1023 return new cc.Sprite(fileName, rect, rotated); 1024 }; 1025 1026 /** 1027 * @deprecated since v3.0, please use new construction instead 1028 * @see cc.Sprite 1029 * @function 1030 */ 1031 cc.Sprite.createWithTexture = cc.Sprite.create; 1032 1033 /** 1034 * @deprecated since v3.0, please use new construction instead 1035 * @see cc.Sprite 1036 * @function 1037 */ 1038 cc.Sprite.createWithSpriteFrameName = cc.Sprite.create; 1039 1040 /** 1041 * @deprecated since v3.0, please use new construction instead 1042 * @see cc.Sprite 1043 * @function 1044 */ 1045 cc.Sprite.createWithSpriteFrame = cc.Sprite.create; 1046 /** 1047 * cc.Sprite invalid index on the cc.SpriteBatchNode 1048 * @constant 1049 * @type {Number} 1050 */ 1051 cc.Sprite.INDEX_NOT_INITIALIZED = -1; 1052 1053 cc.EventHelper.prototype.apply(cc.Sprite.prototype); 1054 1055 cc.assert(cc.isFunction(cc._tmp.PrototypeSprite), cc._LogInfos.MissingFile, "SpritesPropertyDefine.js"); 1056 cc._tmp.PrototypeSprite(); 1057 delete cc._tmp.PrototypeSprite;