Very rough early stuff

This commit is contained in:
Max Lynch
2014-05-01 10:56:35 -05:00
committed by Andy Joslin
parent dffb3bec46
commit 0b47d80f90
7 changed files with 4641 additions and 1 deletions

View File

@@ -60,6 +60,9 @@ module.exports = {
// Controllers
'js/controllers/viewController.js',
'js/controllers/sideMenuController.js',
// Animation
'js/animation/*.js'
],
angularIonicFiles: [

View File

@@ -11,6 +11,35 @@
* ```js
* angular.module('mySuperApp', ['ionic'])
* .controller(function($scope, $ionicAnimation) {
* var anim = $ionicAnimate({
* // A unique, reusable name
* name: 'popIn',
*
* // The duration of an auto playthrough
* duration: 0.5,
*
* // How long to wait before running the animation
* delay: 0,
*
* // Whether to reverse after doing one run through
* autoReverse: false,
*
* // How many times to repeat? -1 or null for infinite
* repeat: -1,
*
* // Timing curve to use (same as CSS timing functions), or a function of time "t" to handle it yourself
* curve: 'ease-in-out'
*
* onStart: function() {
* // Callback on start
* },
* onEnd: function() {
* // Callback on end
* },
* step: function(amt) {
*
* }
* })
* });
* ```
*
@@ -23,4 +52,7 @@ IonicModule
'$timeout',
'$interval',
function($rootScope, $document, $compile, $timeout, $interval) {
});
return function(opts) {
return ionic.Animation.create(opts);
}
}]);

211
js/animation/animation.js Normal file
View File

@@ -0,0 +1,211 @@
(function(window) {
var time = Date.now || function() {
return +new Date();
};
var desiredFrames = 60;
var millisecondsPerSecond = 1000;
var running = {};
var counter = 1;
// Namespace
ionic.Animation = {};
/**
* The main animation system manager. Treated as a singleton.
*/
ionic.Animation = {
anims: [],
add: function(animation) {
this.anims.push(animation);
},
create: function(opts) {
return new ionic.Animation.Animation(opts);
},
remove: function(animation) {
var i, j;
for(i = 0, j = this.anims.length; i < j; i++) {
if(this.anims[i] === animation) {
return this.anims.splice(i, 1);
}
}
},
clear: function(shouldStop) {
while(this.anims.length) {
var anim = this.anims.pop();
if(shouldStop === true) {
anim.stop();
}
}
},
/**
* Stops the given animation.
*
* @param id {Integer} Unique animation ID
* @return {Boolean} Whether the animation was stopped (aka, was running before)
*/
stop: function(id) {
var cleared = running[id] != null;
if (cleared) {
running[id] = null;
}
return cleared;
},
/**
* Whether the given animation is still running.
*
* @param id {Integer} Unique animation ID
* @return {Boolean} Whether the animation is still running
*/
isRunning: function(id) {
return running[id] != null;
},
};
/**
* Animation instance
*/
ionic.Animation.Animation = function(opts) {
ionic.extend(this, opts);
};
ionic.Animation.Animation.prototype = {
el: null,
curve: 'linear',
duration: 500,
delay: 0,
repeat: -1,
stop: function() {
},
start: function() {
var self = this;
var tf;
console.log('Starting animation', this);
// Grab the timing function
if(typeof this.curve === 'string') {
tf = ionic.Animation.TimingFn[this.curve] || ionic.Animation.TimingFn['linear'];
} else {
tf = this.curve;
}
// Get back a timing function for the given duration (used for precision)
tf = tf(this.duration);
return this._run(function(percent, now, virtual) {
//console.log('Animation step', percent, now, virtual);
self.el[0].style[ionic.CSS.TRANSFORM] = 'translate3d(' + (percent * 400) + 'px, 0,0)';
}, function() {
return true;
}, function(droppedFrames, finishedAnimation) {
console.log('Finished anim:', droppedFrames, finishedAnimation);
}, this.duration, tf);
},
/**
* Start the animation.
*
* @param stepCallback {Function} Pointer to function which is executed on every step.
* Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }`
* @param verifyCallback {Function} Executed before every animation step.
* Signature of the method should be `function() { return continueWithAnimation; }`
* @param completedCallback {Function}
* Signature of the method should be `function(droppedFrames, finishedAnimation) {}`
* @param duration {Integer} Milliseconds to run the animation
* @param easingMethod {Function} Pointer to easing function
* Signature of the method should be `function(percent) { return modifiedValue; }`
* @param root {Element} Render root, when available. Used for internal
* usage of requestAnimationFrame.
* @return {Integer} Identifier of animation. Can be used to stop it any time.
*/
_run: function(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) {
var start = time();
var lastFrame = start;
var percent = 0;
var dropCounter = 0;
var id = counter++;
if (!root) {
root = document.body;
}
// Compacting running db automatically every few new animations
if (id % 20 === 0) {
var newRunning = {};
for (var usedId in running) {
newRunning[usedId] = true;
}
running = newRunning;
}
// This is the internal step method which is called every few milliseconds
var step = function(virtual) {
// Normalize virtual value
var render = virtual !== true;
// Get current time
var now = time();
// Verification is executed before next animation step
if (!running[id] || (verifyCallback && !verifyCallback(id))) {
running[id] = null;
completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false);
return;
}
// For the current rendering to apply let's update omitted steps in memory.
// This is important to bring internal state variables up-to-date with progress in time.
if (render) {
var droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1;
for (var j = 0; j < Math.min(droppedFrames, 4); j++) {
step(true);
dropCounter++;
}
}
// Compute percent value
if (duration) {
percent = (now - start) / duration;
if (percent > 1) {
percent = 1;
}
}
// Execute step callback, then...
var value = easingMethod ? easingMethod(percent) : percent;
if ((stepCallback(value, now, render) === false || percent === 1) && render) {
running[id] = null;
completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, percent === 1 || duration == null);
} else if (render) {
lastFrame = now;
ionic.requestAnimationFrame(step, root);
}
};
// Mark as running
running[id] = true;
// Init first step
ionic.requestAnimationFrame(step, root);
// Return unique animation ID
return id;
}
};
})(window);

