1 /** 2 * Copyright (c) 2011-2014 Felix Gnass 3 * Licensed under the MIT license 4 * http://spin.js.org/ 5 * 6 * Example: 7 var opts = { 8 lines: 12 // The number of lines to draw 9 , length: 7 // The length of each line 10 , width: 5 // The line thickness 11 , radius: 10 // The radius of the inner circle 12 , scale: 1.0 // Scales overall size of the spinner 13 , corners: 1 // Roundness (0..1) 14 , color: ‘#000‘ // #rgb or #rrggbb 15 , opacity: 1/4 // Opacity of the lines 16 , rotate: 0 // Rotation offset 17 , direction: 1 // 1: clockwise, -1: counterclockwise 18 , speed: 1 // Rounds per second 19 , trail: 100 // Afterglow percentage 20 , fps: 20 // Frames per second when using setTimeout() 21 , zIndex: 2e9 // Use a high z-index by default 22 , className: ‘spinner‘ // CSS class to assign to the element 23 , top: ‘50%‘ // center vertically 24 , left: ‘50%‘ // center horizontally 25 , shadow: false // Whether to render a shadow 26 , hwaccel: false // Whether to use hardware acceleration (might be buggy) 27 , position: ‘absolute‘ // Element positioning 28 } 29 var target = document.getElementById(‘foo‘) 30 var spinner = new Spinner(opts).spin(target) 31 */ 32 ;(function (root, factory) { 33 34 /* CommonJS */ 35 if (typeof module == ‘object‘ && module.exports) module.exports = factory() 36 37 /* AMD module */ 38 else if (typeof define == ‘function‘ && define.amd) define(factory) 39 40 /* Browser global */ 41 else root.Spinner = factory() 42 }(this, function () { 43 "use strict" 44 45 var prefixes = [‘webkit‘, ‘Moz‘, ‘ms‘, ‘O‘] /* Vendor prefixes */ 46 , animations = {} /* Animation rules keyed by their name */ 47 , useCssAnimations /* Whether to use CSS animations or setTimeout */ 48 , sheet /* A stylesheet to hold the @keyframe or VML rules. */ 49 50 /** 51 * Utility function to create elements. If no tag name is given, 52 * a DIV is created. Optionally properties can be passed. 53 */ 54 function createEl (tag, prop) { 55 var el = document.createElement(tag || ‘div‘) 56 , n 57 58 for (n in prop) el[n] = prop[n] 59 return el 60 } 61 62 /** 63 * Appends children and returns the parent. 64 */ 65 function ins (parent /* child1, child2, ...*/) { 66 for (var i = 1, n = arguments.length; i < n; i++) { 67 parent.appendChild(arguments[i]) 68 } 69 70 return parent 71 } 72 73 /** 74 * Creates an opacity keyframe animation rule and returns its name. 75 * Since most mobile Webkits have timing issues with animation-delay, 76 * we create separate rules for each line/segment. 77 */ 78 function addAnimation (alpha, trail, i, lines) { 79 var name = [‘opacity‘, trail, ~~(alpha * 100), i, lines].join(‘-‘) 80 , start = 0.01 + i/lines * 100 81 , z = Math.max(1 - (1-alpha) / trail * (100-start), alpha) 82 , prefix = useCssAnimations.substring(0, useCssAnimations.indexOf(‘Animation‘)).toLowerCase() 83 , pre = prefix && ‘-‘ + prefix + ‘-‘ || ‘‘ 84 85 if (!animations[name]) { 86 sheet.insertRule( 87 ‘@‘ + pre + ‘keyframes ‘ + name + ‘{‘ + 88 ‘0%{opacity:‘ + z + ‘}‘ + 89 start + ‘%{opacity:‘ + alpha + ‘}‘ + 90 (start+0.01) + ‘%{opacity:1}‘ + 91 (start+trail) % 100 + ‘%{opacity:‘ + alpha + ‘}‘ + 92 ‘100%{opacity:‘ + z + ‘}‘ + 93 ‘}‘, sheet.cssRules.length) 94 95 animations[name] = 1 96 } 97 98 return name 99 } 100 101 /** 102 * Tries various vendor prefixes and returns the first supported property. 103 */ 104 function vendor (el, prop) { 105 var s = el.style 106 , pp 107 , i 108 109 prop = prop.charAt(0).toUpperCase() + prop.slice(1) 110 if (s[prop] !== undefined) return prop 111 for (i = 0; i < prefixes.length; i++) { 112 pp = prefixes[i]+prop 113 if (s[pp] !== undefined) return pp 114 } 115 } 116 117 /** 118 * Sets multiple style properties at once. 119 */ 120 function css (el, prop) { 121 for (var n in prop) { 122 el.style[vendor(el, n) || n] = prop[n] 123 } 124 125 return el 126 } 127 128 /** 129 * Fills in default values. 130 */ 131 function merge (obj) { 132 for (var i = 1; i < arguments.length; i++) { 133 var def = arguments[i] 134 for (var n in def) { 135 if (obj[n] === undefined) obj[n] = def[n] 136 } 137 } 138 return obj 139 } 140 141 /** 142 * Returns the line color from the given string or array. 143 */ 144 function getColor (color, idx) { 145 return typeof color == ‘string‘ ? color : color[idx % color.length] 146 } 147 148 // Built-in defaults 149 150 var defaults = { 151 lines: 12 // The number of lines to draw 152 , length: 7 // The length of each line 153 , width: 5 // The line thickness 154 , radius: 10 // The radius of the inner circle 155 , scale: 1.0 // Scales overall size of the spinner 156 , corners: 1 // Roundness (0..1) 157 , color: ‘#000‘ // #rgb or #rrggbb 158 , opacity: 1/4 // Opacity of the lines 159 , rotate: 0 // Rotation offset 160 , direction: 1 // 1: clockwise, -1: counterclockwise 161 , speed: 1 // Rounds per second 162 , trail: 100 // Afterglow percentage 163 , fps: 20 // Frames per second when using setTimeout() 164 , zIndex: 2e9 // Use a high z-index by default 165 , className: ‘spinner‘ // CSS class to assign to the element 166 , top: ‘50%‘ // center vertically 167 , left: ‘50%‘ // center horizontally 168 , shadow: false // Whether to render a shadow 169 , hwaccel: false // Whether to use hardware acceleration (might be buggy) 170 , position: ‘absolute‘ // Element positioning 171 } 172 173 /** The constructor */ 174 function Spinner (o) { 175 this.opts = merge(o || {}, Spinner.defaults, defaults) 176 } 177 178 // Global defaults that override the built-ins: 179 Spinner.defaults = {} 180 181 merge(Spinner.prototype, { 182 /** 183 * Adds the spinner to the given target element. If this instance is already 184 * spinning, it is automatically removed from its previous target b calling 185 * stop() internally. 186 */ 187 spin: function (target) { 188 this.stop() 189 190 var self = this 191 , o = self.opts 192 , el = self.el = createEl(null, {className: o.className}) 193 194 css(el, { 195 position: o.position 196 , width: 0 197 , zIndex: o.zIndex 198 , left: o.left 199 , top: o.top 200 }) 201 202 if (target) { 203 target.insertBefore(el, target.firstChild || null) 204 } 205 206 el.setAttribute(‘role‘, ‘progressbar‘) 207 self.lines(el, self.opts) 208 209 if (!useCssAnimations) { 210 // No CSS animation support, use setTimeout() instead 211 var i = 0 212 , start = (o.lines - 1) * (1 - o.direction) / 2 213 , alpha 214 , fps = o.fps 215 , f = fps / o.speed 216 , ostep = (1 - o.opacity) / (f * o.trail / 100) 217 , astep = f / o.lines 218 219 ;(function anim () { 220 i++ 221 for (var j = 0; j < o.lines; j++) { 222 alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity) 223 224 self.opacity(el, j * o.direction + start, alpha, o) 225 } 226 self.timeout = self.el && setTimeout(anim, ~~(1000 / fps)) 227 })() 228 } 229 return self 230 } 231 232 /** 233 * Stops and removes the Spinner. 234 */ 235 , stop: function () { 236 var el = this.el 237 if (el) { 238 clearTimeout(this.timeout) 239 if (el.parentNode) el.parentNode.removeChild(el) 240 this.el = undefined 241 } 242 return this 243 } 244 245 /** 246 * Internal method that draws the individual lines. Will be overwritten 247 * in VML fallback mode below. 248 */ 249 , lines: function (el, o) { 250 var i = 0 251 , start = (o.lines - 1) * (1 - o.direction) / 2 252 , seg 253 254 function fill (color, shadow) { 255 return css(createEl(), { 256 position: ‘absolute‘ 257 , width: o.scale * (o.length + o.width) + ‘px‘ 258 , height: o.scale * o.width + ‘px‘ 259 , background: color 260 , boxShadow: shadow 261 , transformOrigin: ‘left‘ 262 , transform: ‘rotate(‘ + ~~(360/o.lines*i + o.rotate) + ‘deg) translate(‘ + o.scale*o.radius + ‘px‘ + ‘,0)‘ 263 , borderRadius: (o.corners * o.scale * o.width >> 1) + ‘px‘ 264 }) 265 } 266 267 for (; i < o.lines; i++) { 268 seg = css(createEl(), { 269 position: ‘absolute‘ 270 , top: 1 + ~(o.scale * o.width / 2) + ‘px‘ 271 , transform: o.hwaccel ? ‘translate3d(0,0,0)‘ : ‘‘ 272 , opacity: o.opacity 273 , animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ‘ ‘ + 1 / o.speed + ‘s linear infinite‘ 274 }) 275 276 if (o.shadow) ins(seg, css(fill(‘#000‘, ‘0 0 4px #000‘), {top: ‘2px‘})) 277 ins(el, ins(seg, fill(getColor(o.color, i), ‘0 0 1px rgba(0,0,0,.1)‘))) 278 } 279 return el 280 } 281 282 /** 283 * Internal method that adjusts the opacity of a single line. 284 * Will be overwritten in VML fallback mode below. 285 */ 286 , opacity: function (el, i, val) { 287 if (i < el.childNodes.length) el.childNodes[i].style.opacity = val 288 } 289 290 }) 291 292 293 function initVML () { 294 295 /* Utility function to create a VML tag */ 296 function vml (tag, attr) { 297 return createEl(‘<‘ + tag + ‘ xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">‘, attr) 298 } 299 300 // No CSS transforms but VML support, add a CSS rule for VML elements: 301 sheet.addRule(‘.spin-vml‘, ‘behavior:url(#default#VML)‘) 302 303 Spinner.prototype.lines = function (el, o) { 304 var r = o.scale * (o.length + o.width) 305 , s = o.scale * 2 * r 306 307 function grp () { 308 return css( 309 vml(‘group‘, { 310 coordsize: s + ‘ ‘ + s 311 , coordorigin: -r + ‘ ‘ + -r 312 }) 313 , { width: s, height: s } 314 ) 315 } 316 317 var margin = -(o.width + o.length) * o.scale * 2 + ‘px‘ 318 , g = css(grp(), {position: ‘absolute‘, top: margin, left: margin}) 319 , i 320 321 function seg (i, dx, filter) { 322 ins( 323 g 324 , ins( 325 css(grp(), {rotation: 360 / o.lines * i + ‘deg‘, left: ~~dx}) 326 , ins( 327 css( 328 vml(‘roundrect‘, {arcsize: o.corners}) 329 , { width: r 330 , height: o.scale * o.width 331 , left: o.scale * o.radius 332 , top: -o.scale * o.width >> 1 333 , filter: filter 334 } 335 ) 336 , vml(‘fill‘, {color: getColor(o.color, i), opacity: o.opacity}) 337 , vml(‘stroke‘, {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change 338 ) 339 ) 340 ) 341 } 342 343 if (o.shadow) 344 for (i = 1; i <= o.lines; i++) { 345 seg(i, -2, ‘progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)‘) 346 } 347 348 for (i = 1; i <= o.lines; i++) seg(i) 349 return ins(el, g) 350 } 351 352 Spinner.prototype.opacity = function (el, i, val, o) { 353 var c = el.firstChild 354 o = o.shadow && o.lines || 0 355 if (c && i + o < c.childNodes.length) { 356 c = c.childNodes[i + o]; c = c && c.firstChild; c = c && c.firstChild 357 if (c) c.opacity = val 358 } 359 } 360 } 361 362 if (typeof document !== ‘undefined‘) { 363 sheet = (function () { 364 var el = createEl(‘style‘, {type : ‘text/css‘}) 365 ins(document.getElementsByTagName(‘head‘)[0], el) 366 return el.sheet || el.styleSheet 367 }()) 368 369 var probe = css(createEl(‘group‘), {behavior: ‘url(#default#VML)‘}) 370 371 if (!vendor(probe, ‘transform‘) && probe.adj) initVML() 372 else useCssAnimations = vendor(probe, ‘animation‘) 373 } 374 375 return Spinner 376 377 }));
时间: 2024-09-29 21:03:19