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