1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | var prefixes = ['webkit', 'Moz', 'ms', 'O'];
|
8 | var animations = {};
|
9 | var useCssAnimations;
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | function createEl(tag, prop) {
|
16 | var el = document.createElement(tag || 'div');
|
17 | var n;
|
18 |
|
19 | for(n in prop) {
|
20 | el[n] = prop[n];
|
21 | }
|
22 | return el;
|
23 | }
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | function ins(parent /* child1, child2, ...*/) {
|
29 | for (var i=1, n=arguments.length; i<n; i++) {
|
30 | parent.appendChild(arguments[i]);
|
31 | }
|
32 | return parent;
|
33 | }
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | var sheet = function() {
|
39 | var el = createEl('style');
|
40 | ins(document.getElementsByTagName('head')[0], el);
|
41 | return el.sheet || el.styleSheet;
|
42 | }();
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 | function addAnimation(alpha, trail, i, lines) {
|
50 | var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-');
|
51 | var start = 0.01 + i/lines*100;
|
52 | var z = Math.max(1-(1-alpha)/trail*(100-start) , alpha);
|
53 | var prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase();
|
54 | var pre = prefix && '-'+prefix+'-' || '';
|
55 |
|
56 | if (!animations[name]) {
|
57 | sheet.insertRule(
|
58 | '@' + pre + 'keyframes ' + name + '{' +
|
59 | '0%{opacity:'+z+'}' +
|
60 | start + '%{opacity:'+ alpha + '}' +
|
61 | (start+0.01) + '%{opacity:1}' +
|
62 | (start+trail)%100 + '%{opacity:'+ alpha + '}' +
|
63 | '100%{opacity:'+ z + '}' +
|
64 | '}', 0);
|
65 | animations[name] = 1;
|
66 | }
|
67 | return name;
|
68 | }
|
69 |
|
70 |
|
71 |
|
72 |
|
73 | function vendor(el, prop) {
|
74 | var s = el.style;
|
75 | var pp;
|
76 | var i;
|
77 |
|
78 | if(s[prop] !== undefined) return prop;
|
79 | prop = prop.charAt(0).toUpperCase() + prop.slice(1);
|
80 | for(i=0; i<prefixes.length; i++) {
|
81 | pp = prefixes[i]+prop;
|
82 | if(s[pp] !== undefined) return pp;
|
83 | }
|
84 | }
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | function css(el, prop) {
|
90 | for (var n in prop) {
|
91 | el.style[vendor(el, n)||n] = prop[n];
|
92 | }
|
93 | return el;
|
94 | }
|
95 |
|
96 |
|
97 |
|
98 |
|
99 | function merge(obj) {
|
100 | for (var i=1; i < arguments.length; i++) {
|
101 | var def = arguments[i];
|
102 | for (var n in def) {
|
103 | if (obj[n] === undefined) obj[n] = def[n];
|
104 | }
|
105 | }
|
106 | return obj;
|
107 | }
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | function pos(el) {
|
113 | var o = {x:el.offsetLeft, y:el.offsetTop};
|
114 | while((el = el.offsetParent)) {
|
115 | o.x+=el.offsetLeft;
|
116 | o.y+=el.offsetTop;
|
117 | }
|
118 | return o;
|
119 | }
|
120 |
|
121 | var defaults = {
|
122 | lines: 12,
|
123 | length: 7,
|
124 | width: 5,
|
125 | radius: 10,
|
126 | rotate: 0,
|
127 | color: '#000',
|
128 | speed: 1,
|
129 | trail: 100,
|
130 | opacity: 1/4,
|
131 | fps: 20,
|
132 | zIndex: 2e9,
|
133 | className: 'spinner',
|
134 | top: 'auto',
|
135 | left: 'auto'
|
136 | };
|
137 |
|
138 |
|
139 | var Spinner = function Spinner(o) {
|
140 | if (!this.spin) return new Spinner(o);
|
141 | this.opts = merge(o || {}, Spinner.defaults, defaults);
|
142 | };
|
143 |
|
144 | Spinner.defaults = {};
|
145 | merge(Spinner.prototype, {
|
146 | spin: function(target) {
|
147 | this.stop();
|
148 | var self = this;
|
149 | var o = self.opts;
|
150 | var el = self.el = css(createEl(0, {className: o.className}), {position: 'relative', zIndex: o.zIndex});
|
151 | var mid = o.radius+o.length+o.width;
|
152 | var ep;
|
153 | var tp;
|
154 |
|
155 | if (target) {
|
156 | target.insertBefore(el, target.firstChild||null);
|
157 | tp = pos(target);
|
158 | ep = pos(el);
|
159 | css(el, {
|
160 | left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 1) : o.left+mid) + 'px',
|
161 | top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : o.top+mid) + 'px'
|
162 | });
|
163 | }
|
164 |
|
165 | el.setAttribute('aria-role', 'progressbar');
|
166 | self.lines(el, self.opts);
|
167 |
|
168 | if (!useCssAnimations) {
|
169 |
|
170 | var i = 0;
|
171 | var fps = o.fps;
|
172 | var f = fps/o.speed;
|
173 | var ostep = (1-o.opacity)/(f*o.trail / 100);
|
174 | var astep = f/o.lines;
|
175 |
|
176 | !function anim() {
|
177 | i++;
|
178 | for (var s=o.lines; s; s--) {
|
179 | var alpha = Math.max(1-(i+s*astep)%f * ostep, o.opacity);
|
180 | self.opacity(el, o.lines-s, alpha, o);
|
181 | }
|
182 | self.timeout = self.el && setTimeout(anim, ~~(1000/fps));
|
183 | }();
|
184 | }
|
185 | return self;
|
186 | },
|
187 | stop: function() {
|
188 | var el = this.el;
|
189 | if (el) {
|
190 | clearTimeout(this.timeout);
|
191 | if (el.parentNode) el.parentNode.removeChild(el);
|
192 | this.el = undefined;
|
193 | }
|
194 | return this;
|
195 | },
|
196 | lines: function(el, o) {
|
197 | var i = 0;
|
198 | var seg;
|
199 |
|
200 | function fill(color, shadow) {
|
201 | return css(createEl(), {
|
202 | position: 'absolute',
|
203 | width: (o.length+o.width) + 'px',
|
204 | height: o.width + 'px',
|
205 | background: color,
|
206 | boxShadow: shadow,
|
207 | transformOrigin: 'left',
|
208 | transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)',
|
209 | borderRadius: (o.width>>1) + 'px'
|
210 | });
|
211 | }
|
212 | for (; i < o.lines; i++) {
|
213 | seg = css(createEl(), {
|
214 | position: 'absolute',
|
215 | top: 1+~(o.width/2) + 'px',
|
216 | transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
|
217 | opacity: o.opacity,
|
218 | animation: useCssAnimations && addAnimation(o.opacity, o.trail, i, o.lines) + ' ' + 1/o.speed + 's linear infinite'
|
219 | });
|
220 | if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}));
|
221 | ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)')));
|
222 | }
|
223 | return el;
|
224 | },
|
225 | opacity: function(el, i, val) {
|
226 | if (i < el.childNodes.length) el.childNodes[i].style.opacity = val;
|
227 | }
|
228 | });
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 | !function() {
|
238 |
|
239 | function vml(tag, attr) {
|
240 | return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr);
|
241 | }
|
242 |
|
243 | var s = css(createEl('group'), {behavior: 'url(#default#VML)'});
|
244 |
|
245 | if (!vendor(s, 'transform') && s.adj) {
|
246 |
|
247 |
|
248 | sheet.addRule('.spin-vml', 'behavior:url(#default#VML)');
|
249 |
|
250 | Spinner.prototype.lines = function(el, o) {
|
251 | var r = o.length+o.width;
|
252 | var s = 2*r;
|
253 |
|
254 | function grp() {
|
255 | return css(vml('group', {coordsize: s +' '+s, coordorigin: -r +' '+-r}), {width: s, height: s});
|
256 | }
|
257 |
|
258 | var margin = -(o.width+o.length)*2+'px';
|
259 | var g = css(grp(), {position: 'absolute', top: margin, left: margin});
|
260 |
|
261 | var i;
|
262 |
|
263 | function seg(i, dx, filter) {
|
264 | ins(g,
|
265 | ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
|
266 | ins(css(vml('roundrect', {arcsize: 1}), {
|
267 | width: r,
|
268 | height: o.width,
|
269 | left: o.radius,
|
270 | top: -o.width>>1,
|
271 | filter: filter
|
272 | }),
|
273 | vml('fill', {color: o.color, opacity: o.opacity}),
|
274 | vml('stroke', {opacity: 0})
|
275 | )
|
276 | )
|
277 | );
|
278 | }
|
279 |
|
280 | if (o.shadow) {
|
281 | for (i = 1; i <= o.lines; i++) {
|
282 | seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)');
|
283 | }
|
284 | }
|
285 | for (i = 1; i <= o.lines; i++) seg(i);
|
286 | return ins(el, g);
|
287 | };
|
288 | Spinner.prototype.opacity = function(el, i, val, o) {
|
289 | var c = el.firstChild;
|
290 | o = o.shadow && o.lines || 0;
|
291 | if (c && i+o < c.childNodes.length) {
|
292 | c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild;
|
293 | if (c) c.opacity = val;
|
294 | }
|
295 | };
|
296 | }
|
297 | else {
|
298 | useCssAnimations = vendor(s, 'animation');
|
299 | }
|
300 | }();
|
301 |
|
302 | module.exports = Spinner;
|