1 //fgnass.github.com/spin.js#v1.3.2
4 * Copyright (c) 2011-2013 Felix Gnass
5 * Licensed under the MIT license
7 (function(root
, factory
) {
10 if (typeof exports
== 'object') module
.exports
= factory()
13 else if (typeof define
== 'function' && define
.amd
) define(factory
)
16 else root
.Spinner
= factory()
21 var prefixes
= ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
22 , animations
= {} /* Animation rules keyed by their name */
23 , useCssAnimations
/* Whether to use CSS animations or setTimeout */
26 * Utility function to create elements. If no tag name is given,
27 * a DIV is created. Optionally properties can be passed.
29 function createEl(tag
, prop
) {
30 var el
= document
.createElement(tag
|| 'div')
33 for(n
in prop
) el
[n
] = prop
[n
]
38 * Appends children and returns the parent.
40 function ins(parent
/* child1, child2, ...*/) {
41 for (var i
=1, n
=arguments
.length
; i
<n
; i
++)
42 parent
.appendChild(arguments
[i
])
48 * Insert a new stylesheet to hold the @keyframe or VML rules.
50 var sheet
= (function() {
51 var el
= createEl('style', {type
: 'text/css'})
52 ins(document
.getElementsByTagName('head')[0], el
)
53 return el
.sheet
|| el
.styleSheet
57 * Creates an opacity keyframe animation rule and returns its name.
58 * Since most mobile Webkits have timing issues with animation-delay,
59 * we create separate rules for each line/segment.
61 function addAnimation(alpha
, trail
, i
, lines
) {
62 var name
= ['opacity', trail
, ~~(alpha
*100), i
, lines
].join('-')
63 , start
= 0.01 + i
/lines
* 100
64 , z
= Math
.max(1 - (1-alpha
) / trail
* (100-start
), alpha
)
65 , prefix
= useCssAnimations
.substring(0, useCssAnimations
.indexOf('Animation')).toLowerCase()
66 , pre
= prefix
&& '-' + prefix
+ '-' || ''
68 if (!animations
[name
]) {
70 '@' + pre
+ 'keyframes ' + name
+ '{' +
71 '0%{opacity:' + z
+ '}' +
72 start
+ '%{opacity:' + alpha
+ '}' +
73 (start
+0.01) + '%{opacity:1}' +
74 (start
+trail
) % 100 + '%{opacity:' + alpha
+ '}' +
75 '100%{opacity:' + z
+ '}' +
76 '}', sheet
.cssRules
.length
)
85 * Tries various vendor prefixes and returns the first supported property.
87 function vendor(el
, prop
) {
92 prop
= prop
.charAt(0).toUpperCase() + prop
.slice(1)
93 for(i
=0; i
<prefixes
.length
; i
++) {
95 if(s
[pp
] !== undefined) return pp
97 if(s
[prop
] !== undefined) return prop
101 * Sets multiple style properties at once.
103 function css(el
, prop
) {
105 el
.style
[vendor(el
, n
)||n
] = prop
[n
]
111 * Fills in default values.
113 function merge(obj
) {
114 for (var i
=1; i
< arguments
.length
; i
++) {
115 var def
= arguments
[i
]
117 if (obj
[n
] === undefined) obj
[n
] = def
[n
]
123 * Returns the absolute page-offset of the given element.
126 var o
= { x
:el
.offsetLeft
, y
:el
.offsetTop
}
127 while((el
= el
.offsetParent
))
128 o
.x
+=el
.offsetLeft
, o
.y
+=el
.offsetTop
134 * Returns the line color from the given string or array.
136 function getColor(color
, idx
) {
137 return typeof color
== 'string' ? color
: color
[idx
% color
.length
]
143 lines
: 12, // The number of lines to draw
144 length
: 7, // The length of each line
145 width
: 5, // The line thickness
146 radius
: 10, // The radius of the inner circle
147 rotate
: 0, // Rotation offset
148 corners
: 1, // Roundness (0..1)
149 color
: '#000', // #rgb or #rrggbb
150 direction
: 1, // 1: clockwise, -1: counterclockwise
151 speed
: 1, // Rounds per second
152 trail
: 100, // Afterglow percentage
153 opacity
: 1/4, // Opacity
of the lines
154 fps
: 20, // Frames per second when using setTimeout()
155 zIndex
: 2e9
, // Use a high z-index by default
156 className
: 'spinner', // CSS class to assign to the element
157 top
: 'auto', // center vertically
158 left
: 'auto', // center horizontally
159 position
: 'relative' // element position
162 /** The constructor */
163 function Spinner(o
) {
164 if (typeof this == 'undefined') return new Spinner(o
)
165 this.opts
= merge(o
|| {}, Spinner
.defaults
, defaults
)
168 // Global defaults that override the built-ins:
169 Spinner
.defaults
= {}
171 merge(Spinner
.prototype, {
174 * Adds the spinner to the given target element. If this instance is already
175 * spinning, it is automatically removed from its previous target b calling
178 spin: function(target
) {
183 , el
= self
.el
= css(createEl(0, {className
: o
.className
}), {position
: o
.position
, width
: 0, zIndex
: o
.zIndex
})
184 , mid
= o
.radius
+o
.length
+o
.width
185 , ep
// element position
186 , tp
// target position
189 target
.insertBefore(el
, target
.firstChild
||null)
193 left
: (o
.left
== 'auto' ? tp
.x
-ep
.x
+ (target
.offsetWidth
>> 1) : parseInt(o
.left
, 10) + mid
) + 'px',
194 top
: (o
.top
== 'auto' ? tp
.y
-ep
.y
+ (target
.offsetHeight
>> 1) : parseInt(o
.top
, 10) + mid
) + 'px'
198 el
.setAttribute('role', 'progressbar')
199 self
.lines(el
, self
.opts
)
201 if (!useCssAnimations
) {
202 // No CSS animation support, use setTimeout() instead
204 , start
= (o
.lines
- 1) * (1 - o
.direction
) / 2
208 , ostep
= (1-o
.opacity
) / (f
*o
.trail
/ 100)
213 for (var j
= 0; j
< o
.lines
; j
++) {
214 alpha
= Math
.max(1 - (i
+ (o
.lines
- j
) * astep
) % f
* ostep
, o
.opacity
)
216 self
.opacity(el
, j
* o
.direction
+ start
, alpha
, o
)
218 self
.timeout
= self
.el
&& setTimeout(anim
, ~~(1000/fps
))
225 * Stops and removes the Spinner.
230 clearTimeout(this.timeout
)
231 if (el
.parentNode
) el
.parentNode
.removeChild(el
)
238 * Internal method that draws the individual lines. Will be overwritten
239 * in VML fallback mode below.
241 lines: function(el
, o
) {
243 , start
= (o
.lines
- 1) * (1 - o
.direction
) / 2
246 function fill(color
, shadow
) {
247 return css(createEl(), {
248 position
: 'absolute',
249 width
: (o
.length
+o
.width
) + 'px',
250 height
: o
.width
+ 'px',
253 transformOrigin
: 'left',
254 transform
: 'rotate(' + ~~(360/o
.lines
*i
+o
.rotate
) + 'deg) translate(' + o
.radius
+'px' +',0)',
255 borderRadius
: (o
.corners
* o
.width
>>1) + 'px'
259 for (; i
< o
.lines
; i
++) {
260 seg
= css(createEl(), {
261 position
: 'absolute',
262 top
: 1+~(o
.width
/2) + 'px',
263 transform
: o
.hwaccel
? 'translate3d(0,0,0)' : '',
265 animation
: useCssAnimations
&& addAnimation(o
.opacity
, o
.trail
, start
+ i
* o
.direction
, o
.lines
) + ' ' + 1/o
.speed
+ 's linear infinite'
268 if (o
.shadow
) ins(seg
, css(fill('#000', '0 0 4px ' + '#000'), {top
: 2+'px'}))
269 ins(el
, ins(seg
, fill(getColor(o
.color
, i
), '0 0 1px rgba(0,0,0,.1)')))
275 * Internal method that adjusts the opacity of a single line.
276 * Will be overwritten in VML fallback mode below.
278 opacity: function(el
, i
, val
) {
279 if (i
< el
.childNodes
.length
) el
.childNodes
[i
].style
.opacity
= val
287 /* Utility function to create a VML tag */
288 function vml(tag
, attr
) {
289 return createEl('<' + tag
+ ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr
)
292 // No CSS transforms but VML support, add a CSS rule for VML elements:
293 sheet
.addRule('.spin-vml', 'behavior:url(#default#VML)')
295 Spinner
.prototype.lines = function(el
, o
) {
296 var r
= o
.length
+o
.width
302 coordsize
: s
+ ' ' + s
,
303 coordorigin
: -r
+ ' ' + -r
305 { width
: s
, height
: s
}
309 var margin
= -(o
.width
+o
.length
)*2 + 'px'
310 , g
= css(grp(), {position
: 'absolute', top
: margin
, left
: margin
})
313 function seg(i
, dx
, filter
) {
315 ins(css(grp(), {rotation
: 360 / o
.lines
* i
+ 'deg', left
: ~~dx
}),
316 ins(css(vml('roundrect', {arcsize
: o
.corners
}), {
323 vml('fill', {color
: getColor(o
.color
, i
), opacity
: o
.opacity
}),
324 vml('stroke', {opacity
: 0}) // transparent stroke to fix color bleeding upon opacity change
331 for (i
= 1; i
<= o
.lines
; i
++)
332 seg(i
, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)')
334 for (i
= 1; i
<= o
.lines
; i
++) seg(i
)
338 Spinner
.prototype.opacity = function(el
, i
, val
, o
) {
339 var c
= el
.firstChild
340 o
= o
.shadow
&& o
.lines
|| 0
341 if (c
&& i
+o
< c
.childNodes
.length
) {
342 c
= c
.childNodes
[i
+o
]; c
= c
&& c
.firstChild
; c
= c
&& c
.firstChild
343 if (c
) c
.opacity
= val
348 var probe
= css(createEl('group'), {behavior
: 'url(#default#VML)'})
350 if (!vendor(probe
, 'transform') && probe
.adj
) initVML()
351 else useCssAnimations
= vendor(probe
, 'animation')