78
js/animation/bezier.js Normal file
View File

@@ -0,0 +1,78 @@
(function(ionic) {
var bezierCoord = function (x,y) {
if(!x) x=0;
if(!y) y=0;
return {x: x, y: y};
};
function B1(t) { return t*t*t; }
function B2(t) { return 3*t*t*(1-t); }
function B3(t) { return 3*t*(1-t)*(1-t); }
function B4(t) { return (1-t)*(1-t)*(1-t); }
ionic.Animation = ionic.Animation || {}
ionic.Animation.Bezier = {
// Quadratic bezier solver
getQuadraticBezier: function(percent,C1,C2,C3,C4) {
var pos = new bezierCoord();
pos.x = C1.x*B1(percent) + C2.x*B2(percent) + C3.x*B3(percent) + C4.x*B4(percent);
pos.y = C1.y*B1(percent) + C2.y*B2(percent) + C3.y*B3(percent) + C4.y*B4(percent);
return pos;
},
// Cubic bezier solver from https://github.com/arian/cubic-bezier (MIT)
getCubicBezier: function(x1, y1, x2, y2, duration) {
// Precision
epsilon = (1000 / 60 / duration) / 4;
var curveX = function(t){
var v = 1 - t;
return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t;
};
var curveY = function(t){
var v = 1 - t;
return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t;
};
var derivativeCurveX = function(t){
var v = 1 - t;
return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (- t * t * t + 2 * v * t) * x2;
};
return function(t) {
var x = t, t0, t1, t2, x2, d2, i;
// First try a few iterations of Newton's method -- normally very fast.
for (t2 = x, i = 0; i < 8; i++){
x2 = curveX(t2) - x;
if (Math.abs(x2) < epsilon) return curveY(t2);
d2 = derivativeCurveX(t2);
if (Math.abs(d2) < 1e-6) break;
t2 = t2 - x2 / d2;
}
t0 = 0, t1 = 1, t2 = x;
if (t2 < t0) return curveY(t0);
if (t2 > t1) return curveY(t1);
// Fallback to the bisection method for reliability.
while (t0 < t1){
x2 = curveX(t2);
if (Math.abs(x2 - x) < epsilon) return curveY(t2);
if (x > x2) t0 = t2;
else t1 = t2;
t2 = (t1 - t0) * 0.5 + t0;
}
// Failure
return curveY(t2);
};
}
};
})(ionic);

4248
js/animation/gl-matrix.js Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
(function(window) {
// Namespace
ionic.Animation = ionic.Animation || {};
ionic.Animation.TimingFn = {
'ease-in-out': function(duration) {
var bz = ionic.Animation.Bezier.getCubicBezier(0.42, 0.0, 0.58, 1.0, duration);
return function(t) {
//console.log(t);
return bz(t);
}
/*
0.42, 0.0, 0.58, 1.0)
t /= d/2;
if (t < 1) return c/2*t*t*t + b;
t -= 2;
return c/2*(t*t*t + 2) + b;
*/
}
};
})(window);

44
test/html/animation.html Normal file
View File

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html ng-app="ionic">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title></title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
<link rel="stylesheet" href="../../../../dist/css/ionic.css" />
<script src="../../../../dist/js/ionic.bundle.js"></script>
<style>
.box {
position: absolute;
width: 100px;
height: 100px;
background-color: black;
}
</style>
</head>
<body>
<div ng-controller="MyCtrl">
<div class="box"></div>
</div>
<script>
function MyCtrl($scope, $ionicAnimation) {
var el = angular.element(document.querySelector('.box'));
var fadeIn = $ionicAnimation({
el: el,
name: 'fadeIn',
duration: 500,
delay: 0,
autoReverse: false,
repeat: -1,
curve: 'ease-in-out'
});
fadeIn.start();
}
</script>
</body>
</html>