mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Very rough early stuff
This commit is contained in:
@@ -60,6 +60,9 @@ module.exports = {
|
||||
// Controllers
|
||||
'js/controllers/viewController.js',
|
||||
'js/controllers/sideMenuController.js',
|
||||
|
||||
// Animation
|
||||
'js/animation/*.js'
|
||||
],
|
||||
|
||||
angularIonicFiles: [
|
||||
|
||||
34
js/angular/service/animation.js
vendored
34
js/angular/service/animation.js
vendored
@@ -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
211
js/animation/animation.js
Normal 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
78
js/animation/bezier.js
Normal 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
4248
js/animation/gl-matrix.js
Normal file
File diff suppressed because it is too large
Load Diff
24
js/animation/timing-functions.js
Normal file
24
js/animation/timing-functions.js
Normal 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
44
test/html/animation.html
Normal 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>
|
||||
Reference in New Issue
Block a user