ionic-angular
Version:
[](https://circleci.com/gh/driftyco/ionic)
436 lines (384 loc) • 10 kB
JavaScript
(function(ionic) {
var TRANSLATE32 = 'translate(32,32)';
var STROKE_OPACITY = 'stroke-opacity';
var ROUND = 'round';
var INDEFINITE = 'indefinite';
var DURATION = '750ms';
var NONE = 'none';
var SHORTCUTS = {
a: 'animate',
an: 'attributeName',
at: 'animateTransform',
c: 'circle',
da: 'stroke-dasharray',
os: 'stroke-dashoffset',
f: 'fill',
lc: 'stroke-linecap',
rc: 'repeatCount',
sw: 'stroke-width',
t: 'transform',
v: 'values'
};
var SPIN_ANIMATION = {
v: '0,32,32;360,32,32',
an: 'transform',
type: 'rotate',
rc: INDEFINITE,
dur: DURATION
};
function createSvgElement(tagName, data, parent, spinnerName) {
var ele = document.createElement(SHORTCUTS[tagName] || tagName);
var k, x, y;
for (k in data) {
if (angular.isArray(data[k])) {
for (x = 0; x < data[k].length; x++) {
if (data[k][x].fn) {
for (y = 0; y < data[k][x].t; y++) {
createSvgElement(k, data[k][x].fn(y, spinnerName), ele, spinnerName);
}
} else {
createSvgElement(k, data[k][x], ele, spinnerName);
}
}
} else {
setSvgAttribute(ele, k, data[k]);
}
}
parent.appendChild(ele);
}
function setSvgAttribute(ele, k, v) {
ele.setAttribute(SHORTCUTS[k] || k, v);
}
function animationValues(strValues, i) {
var values = strValues.split(';');
var back = values.slice(i);
var front = values.slice(0, values.length - back.length);
values = back.concat(front).reverse();
return values.join(';') + ';' + values[0];
}
var IOS_SPINNER = {
sw: 4,
lc: ROUND,
line: [{
fn: function(i, spinnerName) {
return {
y1: spinnerName == 'ios' ? 17 : 12,
y2: spinnerName == 'ios' ? 29 : 20,
t: TRANSLATE32 + ' rotate(' + (30 * i + (i < 6 ? 180 : -180)) + ')',
a: [{
fn: function() {
return {
an: STROKE_OPACITY,
dur: DURATION,
v: animationValues('0;.1;.15;.25;.35;.45;.55;.65;.7;.85;1', i),
rc: INDEFINITE
};
},
t: 1
}]
};
},
t: 12
}]
};
var spinners = {
android: {
c: [{
sw: 6,
da: 128,
os: 82,
r: 26,
cx: 32,
cy: 32,
f: NONE
}]
},
ios: IOS_SPINNER,
'ios-small': IOS_SPINNER,
bubbles: {
sw: 0,
c: [{
fn: function(i) {
return {
cx: 24 * Math.cos(2 * Math.PI * i / 8),
cy: 24 * Math.sin(2 * Math.PI * i / 8),
t: TRANSLATE32,
a: [{
fn: function() {
return {
an: 'r',
dur: DURATION,
v: animationValues('1;2;3;4;5;6;7;8', i),
rc: INDEFINITE
};
},
t: 1
}]
};
},
t: 8
}]
},
circles: {
c: [{
fn: function(i) {
return {
r: 5,
cx: 24 * Math.cos(2 * Math.PI * i / 8),
cy: 24 * Math.sin(2 * Math.PI * i / 8),
t: TRANSLATE32,
sw: 0,
a: [{
fn: function() {
return {
an: 'fill-opacity',
dur: DURATION,
v: animationValues('.3;.3;.3;.4;.7;.85;.9;1', i),
rc: INDEFINITE
};
},
t: 1
}]
};
},
t: 8
}]
},
crescent: {
c: [{
sw: 4,
da: 128,
os: 82,
r: 26,
cx: 32,
cy: 32,
f: NONE,
at: [SPIN_ANIMATION]
}]
},
dots: {
c: [{
fn: function(i) {
return {
cx: 16 + (16 * i),
cy: 32,
sw: 0,
a: [{
fn: function() {
return {
an: 'fill-opacity',
dur: DURATION,
v: animationValues('.5;.6;.8;1;.8;.6;.5', i),
rc: INDEFINITE
};
},
t: 1
}, {
fn: function() {
return {
an: 'r',
dur: DURATION,
v: animationValues('4;5;6;5;4;3;3', i),
rc: INDEFINITE
};
},
t: 1
}]
};
},
t: 3
}]
},
lines: {
sw: 7,
lc: ROUND,
line: [{
fn: function(i) {
return {
x1: 10 + (i * 14),
x2: 10 + (i * 14),
a: [{
fn: function() {
return {
an: 'y1',
dur: DURATION,
v: animationValues('16;18;28;18;16', i),
rc: INDEFINITE
};
},
t: 1
}, {
fn: function() {
return {
an: 'y2',
dur: DURATION,
v: animationValues('48;44;36;46;48', i),
rc: INDEFINITE
};
},
t: 1
}, {
fn: function() {
return {
an: STROKE_OPACITY,
dur: DURATION,
v: animationValues('1;.8;.5;.4;1', i),
rc: INDEFINITE
};
},
t: 1
}]
};
},
t: 4
}]
},
ripple: {
f: NONE,
'fill-rule': 'evenodd',
sw: 3,
circle: [{
fn: function(i) {
return {
cx: 32,
cy: 32,
a: [{
fn: function() {
return {
an: 'r',
begin: (i * -1) + 's',
dur: '2s',
v: '0;24',
keyTimes: '0;1',
keySplines: '0.1,0.2,0.3,1',
calcMode: 'spline',
rc: INDEFINITE
};
},
t: 1
}, {
fn: function() {
return {
an: STROKE_OPACITY,
begin: (i * -1) + 's',
dur: '2s',
v: '.2;1;.2;0',
rc: INDEFINITE
};
},
t: 1
}]
};
},
t: 2
}]
},
spiral: {
defs: [{
linearGradient: [{
id: 'sGD',
gradientUnits: 'userSpaceOnUse',
x1: 55, y1: 46, x2: 2, y2: 46,
stop: [{
offset: 0.1,
class: 'stop1'
}, {
offset: 1,
class: 'stop2'
}]
}]
}],
g: [{
sw: 4,
lc: ROUND,
f: NONE,
path: [{
stroke: 'url(#sGD)',
d: 'M4,32 c0,15,12,28,28,28c8,0,16-4,21-9'
}, {
d: 'M60,32 C60,16,47.464,4,32,4S4,16,4,32'
}],
at: [SPIN_ANIMATION]
}]
}
};
var animations = {
android: function(ele) {
// Note that this is called as a function, not a constructor.
var self = {};
this.stop = false;
var rIndex = 0;
var rotateCircle = 0;
var startTime;
var svgEle = ele.querySelector('g');
var circleEle = ele.querySelector('circle');
function run() {
if (self.stop) return;
var v = easeInOutCubic(Date.now() - startTime, 650);
var scaleX = 1;
var translateX = 0;
var dasharray = (188 - (58 * v));
var dashoffset = (182 - (182 * v));
if (rIndex % 2) {
scaleX = -1;
translateX = -64;
dasharray = (128 - (-58 * v));
dashoffset = (182 * v);
}
var rotateLine = [0, -101, -90, -11, -180, 79, -270, -191][rIndex];
setSvgAttribute(circleEle, 'da', Math.max(Math.min(dasharray, 188), 128));
setSvgAttribute(circleEle, 'os', Math.max(Math.min(dashoffset, 182), 0));
setSvgAttribute(circleEle, 't', 'scale(' + scaleX + ',1) translate(' + translateX + ',0) rotate(' + rotateLine + ',32,32)');
rotateCircle += 4.1;
if (rotateCircle > 359) rotateCircle = 0;
setSvgAttribute(svgEle, 't', 'rotate(' + rotateCircle + ',32,32)');
if (v >= 1) {
rIndex++;
if (rIndex > 7) rIndex = 0;
startTime = Date.now();
}
ionic.requestAnimationFrame(run);
}
return function() {
startTime = Date.now();
run();
return self;
};
}
};
function easeInOutCubic(t, c) {
t /= c / 2;
if (t < 1) return 1 / 2 * t * t * t;
t -= 2;
return 1 / 2 * (t * t * t + 2);
}
IonicModule
.controller('$ionicSpinner', [
'$element',
'$attrs',
'$ionicConfig',
function($element, $attrs, $ionicConfig) {
var spinnerName, anim;
this.init = function() {
spinnerName = $attrs.icon || $ionicConfig.spinner.icon();
var container = document.createElement('div');
createSvgElement('svg', {
viewBox: '0 0 64 64',
g: [spinners[spinnerName]]
}, container, spinnerName);
// Specifically for animations to work,
// Android 4.3 and below requires the element to be
// added as an html string, rather than dynmically
// building up the svg element and appending it.
$element.html(container.innerHTML);
this.start();
return spinnerName;
};
this.start = function() {
animations[spinnerName] && (anim = animations[spinnerName]($element[0])());
};
this.stop = function() {
animations[spinnerName] && (anim.stop = true);
};
}]);
})(ionic);