This is a small library I wrote for creating simple and complicated easing effects without using JQuery, MooTools or other JS libraries.
The library is small 13.4 KB or 5.6 KB compressed,
The function can be called as follows:
tween(element, property, from, to, duration, [optional] function)
Parameters
element: The idĀ of the HTML element you want to animate likeĀ “my_div”, “left_sidebar”, DOM objects are also accepted
property: The CSS property that you want to animate like “color”, background-color”, “left”, “margin-top”…
from: The initial value, like 0px, top, none, 0.5…
to: The final value
duration: the animation duration in ms
function (optional): the easing function to use, by default the tween function uses the linear tween, there are nine other functions included (easeInBack, easeOutBack, easeInOutBack, easeInCirc, easeOutCirc, easeInOutCirc, easeInBounce, easeOutBounce & easeInOutBounce)
Examples
Minified version
Full version
var fps = 40;
var debugging = 0;
var tw = [];
var rand_str = generatePassword();
var float_pcre = new RegExp("(\+|-)?(\d*\.)?\d+", 'g');
var color_at_end = new RegExp("[Cc]olor$");
function tween(element, property, from, to, duration) {
if (arguments.length = 0.75) {
for (var i = 0; i < tw.length; ++i) {
if (tw[i]["status"] === "playing") break;
if (i == tw.length - 1) {
tw = [];
}
}
}
var tw_id = tw.length;
tw[tw_id] = [];
tw[tw_id]["f"] = (Boolean(arguments[5])) ? arguments[5] : "linear";
tw[tw_id]["ez"] = 0; // defined here to avoid collisions on simultaneous tw
tw[tw_id]["status"] = "playing"; // playing, aborted, complete
tw[tw_id]["element"] = resolve_element(element);
tw[tw_id]["property"] = resolve_property(property);
if (Boolean(tw[tw_id]["property"].match(color_at_end))) tw[tw_id]["is_color_tween"] = true;
tw[tw_id]["duration"] = duration; //ms
tw[tw_id]["step"] = 0;
tw[tw_id]["interval"] = 1000 / fps; //ms
tw[tw_id]["steps"] = Math.ceil(duration / tw[tw_id]["interval"]);
tw[tw_id]["from_orig"] = from; // '', 'current', '1', '30%' ...
tw[tw_id]["to_orig"] = to;
tw[tw_id]["timer"] = false;
// Stop simultaneous
for (var kounter = 0; kounter < tw_id; ++kounter) {
if (tw[kounter]["status"] !== "playing") continue;
if (tw[kounter]["element"] !== tw[tw_id]["element"]) continue;
if (tw[kounter]["property"] !== tw[tw_id]["property"]) continue;
// We arrived here! that means 2 tw on the same element with the same property, keep the newest tween..
tw[kounter]["status"] = "aborted";
clearInterval(tw[kounter]["timer"]);
}
if (tw[tw_id]["from_orig"] === 'current') {
tw[tw_id]["from"] = getStyle(tw[tw_id]["element"], tw[tw_id]["property"]);
if (tw[tw_id]["from"] === '' || tw[tw_id]["from"] === 'NaN' || typeof (tw[tw_id]["from"]) === "undefined") tw[tw_id]["from"] = tw[tw_id]["element"].style[tw[tw_id]["property"]];
if (tw[tw_id]["from"] === '' || tw[tw_id]["from"] === 'NaN' || typeof (tw[tw_id]["from"]) === "undefined") {
if (tw[tw_id]["is_color_tween"]) tw[tw_id]["from"] = "#7F7F7F";
else tw[tw_id]["from"] = '0';
}
// Format the color array
if (tw[tw_id]["is_color_tween"]) tw[tw_id]["from"] = resolve_color(tw[tw_id]["from"]);
}
if (tw[tw_id]["is_color_tween"]) {
if (tw[tw_id]["from_orig"] === 'current'); // tw[tw_id]["from"] = tw[tw_id]["from"];
else tw[tw_id]["from"] = resolve_color(tw[tw_id]["from_orig"]);
tw[tw_id]["to"] = resolve_color(tw[tw_id]["to_orig"]);
tw[tw_id]["change"] = [];
tw[tw_id]["change"]["red"] = tw[tw_id]["to"]["red"] - tw[tw_id]["from"]["red"];
tw[tw_id]["change"]["green"] = tw[tw_id]["to"]["green"] - tw[tw_id]["from"]["green"];
tw[tw_id]["change"]["blue"] = tw[tw_id]["to"]["blue"] - tw[tw_id]["from"]["blue"];
} else {
if (tw[tw_id]["from_orig"] !== '') {
// tmp is a temporary variable that I use for retrieving prefix and suffix
if (tw[tw_id]["from_orig"] === 'current') {
try {
tw[tw_id]["from"] = tw[tw_id]["from"].match(float_pcre)
} catch (e) {
tw[tw_id]["from"] = 0;
}
} else tw[tw_id]["from"] = tw[tw_id]["from_orig"].match(float_pcre);
tw[tw_id]["from"] = parseFloat(tw[tw_id]["from"]);
tw[tw_id]["to"] = parseFloat(tw[tw_id]["to_orig"].match(float_pcre));
tw[tw_id]["change"] = tw[tw_id]["to"] - tw[tw_id]["from"];
var tmp;
if (tw[tw_id]["from_orig"] === 'current') tmp = tw[tw_id]["to_orig"];
else tmp = tw[tw_id]["from_orig"];
tmp = tmp.replace(float_pcre, rand_str);
tw[tw_id]["prefix"] = tmp.slice(0, tmp.indexOf(rand_str));
tw[tw_id]["suffix"] = str_replace(tw[tw_id]["prefix"] + rand_str, '', tmp);
}
}
// debuggg...
if (debugging) {
var tw_info = new String();
var elm_props = ['id', 'tagName', 'className'];
for (var i in tw[tw_id]) {
tw_info += i + ": ";
if (i !== "element" && typeof (tw[tw_id][i]) === typeof (Object())) {
for (var j in tw[tw_id][i])
tw_info += "nt" + j + ": " + tw[tw_id][i][j];
} else if (i === "element") {
for (var j = 0; j < elm_props.length; j++)
tw_info += "nt" + elm_props[j] + ": " + tw[tw_id][i][elm_props[j]];
} else tw_info += tw[tw_id][i];
tw_info += "nn";
}
alert(tw_info);
}
if (tw[tw_id]["from_orig"] === "") {
tw[tw_id]["status"] = "playing";
tw[tw_id]["timer"] = setTimeout("tw[" + tw_id + "]["element"].style[tw[" + tw_id + "]["property"]] = tw[" + tw_id + "]["to_orig"];tw[" + tw_id + "]["status"] = "complete"", tw[tw_id]["duration"]);
} else ease(tw_id);
return tw_id;
}
function ease(tw_id) {
if (tw[tw_id]["step"] === 0) {
tw[tw_id]["status"] = "playing";
if (tw[tw_id]["is_color_tween"]) tw[tw_id]["element"].style[tw[tw_id]["property"]] = "rgb(" + tw[tw_id]["from"]["red"] + "," + tw[tw_id]["from"]["green"] + "," + tw[tw_id]["from"]["blue"] + ")";
else tw[tw_id]["element"].style[tw[tw_id]["property"]] = tw[tw_id]["prefix"] + tw[tw_id]["from"] + tw[tw_id]["suffix"];
}
tw[tw_id]["step"]++;
if (tw[tw_id]["step"] = tw[tw_id]["steps"]) {
tw[tw_id]["status"] = "complete";
}
}
function ease_repeat(tw_id) {
try {
tw[tw_id]["element"].style[tw[tw_id]["property"]] = tw[tw_id]["ez"];
ease(tw_id);
} catch (e) {}
}
/* Keep it simple with these nice functions */
function h2d(h) {
// hex to decimal
return parseInt(h, 16);
}
function str_replace(search, replace, subject, count) {
// version: 908.406
// discuss at: http://phpjs.org/functions/str_replace
var i = 0,
j = 0,
temp = '',
repl = '',
sl = 0,
fl = 0,
f = [].concat(search),
r = [].concat(replace),
s = subject,
ra = r instanceof Array,
sa = s instanceof Array;
s = [].concat(s);
if (count) this.window[count] = 0;
for (i = 0, sl = s.length; i < sl; i++) {
if (s[i] === '') continue;
for (j = 0, fl = f.length; j "MozBorderRadius";
var matched;
var dash_letter = new RegExp("-[a-z]");
while (1) {
if (matched = property.match(dash_letter)) {
matched = matched.toString();
property = str_replace(matched, matched.charAt(1).toUpperCase(), property);
} else break;
}
return property;
}
function resolve_color(color) {
// return an array containing R, G and B values
if (color === 'transparent') // IE (6 and ?)
color = '#FFF';
var r, g, b;
var hex_color_pcre = new RegExp("^#[0-9a-f]{3}([0-9a-f]{3})?$", 'gi');
var rgb_color_pcre = new RegExp("rgb\(\s*((?:[0-2]?[0-9])?[0-9])\s*,\s*((?:[0-2]?[0-9])?[0-9])\s*,\s*((?:[0-2]?[0-9])?[0-9])\s*\)$", 'gi');
var rgb_percent_color_pcre = new RegExp("rgb\(\s*((?:[0-1]?[0-9])?[0-9])%\s*,\s*((?:[0-1]?[0-9])?[0-9])%\s*,\s*((?:[0-1]?[0-9])?[0-9])%\s*\)$", 'gi');
if (color.match(hex_color_pcre)) {
if (color.length == 4) {
r = color.charAt(1) + "" + color.charAt(1);
g = color.charAt(2) + "" + color.charAt(2);
b = color.charAt(3) + "" + color.charAt(3);
} else {
r = color.charAt(1) + "" + color.charAt(2);
g = color.charAt(3) + "" + color.charAt(4);
b = color.charAt(5) + "" + color.charAt(6);
}
r = h2d(r);
g = h2d(g);
b = h2d(b);
} else if (color.match(rgb_color_pcre)) {
r = RegExp.$1;
g = RegExp.$2;
b = RegExp.$3;
} else if (color.match(rgb_percent_color_pcre)) {
r = parseInt((RegExp.$1) * 2.55);
g = parseInt((RegExp.$2) * 2.55);
b = parseInt((RegExp.$3) * 2.55);
} else return false;
var returned = [];
returned['red'] = r;
returned['green'] = g;
returned['blue'] = b;
return returned;
}
function getStyle(oElm, strCssRule) {
// http://robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
var strValue = "";
if (document.defaultView && document.defaultView.getComputedStyle) {
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
} else if (oElm.currentStyle) {
strCssRule = strCssRule.replace(/-(w)/g, function (strMatch, p1) {
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
}
function generatePassword(len) {
// https://kadimi.com/en/javascript/random-password-string/
len = parseInt(len);
if (!len) len = 6;
var password = "";
var chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var charsN = chars.length;
var nextChar;
for (i = 0; i < len; i++) {
nextChar = chars.charAt(Math.floor(Math.random() * charsN));
password += nextChar
}
return password;
}
function substr(f_string, f_start, f_length) {
// version: 908.406
// discuss at: http://phpjs.org/functions/substr
f_string += '';
if (f_start < 0) {
f_start += f_string.length;
}
if (f_length == undefined) {
f_length = f_string.length;
} else if (f_length < 0) {
f_length += f_string.length;
} else {
f_length += f_start;
}
if (f_length 1000000000 && timeStamp < 2000000000
var now = new Date().getTime() / 1000;
var s = parseInt(now, 10);
return (get_as_float) ? now : (Math.round((now - s) * 1000) / 1000) + ' ' + s;
}
// My favorite $()
function $() {
//http://v3.thewatchmakerproject.com/code/extended-dollar.txt
var elements = new Array();
for (var i = 0, len = arguments.length; i < len; i++) {
var element = arguments[i];
if (typeof element == 'string') {
var matched = document.getElementById(element);
if (matched) {
elements.push(matched);
} else {
var allels = (document.all) ? document.all : document.getElementsByTagName('*');
var regexp = new RegExp('(^| )' + element + '( |$)');
for (var i = 0, len = allels.length; i < len; i++) if (regexp.test(allels[i].className)) elements.push(allels[i]);
}
if (!elements.length) elements = document.getElementsByTagName(element);
if (!elements.length) {
elements = new Array();
var allels = (document.all) ? document.all : document.getElementsByTagName('*');
for (var i = 0, len = allels.length; i < len; i++) if (allels[i].getAttribute(element)) elements.push(allels[i]);
}
if (!elements.length) {
var allels = (document.all) ? document.all : document.getElementsByTagName('*');
for (var i = 0, len = allels.length; i < len; i++) if (allels[i].attributes) for (var j = 0, lenn = allels[i].attributes.length; j < lenn; j++) if (allels[i].attributes[j].specified) if (allels[i].attributes[j].nodeValue == element) elements.push(allels[i]);
}
} else {
elements.push(element);
}
}
if (elements.length == 1) {
return elements[0];
} else {
return elements;
}
}
// Ease function(s)
linear = function (t, b, c, d) {
return c * t / d + b;
};
// ease functions
// Back
easeInBack = function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c * (t /= d) * t * ((s + 1) * t - s) + b;
};
easeOutBack = function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
};
easeInOutBack = function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
};
//Circulat
easeInCirc = function (t, b, c, d) {
return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
};
easeOutCirc = function (t, b, c, d) {
return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
};
easeInOutCirc = function (t, b, c, d) {
if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
};
//Bounce
easeInBounce = function (t, b, c, d) {
return c - easeOutBounce(d - t, 0, c, d) + b;
};
easeOutBounce = function (t, b, c, d) {
if ((t /= d) < (1 / 2.75)) {
return c * (7.5625 * t * t) + b;
} else if (t < (2 / 2.75)) {
return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
} else if (t < (2.5 / 2.75)) {
return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
} else {
return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
}
};
easeInOutBounce = function (t, b, c, d) {
if (t < d / 2) return easeInBounce(t * 2, 0, c, d) * .5 + b;
return easeOutBounce(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
};
//*/
Thanks Kadimi for sharing this tween() javascript function:
I have taken the original ‘minimized’ tween() js code and expanded it back to a human readable source. I have corrected some JSLint issues presented with code and additionally corrected two js ‘if conditionals’ that had no ‘TRUE’ state code to execute. I think this code you have created is educational in human readable code format:
Peter Bowey
Thank you for your interest, this small library is a draft and I will put more effort on improving if I see that people like you are willing to support it by providing bug reports and feedback.
I posted the full version of the function and I will – in the near future – have a repository and a bug tracker.
Notes: The two (original) failed
ifconditionals occur on the following code:1)
if (tw[f]["from_orig"] === 'current'); // line 532)
if (typeof(a) == "object"); // line 155There is no
TRUEstate code to execute after either of the above conditions. Note the semicolon;ending each conditionPeter Bowey
I wrote that on purpose, this doesn’t hurt but sure there is a better way to write it.
Actually I should have wrote a comment to remind me why I coded it that way… oops.
And y the way, I forgot to tell you that I was very busy with my personal life, I apologize for not responding to your comments earlier.