animation-stepper
Version:
Javascript library to handle several animations at once using a singleton approach. One window.requestAnimationFrame instance will be running in the background to orchest all animations.
474 lines (348 loc) • 12.7 kB
Plain Text
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
/*
var as = as || {};
as = fuction(){
return
}
*/
/**
* animation-stepper
* Utility library that manages javascript animations in the UI
* @version v1.2.0 - 2017-12-14
* @link https://github.com/ajsoriar/animation-stepper
* @author Andres J. Soria R. <ajsoriar@gmail.com>
* @license MIT License, http://www.opensource.org/licenses/MIT
*/
(function() {
"use strict";
// window.AS.mainAnimation.flags.executeDefaultFunc = false;
window.AS = {
//mainAnimationID: null,
mainAnimation: {
id: null,
defaultFunction: function() {
if ( this.flags.executeDefaultFunc ){
console.log(Date.now());
}
},
flags:{
executeDefaultFunc:true //,
//executeAttachedFunc:true
},
attachedFuncs: [] // array of functions attached to main animation that should be executed.
},
//arrOfActions: [], // Actions executed by
//arrOfAnimations: [],
//onAnimationStep: function(){ }
attachedAnimations: {
// animation func structure
/*
{
id: null;
func: null;
duration: null;
}
*/
}
};
// ------------------
// Internal
// ------------------
var requestId = 0;
var animationStartTime = 0;
/*
var startMainAnimation = function() {
// AS.mainAnimation =
};
*/
function animate(time) {
//document.getElementById("animated").style.left = (time - animationStartTime) % 2000 / 4 + "px";
AS.mainAnimation.defaultFunction();
//AS.mainAnimation.attachedFunctions(); // Execute attached functios
executeFuncs( window.AS.mainAnimation.arrOfFuncs ); // ececutes functions in main threat
//executeFuncs( window.AS.attachedAnimations.arrOfFuncs );
requestId = window.requestAnimationFrame(animate);
}
function executeFuncs( funcsArray ) {
if ( funcsArray === undefined || funcsArray.length === 0 ) return
var lon = funcsArray.length;
for ( var i = 0; i < lon; i++ ){
var f = funcsArray[i];
f();
}
}
function start() {
//animationStartTime = window.performance.now();
animationStartTime = Date.now();
requestId = window.requestAnimationFrame(animate);
}
function stop() {
if (requestId)
window.cancelAnimationFrame(requestId);
requestId = 0;
}
//mainAnimationID
function attachFunctionToMainAnimation() {
}
function attachFunctionToAnimation() {
}
// ------------------
// Public
// ------------------
AS.init = function ( el ) {
console.log("AS.init()");
/*
if (!el){
this.createBoard();
} else {
this.el = document.getElementById( el );
}
*/
//startMainAnimation();
//this.
start();
return this
};
/*
AS.start = function() {
};
AS.stop = function() { // Stops all
};
*/
/*
AS.getLineString = function (idString,x1,y1,x2,y2,weight,color,opacity, roundBorder, longSombra, colSombra){
};
AS.getFastLineString = function (x1,y1,x2,y2,weight,color){
};
*/
/*
AS.createAnimation = function() {
};
*/
AS.attachAnimation = function( func, funcID, targetID ) {
if ( funcID === null ) {
//console.error("funcID is needed!");
}
if ( target != null ){
// Search target
if ( findTarget( targetID ) ) {
attachFunctionToAnimation( targetID, func );
} else {
// error!
//console.error("targetID was specified but not found!");
}
return
}
// Attach function to main animation
attachFunctionToMainAnimation( func );
};
})();
// ----------------------------------------------------------------------
// myStuff.js
// ----------------------------------------------------------------------
AS.init();
// ----------------------------------------------------------------------
// demo.html
// ----------------------------------------------------------------------
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Script-based animation using requestAnimationFrame</title>
<style>
body {
background-color: black;
}
div {
position: absolute;
left: 10px;
padding: 50px;
background: rgb(31, 31, 31);
color: rgb(77, 77, 77)
}
</style>
<script>
/*
Opera engineer Erik Möller wrote about rAF and developed a polyfill that better handles
browsers without native support. You can read about it, but basically his code will choose
a delay of between 4ms and 16ms in order to more closely match 60fps. Here it is, in case
you’d like to use it. Note it uses the standard method name. I have also fixed the cancel*
method’s name, as it has changed in WebKit.
*/
/*
(function() {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
*/
(function() {
var lastTime = 0,
vendors = ['ms', 'moz', 'webkit', 'o'],
x,
length,
currTime,
timeToCall;
for(x = 0, length = vendors.length; x < length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
currTime = new Date().getTime();
timeToCall = Math.max(0, 16 - (currTime - lastTime));
lastTime = currTime + timeToCall;
return window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
// requestAnimationFrame polyfill ends here.
</script>
</head>
<body>
<div id="root"></div>
<div id="animated">Hello there.</div>
<script type="text/javascript" src="animation-stepper.js"></script>
<script type="text/javascript" src="myStuff.js"></script>
</body>
</html>
<script>
var requestId = 0;
function animate(time) {
document.getElementById("animated").style.left =
(time - animationStartTime) % 2000 / 4 + "px";
requestId = window.requestAnimationFrame(animate);
}
function start() {
//animationStartTime = window.performance.now();
animationStartTime = Date.now();
requestId = window.requestAnimationFrame(animate);
}
function stop() {
if (requestId)
window.cancelAnimationFrame(requestId);
requestId = 0;
}
<button onclick="start()">Click me to start!</button>
<button onclick="stop()">Click me to stop!</button>
https://www.w3.org/TR/animation-timing/
// ----------------------------------------------------------------------
// code.js
// ----------------------------------------------------------------------
function inOutQuad(n) {
n *= 2;
if (n < 1) return 0.5 * n * n;
return -0.5 * (--n * (n - 2) - 1);
}
window.myFunction = function( value ) {
//console.log("Do it! stop:", window.stop );
if ( value === 'CANCEL-PREVIOWS' ){
console.log("btn. Cancel previous and launch");
startAnimation($('#thing'));
}
if ( value === 'IF-PREVIOUS-FINISHED' ){
if ( window.stop != false ) {
startAnimation($('#thing'));
}else{
console.log("btn. Previous is running so can't launch");
}
}
if ( value === 'ADD-TO-QUEUE'){
window.queue++;
}
}
window.queue = 0;
window.stop = false;
var animation = {};
animation.afterLaunch = function(){
console.log("animation.afterLaunch!");
};
animation.whenFinished = function(){
console.log("animation.whenFinished!");
//check the queue
if ( window.queue != 0) {
window.queue--;
requestAnimationFrame(startAnim);
}
};
animation.onEveryStep = function(){
console.log("animation.onEveryStep!");
};
function startAnimation(domEl) {
animation.afterLaunch();
window.stop = false;
// animating x (margin-left) from 20 to 300, for example
var startx = 20;
var destx = 300;
var duration = 2000; //5000
var start = null;
var end = null;
// (1) (2) (3) (4)
// 1------2------3------4------5
// 0.0s 0.5s 1.0s 1.5s 2.0s
// 0ms 500ms 1000ms 1500ms 2000ms
var numOfSteps = 5;
var stepTime = duration / (numOfSteps - 1); // 2000/4 = 500ms
var stepfunction = function(stepNum, stepTime) {
console.log("doit!", stepNum, " - ", stepTime);
}
var stepsCount = 0;
var stepsTimes = [];
// calculate steps times...
stepsTimes[0] = 0;
for (var i = 1; i < numOfSteps; i++) {
stepsTimes[i] = stepsTimes[i - 1] + stepTime;
}
function startAnim(timeStamp) {
console.log(timeStamp);
start = timeStamp;
end = start + duration;
draw(timeStamp);
}
function draw(now) {
var t = now - start;
if (t >= stepsTimes[stepsCount]) {
stepfunction(stepsCount + 1, t);
stepsCount++;
animation.onEveryStep();
}
if (window.stop) {
animation.whenFinished();
return
};
if (now - start >= duration) window.stop = true;
var p = (now - start) / duration;
val = inOutQuad(p);
x = startx + (destx - startx) * val;
$(domEl).css('margin-left', `${x}px`);
requestAnimationFrame(draw);
}
requestAnimationFrame(startAnim);
}
startAnimation($('#thing'))