      1 /**
      2  * Hilo
      3  * Copyright 2015 alibaba.com
      4  * Licensed under the MIT License
      5  */
      6
      7 /**
      8  * @class TextureAtlas纹理集是将许多小的纹理图片整合到一起的一张大图。这个类可根据一个纹理集数据读取纹理小图、精灵动画等。
      9  * @param {Object} atlasData 纹理集数据。它可包含如下数据：
     10  * <ul>
     11  * <li><b>image</b> - 纹理集图片。必需。</li>
     12  * <li><b>width</b> - 纹理集图片宽度。若frames数据为Object时，此属性必需。</li>
     13  * <li><b>height</b> - 纹理集图片高度。若frames数据为Object时，此属性必需。</li>
     14  * <li><b>frames</b> - 纹理集帧数据，可以是Array或Object。必需。
     15  * <ul>
     16  * <li>若为Array，则每项均为一个纹理图片帧数据，如：[[0, 0, 50, 50], [0, 50, 50, 50]。</li>
     17  * <li>若为Object，则需包含frameWidth(帧宽)、frameHeight(帧高)、numFrames(帧数) 属性。</li>
     18  * </ul>
     19  * </li>
     20  * <li><b>sprites</b> - 纹理集精灵动画定义，其每个值均定义一个精灵。为Object对象。可选。
     21  * <ul>
     22  * <li>若为Number，即此精灵只包含一帧，此帧为帧数据中索引为当前值的帧。如：sprites:{'foo':1}。</li>
     23  * <li>若为Array，则每项均为一个帧的索引值。如：sprites:{'foo':[0, 1, 2, 3]}。</li>
     24  * <li>若为Object，则需包含from(起始帧索引值)、to(末帧索引值) 属性。</li>
     25  * </ul>
     26  * </li>
     27  * </ul>
     28  * @module hilo/util/TextureAtlas
     29  * @requires hilo/core/Class
     30  */
     31 var TextureAtlas = (function(){
     32
     33 return Class.create(/** @lends TextureAtlas.prototype */{
     34     constructor: function(atlasData){
     35         this._frames = parseTextureFrames(atlasData);
     36         this._sprites = parseTextureSprites(atlasData, this._frames);
     37     },
     38
     39     _frames: null,
     40     _sprites: null,
     41
     42     /**
     43      * 获取指定索引位置index的帧数据。
     44      * @param {Int} index 要获取帧的索引位置。
     45      * @returns {Object} 帧数据。
     46      */
     47     getFrame: function(index){
     48         var frames = this._frames;
     49         return frames && frames[index];
     50     },
     51
     52     /**
     53      * 获取指定id的精灵数据。
     54      * @param {String} id 要获取精灵的id。
     55      * @returns {Object} 精灵数据。
     56      */
     57     getSprite: function(id){
     58         var sprites = this._sprites;
     59         return sprites && sprites[id];
     60     },
     61
     62     Statics: /** @lends TextureAtlas */ {
     63         /**
     64          * 创建精灵帧数据的快捷方法。
     65          * @param {String|Array} name 动画名称|一组动画数据
     66          * @param {String} frames 帧数据 eg:"0-5"代表第0到第5帧
     67          * @param {Number} w 每帧的宽
     68          * @param {Number} h 每帧的高
     69          * @param {Boolean} loop 是否循环
     70          * @param {Number} duration 每帧间隔 默认单位帧, 如果sprite的timeBased为true则单位是毫秒，默认一帧
     71          * @example
     72          *  //方式一 单个动画
     73          *  createSpriteFrames("walk", "0-5,8,9", meImg, 55, 88, true, 1);
     74          *  //方式二 多组动画
     75          *  createSpriteFrames([
     76          *    ["walk", "0-5,8,9", meImg, 55, 88, true, 1],
     77          *    ["jump", "0-5", meImg, 55, 88, false, 1]
     78          *  ]);
     79         */
     80         createSpriteFrames:function(name, frames, img, w, h, loop, duration){
     81             var i, l;
     82             if(Object.prototype.toString.call(name) === "[object Array]"){
     83                 var res = [];
     84                 for(i = 0, l = name.length;i < l;i ++){
     85                     res = res.concat(this.createSpriteFrames.apply(this, name[i]));
     86                 }
     87                 return res;
     88             }
     89             else{
     90                 if(typeof(frames) === "string"){
     91                     var all = frames.split(",");
     92                     frames = [];
     93                     for(var j = 0, jl = all.length;j < jl;j ++){
     94                         var temp = all[j].split("-");
     95                         if(temp.length == 1){
     96                             frames.push(parseInt(temp[0]));
     97                         }
     98                         else{
     99                             for(i = parseInt(temp[0]), l = parseInt(temp[1]);i <= l;i ++){
    100                                 frames.push(i);
    101                             }
    102                         }
    103                     }
    104                 }
    105
    106                 var col = Math.floor(img.width/w);
    107                 for(i = 0;i < frames.length;i ++){
    108                     var n = frames[i];
    109                     frames[i] = {
    110                         rect:[w*(n%col), h*Math.floor(n/col), w, h],
    111                         image:img,
    112                         duration:duration
    113                     };
    114                 }
    115                 frames[0].name = name;
    116                 if(loop){
    117                     frames[frames.length-1].next = name;
    118                 }
    119                 else{
    120                     frames[frames.length-1].stop = true;
    121                 }
    122                 return frames;
    123             }
    124         }
    125     }
    126 });
    127
    128 /**
    129  * 解析纹理集帧数据。
    130  * @private
    131  */
    132 function parseTextureFrames(atlasData){
    133     var i, len;
    134     var frameData = atlasData.frames;
    135     if(!frameData) return null;
    136
    137     var frames = [], obj;
    138
    139     if(frameData instanceof Array){ //frames by array
    140         for(i = 0, len = frameData.length; i < len; i++){
    141             obj = frameData[i];
    142             frames[i] = {
    143                 image: atlasData.image,
    144                 rect: obj
    145             };
    146         }
    147     }else{ //frames by object
    148         var frameWidth = frameData.frameWidth;
    149         var frameHeight = frameData.frameHeight;
    150         var cols = atlasData.width / frameWidth | 0;
    151         var rows = atlasData.height / frameHeight | 0;
    152         var numFrames = frameData.numFrames || cols * rows;
    153         for(i = 0; i < numFrames; i++){
    154             frames[i] = {
    155                 image: atlasData.image,
    156                 rect: [i%cols*frameWidth, (i/cols|0)*frameHeight, frameWidth, frameHeight]
    157             };
    158         }
    159     }
    160
    161     return frames;
    162 }
    163
    164 /**
    165  * 解析精灵数据。
    166  * @private
    167  */
    168 function parseTextureSprites(atlasData, frames){
    169     var i, len;
    170     var spriteData = atlasData.sprites;
    171     if(!spriteData) return null;
    172
    173     var sprites = {}, sprite, spriteFrames, spriteFrame;
    174
    175     for(var s in spriteData){
    176         sprite = spriteData[s];
    177         if(isNumber(sprite)){ //single frame
    178             spriteFrames = translateSpriteFrame(frames[sprite]);
    179         }else if(sprite instanceof Array){ //frames by array
    180             spriteFrames = [];
    181             for(i = 0, len = sprite.length; i < len; i++){
    182                 var spriteObj = sprite[i], frameObj;
    183                 if(isNumber(spriteObj)){
    184                     spriteFrame = translateSpriteFrame(frames[spriteObj]);
    185                 }else{
    186                     frameObj = spriteObj.rect;
    187                     if(isNumber(frameObj)) frameObj = frames[spriteObj.rect];
    188                     spriteFrame = translateSpriteFrame(frameObj, spriteObj);
    189                 }
    190                 spriteFrames[i] = spriteFrame;
    191             }
    192         }else{ //frames by object
    193             spriteFrames = [];
    194             for(i = sprite.from; i <= sprite.to; i++){
    195                 spriteFrames[i - sprite.from] = translateSpriteFrame(frames[i], sprite[i]);
    196             }
    197         }
    198         sprites[s] = spriteFrames;
    199     }
    200
    201     return sprites;
    202 }
    203
    204 function translateSpriteFrame(frameObj, spriteObj){
    205     var spriteFrame = {
    206         image: frameObj.image,
    207         rect: frameObj.rect
    208     };
    209
    210     if(spriteObj){
    211         spriteFrame.name = spriteObj.name || null;
    212         spriteFrame.duration = spriteObj.duration || 0;
    213         spriteFrame.stop = !!spriteObj.stop;
    214         spriteFrame.next = spriteObj.next || null;
    215     }
    216
    217     return spriteFrame;
    218 }
    219
    220 function isNumber(value){
    221     return typeof value === 'number';
    222 }
    223
    224 })();