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