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
if
conditionals occur on the following code:1)
if (tw[f]["from_orig"] === 'current'); // line 53
2)
if (typeof(a) == "object"); // line 155
There is no
TRUE
state 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.