123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- /*!
- * jQuery Transit - CSS3 transitions and transformations
- * Copyright(c) 2011 Rico Sta. Cruz <rico@ricostacruz.com>
- * MIT Licensed.
- *
- * http://ricostacruz.com/jquery.transit
- * http://github.com/rstacruz/jquery.transit
- */
- (function($) {
- "use strict";
- $.transit = {
- version: "0.1.3",
- // Map of $.css() keys to values for 'transitionProperty'.
- // See https://developer.mozilla.org/en/CSS/CSS_transitions#Properties_that_can_be_animated
- propertyMap: {
- marginLeft : 'margin',
- marginRight : 'margin',
- marginBottom : 'margin',
- marginTop : 'margin',
- paddingLeft : 'padding',
- paddingRight : 'padding',
- paddingBottom : 'padding',
- paddingTop : 'padding'
- },
- // Will simply transition "instantly" if false
- enabled: true,
- // Set this to false if you don't want to use the transition end property.
- useTransitionEnd: false
- };
- var div = document.createElement('div');
- var support = {};
- // Helper function to get the proper vendor property name.
- // (`transition` => `WebkitTransition`)
- function getVendorPropertyName(prop) {
- var prefixes = ['Moz', 'Webkit', 'O', 'ms'];
- var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1);
- if (prop in div.style) { return prop; }
- for (var i=0; i<prefixes.length; ++i) {
- var vendorProp = prefixes[i] + prop_;
- if (vendorProp in div.style) { return vendorProp; }
- }
- }
- // Helper function to check if transform3D is supported.
- // Should return true for Webkits and Firefox 10+.
- function checkTransform3dSupport() {
- div.style[support.transform] = '';
- div.style[support.transform] = 'rotateY(90deg)';
- return div.style[support.transform] !== '';
- }
- var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
- // Check for the browser's transitions support.
- // You can access this in jQuery's `$.support.transition`.
- // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/),
- // we set $.support.transition to a string of the actual property name used.
- support.transition = getVendorPropertyName('transition');
- support.transitionDelay = getVendorPropertyName('transitionDelay');
- support.transform = getVendorPropertyName('transform');
- support.transformOrigin = getVendorPropertyName('transformOrigin');
- support.transform3d = checkTransform3dSupport();
- $.extend($.support, support);
- var eventNames = {
- 'MozTransition': 'transitionend',
- 'OTransition': 'oTransitionEnd',
- 'WebkitTransition': 'webkitTransitionEnd',
- 'msTransition': 'MSTransitionEnd'
- };
- // Detect the 'transitionend' event needed.
- var transitionEnd = support.transitionEnd = eventNames[support.transition] || null;
- // Avoid memory leak in IE.
- div = null;
- // ## $.cssEase
- // List of easing aliases that you can use with `$.fn.transition`.
- $.cssEase = {
- '_default': 'ease',
- 'in': 'ease-in',
- 'out': 'ease-out',
- 'in-out': 'ease-in-out',
- 'snap': 'cubic-bezier(0,1,.5,1)'
- };
- // ## 'transform' CSS hook
- // Allows you to use the `transform` property in CSS.
- //
- // $("#hello").css({ transform: "rotate(90deg)" });
- //
- // $("#hello").css('transform');
- // //=> { rotate: '90deg' }
- //
- $.cssHooks.transform = {
- // The getter returns a `Transform` object.
- get: function(elem) {
- return $(elem).data('transform');
- },
- // The setter accepts a `Transform` object or a string.
- set: function(elem, v) {
- var value = v;
- if (!(value instanceof Transform)) {
- value = new Transform(value);
- }
- // We've seen the 3D version of Scale() not work in Chrome when the
- // element being scaled extends outside of the viewport. Thus, we're
- // forcing Chrome to not use the 3d transforms as well. Not sure if
- // translate is affectede, but not risking it. Detection code from
- // http://davidwalsh.name/detecting-google-chrome-javascript
- if (support.transform === 'WebkitTransform' && !isChrome) {
- elem.style[support.transform] = value.toString(true);
- } else {
- elem.style[support.transform] = value.toString();
- }
- $(elem).data('transform', value);
- }
- };
- // ## 'transformOrigin' CSS hook
- // Allows the use for `transformOrigin` to define where scaling and rotation
- // is pivoted.
- //
- // $("#hello").css({ transformOrigin: '0 0' });
- //
- $.cssHooks.transformOrigin = {
- get: function(elem) {
- return elem.style[support.transformOrigin];
- },
- set: function(elem, value) {
- elem.style[support.transformOrigin] = value;
- }
- };
- // ## 'transition' CSS hook
- // Allows you to use the `transition` property in CSS.
- //
- // $("#hello").css({ transition: 'all 0 ease 0' });
- //
- $.cssHooks.transition = {
- get: function(elem) {
- return elem.style[support.transition];
- },
- set: function(elem, value) {
- elem.style[support.transition] = value;
- }
- };
- // ## Other CSS hooks
- // Allows you to rotate, scale and translate.
- registerCssHook('scale');
- registerCssHook('translate');
- registerCssHook('rotate');
- registerCssHook('rotateX');
- registerCssHook('rotateY');
- registerCssHook('rotate3d');
- registerCssHook('perspective');
- registerCssHook('skewX');
- registerCssHook('skewY');
- registerCssHook('x', true);
- registerCssHook('y', true);
- // ## Transform class
- // This is the main class of a transformation property that powers
- // `$.fn.css({ transform: '...' })`.
- //
- // This is, in essence, a dictionary object with key/values as `-transform`
- // properties.
- //
- // var t = new Transform("rotate(90) scale(4)");
- //
- // t.rotate //=> "90deg"
- // t.scale //=> "4,4"
- //
- // Setters are accounted for.
- //
- // t.set('rotate', 4)
- // t.rotate //=> "4deg"
- //
- // Convert it to a CSS string using the `toString()` and `toString(true)` (for WebKit)
- // functions.
- //
- // t.toString() //=> "rotate(90deg) scale(4,4)"
- // t.toString(true) //=> "rotate(90deg) scale3d(4,4,0)" (WebKit version)
- //
- function Transform(str) {
- if (typeof str === 'string') { this.parse(str); }
- return this;
- }
- Transform.prototype = {
- // ### setFromString()
- // Sets a property from a string.
- //
- // t.setFromString('scale', '2,4');
- // // Same as set('scale', '2', '4');
- //
- setFromString: function(prop, val) {
- var args =
- (typeof val === 'string') ? val.split(',') :
- (val.constructor === Array) ? val :
- [ val ];
- args.unshift(prop);
- Transform.prototype.set.apply(this, args);
- },
- // ### set()
- // Sets a property.
- //
- // t.set('scale', 2, 4);
- //
- set: function(prop) {
- var args = Array.prototype.slice.apply(arguments, [1]);
- if (this.setter[prop]) {
- this.setter[prop].apply(this, args);
- } else {
- this[prop] = args.join(',');
- }
- },
- get: function(prop) {
- if (this.getter[prop]) {
- return this.getter[prop].apply(this);
- } else {
- return this[prop] || 0;
- }
- },
- setter: {
- // ### rotate
- //
- // .css({ rotate: 30 })
- // .css({ rotate: "30" })
- // .css({ rotate: "30deg" })
- // .css({ rotate: "30deg" })
- //
- rotate: function(theta) {
- this.rotate = unit(theta, 'deg');
- },
- rotateX: function(theta) {
- this.rotateX = unit(theta, 'deg');
- },
- rotateY: function(theta) {
- this.rotateY = unit(theta, 'deg');
- },
- // ### scale
- //
- // .css({ scale: 9 }) //=> "scale(9,9)"
- // .css({ scale: '3,2' }) //=> "scale(3,2)"
- //
- scale: function(x, y) {
- if (y === undefined) { y = x; }
- this.scale = x + "," + y;
- },
- // ### skewX + skewY
- skewX: function(x) {
- this.skewX = unit(x, 'deg');
- },
- skewY: function(y) {
- this.skewY = unit(y, 'deg');
- },
- // ### perspectvie
- perspective: function(dist) {
- this.perspective = unit(dist, 'px');
- },
- // ### x / y
- // Translations. Notice how this keeps the other value.
- //
- // .css({ x: 4 }) //=> "translate(4px, 0)"
- // .css({ y: 10 }) //=> "translate(4px, 10px)"
- //
- x: function(x) {
- this.set('translate', x, null);
- },
- y: function(y) {
- this.set('translate', null, y);
- },
- // ### translate
- // Notice how this keeps the other value.
- //
- // .css({ translate: '2, 5' }) //=> "translate(2px, 5px)"
- //
- translate: function(x, y) {
- if (this._translateX === undefined) { this._translateX = 0; }
- if (this._translateY === undefined) { this._translateY = 0; }
- if (x !== null) { this._translateX = unit(x, 'px'); }
- if (y !== null) { this._translateY = unit(y, 'px'); }
- this.translate = this._translateX + "," + this._translateY;
- }
- },
- getter: {
- x: function() {
- return this._translateX || 0;
- },
- y: function() {
- return this._translateY || 0;
- },
- scale: function() {
- var s = (this.scale || "1,1").split(',');
- if (s[0]) { s[0] = parseFloat(s[0]); }
- if (s[1]) { s[1] = parseFloat(s[1]); }
- // "2.5,2.5" => 2.5
- // "2.5,1" => [2.5,1]
- return (s[0] === s[1]) ? s[0] : s;
- },
- rotate3d: function() {
- var s = (this.rotate3d || "0,0,0,0deg").split(',');
- for (var i=0; i<=3; ++i) {
- if (s[i]) { s[i] = parseFloat(s[i]); }
- }
- if (s[3]) { s[3] = unit(s[3], 'deg'); }
- return s;
- }
- },
- // ### parse()
- // Parses from a string. Called on constructor.
- parse: function(str) {
- var self = this;
- str.replace(/([a-zA-Z0-9]+)\((.*?)\)/g, function(x, prop, val) {
- self.setFromString(prop, val);
- });
- },
- // ### toString()
- // Converts to a `transition` CSS property string. If `use3d` is given,
- // it converts to a `-webkit-transition` CSS property string instead.
- toString: function(use3d) {
- var re = [];
- for (var i in this) {
- if (this.hasOwnProperty(i)) {
- // Don't use 3D transformations if the browser can't support it.
- if ((!support.transform3d) && (
- (i === 'rotateX') ||
- (i === 'rotateY') ||
- (i === 'perspective') ||
- (i === 'transformOrigin'))) { continue; }
- if (i[0] !== '_') {
- if (use3d && (i === 'scale')) {
- re.push(i + "3d(" + this[i] + ",1)");
- } else if (use3d && (i === 'translate')) {
- re.push(i + "3d(" + this[i] + ",0)");
- } else {
- re.push(i + "(" + this[i] + ")");
- }
- }
- }
- }
- return re.join(" ");
- }
- };
- function callOrQueue(self, queue, fn) {
- if (queue === true) {
- self.queue(fn);
- } else if (queue) {
- self.queue(queue, fn);
- } else {
- fn();
- }
- }
- // ### getProperties(dict)
- // Returns properties (for `transition-property`) for dictionary `props`. The
- // value of `props` is what you would expect in `$.css(...)`.
- function getProperties(props) {
- var re = [];
- $.each(props, function(key) {
- key = $.camelCase(key); // Convert "text-align" => "textAlign"
- key = $.transit.propertyMap[key] || key;
- key = uncamel(key); // Convert back to dasherized
- if ($.inArray(key, re) === -1) { re.push(key); }
- });
- return re;
- }
- // ### getTransition()
- // Returns the transition string to be used for the `transition` CSS property.
- //
- // Example:
- //
- // getTransition({ opacity: 1, rotate: 30 }, 500, 'ease');
- // //=> 'opacity 500ms ease, -webkit-transform 500ms ease'
- //
- function getTransition(properties, duration, easing, delay) {
- // Get the CSS properties needed.
- var props = getProperties(properties);
- // Account for aliases (`in` => `ease-in`).
- if ($.cssEase[easing]) { easing = $.cssEase[easing]; }
- // Build the duration/easing/delay attributes for it.
- var attribs = '' + toMS(duration) + ' ' + easing;
- if (parseInt(delay, 10) > 0) { attribs += ' ' + toMS(delay); }
- // For more properties, add them this way:
- // "margin 200ms ease, padding 200ms ease, ..."
- var transitions = [];
- $.each(props, function(i, name) {
- transitions.push(name + ' ' + attribs);
- });
- return transitions.join(', ');
- }
- // ## $.fn.transition
- // Works like $.fn.animate(), but uses CSS transitions.
- //
- // $("...").transition({ opacity: 0.1, scale: 0.3 });
- //
- // // Specific duration
- // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500);
- //
- // // With duration and easing
- // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in');
- //
- // // With callback
- // $("...").transition({ opacity: 0.1, scale: 0.3 }, function() { ... });
- //
- // // With everything
- // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in', function() { ... });
- //
- // // Alternate syntax
- // $("...").transition({
- // opacity: 0.1,
- // duration: 200,
- // delay: 40,
- // easing: 'in',
- // complete: function() { /* ... */ }
- // });
- //
- $.fn.transition = $.fn.transit = function(properties, duration, easing, callback) {
- var self = this;
- var delay = 0;
- var queue = true;
- // Account for `.transition(properties, callback)`.
- if (typeof duration === 'function') {
- callback = duration;
- duration = undefined;
- }
- // Account for `.transition(properties, duration, callback)`.
- if (typeof easing === 'function') {
- callback = easing;
- easing = undefined;
- }
- // Alternate syntax.
- if (typeof properties.easing !== 'undefined') {
- easing = properties.easing;
- delete properties.easing;
- }
- if (typeof properties.duration !== 'undefined') {
- duration = properties.duration;
- delete properties.duration;
- }
- if (typeof properties.complete !== 'undefined') {
- callback = properties.complete;
- delete properties.complete;
- }
- if (typeof properties.queue !== 'undefined') {
- queue = properties.queue;
- delete properties.queue;
- }
- if (typeof properties.delay !== 'undefined') {
- delay = properties.delay;
- delete properties.delay;
- }
- // Set defaults. (`400` duration, `ease` easing)
- if (typeof duration === 'undefined') { duration = $.fx.speeds._default; }
- if (typeof easing === 'undefined') { easing = $.cssEase._default; }
- duration = toMS(duration);
- // Build the `transition` property.
- var transitionValue = getTransition(properties, duration, easing, delay);
- // Compute delay until callback.
- // If this becomes 0, don't bother setting the transition property.
- var work = $.transit.enabled && support.transition;
- var i = work ? (parseInt(duration, 10) + parseInt(delay, 10)) : 0;
- // If there's nothing to do...
- if (i === 0) {
- var fn = function(next) {
- self.css(properties);
- if (callback) { callback.apply(self); }
- if (next) { next(); }
- };
- callOrQueue(self, queue, fn);
- return self;
- }
- // Save the old transitions of each element so we can restore it later.
- var oldTransitions = {};
- var run = function(nextCall) {
- var bound = false;
- // Prepare the callback.
- var cb = function() {
- if (bound) { self.unbind(transitionEnd, cb); }
- if (i > 0) {
- self.each(function() {
- this.style[support.transition] = (oldTransitions[this] || null);
- });
- }
- if (typeof callback === 'function') { callback.apply(self); }
- if (typeof nextCall === 'function') { nextCall(); }
- };
- if ((i > 0) && (transitionEnd) && ($.transit.useTransitionEnd)) {
- // Use the 'transitionend' event if it's available.
- bound = true;
- self.bind(transitionEnd, cb);
- } else {
- // Fallback to timers if the 'transitionend' event isn't supported.
- window.setTimeout(cb, i);
- }
- // Apply transitions.
- self.each(function() {
- if (i > 0) {
- this.style[support.transition] = transitionValue;
- }
- $(this).css(properties);
- });
- };
- // Defer running. This allows the browser to paint any pending CSS it hasn't
- // painted yet before doing the transitions.
- var deferredRun = function(next) {
- var i = 0;
- // Durations that are too slow will get transitions mixed up.
- // (Tested on Mac/FF 7.0.1)
- if ((support.transition === 'MozTransition') && (i < 25)) { i = 25; }
- window.setTimeout(function() { run(next); }, i);
- };
- // Use jQuery's fx queue.
- callOrQueue(self, queue, deferredRun);
- // Chainability.
- return this;
- };
- function registerCssHook(prop, isPixels) {
- // For certain properties, the 'px' should not be implied.
- if (!isPixels) { $.cssNumber[prop] = true; }
- $.transit.propertyMap[prop] = support.transform;
- $.cssHooks[prop] = {
- get: function(elem) {
- var t = $(elem).css('transform') || new Transform();
- return t.get(prop);
- },
- set: function(elem, value) {
- var t = $(elem).css('transform') || new Transform();
- t.setFromString(prop, value);
- $(elem).css({ transform: t });
- }
- };
- }
- // ### uncamel(str)
- // Converts a camelcase string to a dasherized string.
- // (`marginLeft` => `margin-left`)
- function uncamel(str) {
- return str.replace(/([A-Z])/g, function(letter) { return '-' + letter.toLowerCase(); });
- }
- // ### unit(number, unit)
- // Ensures that number `number` has a unit. If no unit is found, assume the
- // default is `unit`.
- //
- // unit(2, 'px') //=> "2px"
- // unit("30deg", 'rad') //=> "30deg"
- //
- function unit(i, units) {
- if ((typeof i === "string") && (!i.match(/^[\-0-9\.]+$/))) {
- return i;
- } else {
- return "" + i + units;
- }
- }
- // ### toMS(duration)
- // Converts given `duration` to a millisecond string.
- //
- // toMS('fast') //=> '400ms'
- // toMS(10) //=> '10ms'
- //
- function toMS(duration) {
- var i = duration;
- // Allow for string durations like 'fast'.
- if ($.fx.speeds[i]) { i = $.fx.speeds[i]; }
- return unit(i, 'ms');
- }
- // Export some functions for testable-ness.
- $.transit.getTransitionValue = getTransition;
- })(jQuery);
|