I'd like to have the ability to run functions at a certain rate, which can increase or decrease according to a mathematical function such as a curve... much the same way easing functions like easeIn
and easeOut
work in CSS and JQuery.
Here's a crude illustration of an "easeInOut" type scenario. The line represents time, the o
s a function call.
o-o--o---o-----o----------o-----o---o--o-o
Implementation could look something like:
trigger(5000, "easeInOut", callback); // Over five seconds, "callback()" is called with an easeInOut ease.
function triggerWithEase(duration, ease, callback){
// ???
}
function callback(){
console.log("Heyo!");
}
Is there a Javascript / JQuery solution for this already? If not, how could this be accomplished?
Some inspiration for the timing graph itself:
http://easings.net/
I'm impressed with you creative folks who have actually used the little ASCII graph I used in the description as an input to the function! I'm looking for a more mathematical solution to this, though...
Here's a better illustration of what I'm thinking. This assumes we're using a quadratic formula (ax^2 + bx + c) over a duration of 20 seconds.
It'd be really cool to pass a function some parameters and have it trigger callbacks at the correct intervals doing something like this:
Some completely untested, back-of-the-napkin pseudocode I'm playing with:
function easeTrigger(callback, functionType, functionOptions, duration, numberOfCalls){
switch("functionType"){
case "quadratic":
quadtratic(functionOptions);
break;
case "sine":
sine(functionOptions);
break;
/* ... */
default:
linear(functionOptions);
break;
}
function quadratic(options){
var x = numberOfCalls;
var result;
var delay;
var timer;
for (var i = x - 1; i >= 0; i--) {
result = a * Math.pow(x, 2) + (b * x) + c;
delay = result * 1000; // using the result to calculate the time between function calls
}
// ???
for (var s = numberOfCalls - 1; s >= 0; s--) {
delay = result * 1000; // probably need to chain this somehow, a for loop might not be the best way to go about this.
timer = setTimeout(result, caller);
}
// ???
// PROFIT!
}
function caller(duration){
clearTimeout(timer);
timer = setTimeout(delay, callback);
}
}
// How this might be implemented...
easeTrigger(callback, "quadratic", {a: 1, b: 2, c:0}, 5000, 20);
easeTrigger(callback, "linear", {a: 1, b: 2}, 5000, 10);
// Callback to call at the end of every period
function callback(){
console.log("Yo!");
}
In the action
you should set whatever is that you need to do on that curve tick you have mentioned.
function TimeLine(unit, curve, action) {
this.unit = unit;
this.curve = curve;
this.action = action;
this.tick = 0;
}
TimeLine.prototype.start = function() {
var me = this;
console.log('Start.');
me.id = setInterval(function() {
me.onRun();
}, me.unit);
me.onRun();
};
TimeLine.prototype.stop = function() {
var me = this;
console.log('Stop.');
clearInterval(me.id);
delete me.id;
};
TimeLine.prototype.onRun = function() {
var me = this;
if (me.curve.charAt(me.tick++) === 'o') {
me.action && me.action();
} else {
console.log('Idle...');
}
if (me.tick > me.curve.length) {
me.stop();
}
}
var log = function() {
console.log('Ping:', (new Date).getTime());
},
t = new TimeLine(200, 'o----o----o--o-o-o-o-ooo', log);
t.start();
EDIT Related to your last edit, calculating the intervals some function will be called, some corrections:
So you are seeing the axis of time like this:
`t0 -------------- tx --------------------------------------- tN`
and you say that on a time interval duration
(=tN-t0) you will call a function a numberOfTimes
so the function will be called in [t0,...,ti,..tx] where x = the numberOfTimes
and each of these times is calculated with some function
function quadratic(options){
var x = numberOfCalls,
delay = 0,
result, , timer;
for (var i = x - 1; i >= 0; i--) {
result = a * Math.pow(x, 2) + (b * x) + c;
delay += result * 1000; // cumulate these time values so you can lunch the call already
// so you call them in a loop but they will start at the precalculated times
setTimeout(caller, delay);
}
}
This will work though I don t see how you will force the duration to be a specific one, unless it is a parameter of the function somehow.
If you are able to use an external lib, then I would suggest you use a reactive lib like xstream
. This is typically the kind of problems reactive programming addresses!
There is a great method on observables that allow you to emit things according to a diagram (which is exactly what you have). The method is fromDiagram
So in your case, you could do
import fromDiagram from 'xstream/extra/fromDiagram'
const stream = fromDiagram('o-o--o---o-----o----------o-----o---o--o-o', {
timeUnit: 20 // the number of ms, would have to be computed manually
})
stream.addListener({
next: callback
})
The only thing left to do is to compute the timeUnit
option according to the duration of your animation and the number of steps.
Add to this an object that associate a name of an easing function to a diagram and wrap everything up in a function, and you are good to go :)
Hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With