1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * @namespace Hilo的基础核心方法集合。 9 * @static 10 * @module hilo/core/Hilo 11 */ 12 var Hilo = (function(){ 13 14 var win = window, doc = document, docElem = doc.documentElement, 15 uid = 0; 16 17 return { 18 /** 19 * 获取一个全局唯一的id。如Stage1,Bitmap2等。 20 * @param {String} prefix 生成id的前缀。 21 * @returns {String} 全局唯一id。 22 */ 23 getUid: function(prefix){ 24 var id = ++uid; 25 if(prefix){ 26 var charCode = prefix.charCodeAt(prefix.length - 1); 27 if (charCode >= 48 && charCode <= 57) prefix += "_"; //0至9之间添加下划线 28 return prefix + id; 29 } 30 return id; 31 }, 32 33 /** 34 * 为指定的可视对象生成一个包含路径的字符串表示形式。如Stage1.Container2.Bitmap3。 35 * @param {View} view 指定的可视对象。 36 * @returns {String} 可视对象的字符串表示形式。 37 */ 38 viewToString: function(view){ 39 var result, obj = view; 40 while(obj){ 41 result = result ? (obj.id + '.' + result) : obj.id; 42 obj = obj.parent; 43 } 44 return result; 45 }, 46 47 /** 48 * 简单的浅复制对象。 49 * @param {Object} target 要复制的目标对象。 50 * @param {Object} source 要复制的源对象。 51 * @param {Boolean} strict 指示是否复制未定义的属性,默认为false,即不复制未定义的属性。 52 * @returns {Object} 复制后的对象。 53 */ 54 copy: function(target, source, strict){ 55 for(var key in source){ 56 if(!strict || target.hasOwnProperty(key) || target[key] !== undefined){ 57 target[key] = source[key]; 58 } 59 } 60 return target; 61 }, 62 63 /** 64 * 浏览器特性集合。包括: 65 * <ul> 66 * <li><b>jsVendor</b> - 浏览器厂商CSS前缀的js值。比如:webkit。</li> 67 * <li><b>cssVendor</b> - 浏览器厂商CSS前缀的css值。比如:-webkit-。</li> 68 * <li><b>supportTransform</b> - 是否支持CSS Transform变换。</li> 69 * <li><b>supportTransform3D</b> - 是否支持CSS Transform 3D变换。</li> 70 * <li><b>supportStorage</b> - 是否支持本地存储localStorage。</li> 71 * <li><b>supportTouch</b> - 是否支持触碰事件。</li> 72 * <li><b>supportCanvas</b> - 是否支持canvas元素。</li> 73 * </ul> 74 */ 75 browser: (function(){ 76 var ua = navigator.userAgent; 77 var data = { 78 iphone: /iphone/i.test(ua), 79 ipad: /ipad/i.test(ua), 80 ipod: /ipod/i.test(ua), 81 ios: /iphone|ipad|ipod/i.test(ua), 82 android: /android/i.test(ua), 83 webkit: /webkit/i.test(ua), 84 chrome: /chrome/i.test(ua), 85 safari: /safari/i.test(ua), 86 firefox: /firefox/i.test(ua), 87 ie: /msie/i.test(ua), 88 opera: /opera/i.test(ua), 89 supportTouch: 'ontouchstart' in win, 90 supportCanvas: doc.createElement('canvas').getContext != null, 91 supportStorage: false, 92 supportOrientation: 'orientation' in win, 93 supportDeviceMotion: 'ondevicemotion' in win 94 }; 95 96 //`localStorage` is null or `localStorage.setItem` throws error in some cases (e.g. localStorage is disabled) 97 try{ 98 var value = 'hilo'; 99 localStorage.setItem(value, value); 100 localStorage.removeItem(value); 101 data.supportStorage = true; 102 }catch(e){ }; 103 104 //vendro prefix 105 var jsVendor = data.jsVendor = data.webkit ? 'webkit' : data.firefox ? 'Moz' : data.opera ? 'O' : data.ie ? 'ms' : ''; 106 var cssVendor = data.cssVendor = '-' + jsVendor + '-'; 107 108 //css transform/3d feature dectection 109 var testElem = doc.createElement('div'), style = testElem.style; 110 var supportTransform = style[jsVendor + 'Transform'] != undefined; 111 var supportTransform3D = style[jsVendor + 'Perspective'] != undefined; 112 if(supportTransform3D){ 113 testElem.id = 'test3d'; 114 style = doc.createElement('style'); 115 style.textContent = '@media ('+ cssVendor +'transform-3d){#test3d{height:3px}}'; 116 doc.head.appendChild(style); 117 118 docElem.appendChild(testElem); 119 supportTransform3D = testElem.offsetHeight == 3; 120 doc.head.removeChild(style); 121 docElem.removeChild(testElem); 122 }; 123 data.supportTransform = supportTransform; 124 data.supportTransform3D = supportTransform3D; 125 126 return data; 127 })(), 128 129 /** 130 * 事件类型枚举对象。包括: 131 * <ul> 132 * <li><b>POINTER_START</b> - 鼠标或触碰开始事件。对应touchstart或mousedown。</li> 133 * <li><b>POINTER_MOVE</b> - 鼠标或触碰移动事件。对应touchmove或mousemove。</li> 134 * <li><b>POINTER_END</b> - 鼠标或触碰结束事件。对应touchend或mouseup。</li> 135 * </ul> 136 */ 137 event: (function(){ 138 var supportTouch = 'ontouchstart' in win; 139 return { 140 POINTER_START: supportTouch ? 'touchstart' : 'mousedown', 141 POINTER_MOVE: supportTouch ? 'touchmove' : 'mousemove', 142 POINTER_END: supportTouch ? 'touchend' : 'mouseup' 143 }; 144 })(), 145 146 /** 147 * 可视对象对齐方式枚举对象。包括: 148 * <ul> 149 * <li><b>TOP_LEFT</b> - 左上角对齐。</li> 150 * <li><b>TOP</b> - 顶部居中对齐。</li> 151 * <li><b>TOP_RIGHT</b> - 右上角对齐。</li> 152 * <li><b>LEFT</b> - 左边居中对齐。</li> 153 * <li><b>CENTER</b> - 居中对齐。</li> 154 * <li><b>RIGHT</b> - 右边居中对齐。</li> 155 * <li><b>BOTTOM_LEFT</b> - 左下角对齐。</li> 156 * <li><b>BOTTOM</b> - 底部居中对齐。</li> 157 * <li><b>BOTTOM_RIGHT</b> - 右下角对齐。</li> 158 * </ul> 159 */ 160 align: { 161 TOP_LEFT: 'TL', //top & left 162 TOP: 'T', //top & center 163 TOP_RIGHT: 'TR', //top & right 164 LEFT: 'L', //left & center 165 CENTER: 'C', //center 166 RIGHT: 'R', //right & center 167 BOTTOM_LEFT: 'BL', //bottom & left 168 BOTTOM: 'B', //bottom & center 169 BOTTOM_RIGHT: 'BR' //bottom & right 170 }, 171 172 /** 173 * 获取DOM元素在页面中的内容显示区域。 174 * @param {HTMLElement} elem DOM元素。 175 * @returns {Object} DOM元素的可视区域。格式为:{left:0, top:0, width:100, height:100}。 176 */ 177 getElementRect: function(elem){ 178 try{ 179 //this fails if it's a disconnected DOM node 180 var bounds = elem.getBoundingClientRect(); 181 }catch(e){ 182 bounds = {top:elem.offsetTop, left:elem.offsetLeft, width:elem.offsetWidth, height:elem.offsetHeight}; 183 } 184 185 var offsetX = ((win.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0)) || 0; 186 var offsetY = ((win.pageYOffset || docElem.scrollTop) - (docElem.clientTop || 0)) || 0; 187 var styles = win.getComputedStyle ? getComputedStyle(elem) : elem.currentStyle; 188 var parseIntFn = parseInt; 189 190 var padLeft = (parseIntFn(styles.paddingLeft) + parseIntFn(styles.borderLeftWidth)) || 0; 191 var padTop = (parseIntFn(styles.paddingTop) + parseIntFn(styles.borderTopWidth)) || 0; 192 var padRight = (parseIntFn(styles.paddingRight) + parseIntFn(styles.borderRightWidth)) || 0; 193 var padBottom = (parseIntFn(styles.paddingBottom) + parseIntFn(styles.borderBottomWidth)) || 0; 194 var top = bounds.top || 0; 195 var left = bounds.left || 0; 196 197 return { 198 left: left + offsetX + padLeft, 199 top: top + offsetY + padTop, 200 width: bounds.right - padRight - left - padLeft, 201 height: bounds.bottom - padBottom - top - padTop 202 }; 203 }, 204 205 /** 206 * 创建一个DOM元素。可指定属性和样式。 207 * @param {String} type 要创建的DOM元素的类型。比如:'div'。 208 * @param {Object} properties 指定DOM元素的属性和样式。 209 * @returns {HTMLElement} 一个DOM元素。 210 */ 211 createElement: function(type, properties){ 212 var elem = doc.createElement(type), p, val, s; 213 for(p in properties){ 214 val = properties[p]; 215 if(p === 'style'){ 216 for(s in val) elem.style[s] = val[s]; 217 }else{ 218 elem[p] = val; 219 } 220 } 221 return elem; 222 }, 223 224 /** 225 * 根据参数id获取一个DOM元素。此方法等价于document.getElementById(id)。 226 * @param {String} id 要获取的DOM元素的id。 227 * @returns {HTMLElement} 一个DOM元素。 228 */ 229 getElement: function(id){ 230 return doc.getElementById(id); 231 }, 232 233 /** 234 * 设置可视对象DOM元素的CSS样式。 235 * @param {View} obj 指定要设置CSS样式的可视对象。 236 * @private 237 */ 238 setElementStyleByView: function(obj){ 239 var drawable = obj.drawable, 240 style = drawable.domElement.style, 241 stateCache = obj._stateCache || (obj._stateCache = {}), 242 prefix = Hilo.browser.jsVendor, px = 'px', flag = false; 243 244 if(this.cacheStateIfChanged(obj, ['visible'], stateCache)){ 245 style.display = !obj.visible ? 'none' : ''; 246 } 247 if(this.cacheStateIfChanged(obj, ['alpha'], stateCache)){ 248 style.opacity = obj.alpha; 249 } 250 if(!obj.visible || obj.alpha <= 0) return; 251 252 if(this.cacheStateIfChanged(obj, ['width'], stateCache)){ 253 style.width = obj.width + px; 254 } 255 if(this.cacheStateIfChanged(obj, ['height'], stateCache)){ 256 style.height = obj.height + px; 257 } 258 if(this.cacheStateIfChanged(obj, ['depth'], stateCache)){ 259 style.zIndex = obj.depth + 1; 260 } 261 if(flag = this.cacheStateIfChanged(obj, ['pivotX', 'pivotY'], stateCache)){ 262 style[prefix + 'TransformOrigin'] = obj.pivotX + px + ' ' + obj.pivotY + px; 263 } 264 if(this.cacheStateIfChanged(obj, ['x', 'y', 'rotation', 'scaleX', 'scaleY'], stateCache) || flag){ 265 style[prefix + 'Transform'] = this.getTransformCSS(obj); 266 } 267 if(this.cacheStateIfChanged(obj, ['background'], stateCache)){ 268 style.backgroundColor = obj.background; 269 } 270 if(!style.pointerEvents){ 271 style.pointerEvents = 'none'; 272 } 273 274 //render image as background 275 var image = drawable.image; 276 if(image){ 277 var src = image.src; 278 if(src !== stateCache.image){ 279 stateCache.image = src; 280 style.backgroundImage = 'url(' + src + ')'; 281 } 282 283 var rect = drawable.rect; 284 if(rect){ 285 var sx = rect[0], sy = rect[1]; 286 if(sx !== stateCache.sx){ 287 stateCache.sx = sx; 288 style.backgroundPositionX = -sx + px; 289 } 290 if(sy !== stateCache.sy){ 291 stateCache.sy = sy; 292 style.backgroundPositionY = -sy + px; 293 } 294 } 295 } 296 297 //render mask 298 var mask = obj.mask; 299 if(mask){ 300 var maskImage = mask.drawable.domElement.style.backgroundImage; 301 if(maskImage !== stateCache.maskImage){ 302 stateCache.maskImage = maskImage; 303 style[prefix + 'MaskImage'] = maskImage; 304 style[prefix + 'MaskRepeat'] = 'no-repeat'; 305 } 306 307 var maskX = mask.x, maskY = mask.y; 308 if(maskX !== stateCache.maskX || maskY !== stateCache.maskY){ 309 stateCache.maskX = maskX; 310 stateCache.maskY = maskY; 311 style[prefix + 'MaskPosition'] = maskX + px + ' ' + maskY + px; 312 } 313 } 314 }, 315 316 /** 317 * @private 318 */ 319 cacheStateIfChanged: function(obj, propNames, stateCache){ 320 var i, len, name, value, changed = false; 321 for(i = 0, len = propNames.length; i < len; i++){ 322 name = propNames[i]; 323 value = obj[name]; 324 if(value != stateCache[name]){ 325 stateCache[name] = value; 326 changed = true; 327 } 328 } 329 return changed; 330 }, 331 332 /** 333 * 生成可视对象的CSS变换样式。 334 * @param {View} obj 指定生成CSS变换样式的可视对象。 335 * @returns {String} 生成的CSS样式字符串。 336 */ 337 getTransformCSS: function(obj){ 338 var use3d = this.browser.supportTransform3D, 339 str3d = use3d ? '3d' : ''; 340 341 return 'translate' + str3d + '(' + (obj.x - obj.pivotX) + 'px, ' + (obj.y - obj.pivotY) + (use3d ? 'px, 0px)' : 'px)') 342 + 'rotate' + str3d + (use3d ? '(0, 0, 1, ' : '(') + obj.rotation + 'deg)' 343 + 'scale' + str3d + '(' + obj.scaleX + ', ' + obj.scaleY + (use3d ? ', 1)' : ')'); 344 } 345 }; 346 347 })();