1/*! 2 * jQuery Transit - CSS3 transitions and transformations 3 * (c) 2011-2012 Rico Sta. Cruz 4 * MIT Licensed. 5 * 6 * http://ricostacruz.com/jquery.transit 7 * http://github.com/rstacruz/jquery.transit 8 */ 9 10(function($) { 11 $.transit = { 12 version: "0.9.9", 13 14 // Map of $.css() keys to values for 'transitionProperty'. 15 // See https://developer.mozilla.org/en/CSS/CSS_transitions#Properties_that_can_be_animated 16 propertyMap: { 17 marginLeft : 'margin', 18 marginRight : 'margin', 19 marginBottom : 'margin', 20 marginTop : 'margin', 21 paddingLeft : 'padding', 22 paddingRight : 'padding', 23 paddingBottom : 'padding', 24 paddingTop : 'padding' 25 }, 26 27 // Will simply transition "instantly" if false 28 enabled: true, 29 30 // Set this to false if you don't want to use the transition end property. 31 useTransitionEnd: false 32 }; 33 34 var div = document.createElement('div'); 35 var support = {}; 36 37 // Helper function to get the proper vendor property name. 38 // (`transition` => `WebkitTransition`) 39 function getVendorPropertyName(prop) { 40 // Handle unprefixed versions (FF16+, for example) 41 if (prop in div.style) return prop; 42 43 var prefixes = ['Moz', 'Webkit', 'O', 'ms']; 44 var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1); 45 46 if (prop in div.style) { return prop; } 47 48 for (var i=0; i<prefixes.length; ++i) { 49 var vendorProp = prefixes[i] + prop_; 50 if (vendorProp in div.style) { return vendorProp; } 51 } 52 } 53 54 // Helper function to check if transform3D is supported. 55 // Should return true for Webkits and Firefox 10+. 56 function checkTransform3dSupport() { 57 div.style[support.transform] = ''; 58 div.style[support.transform] = 'rotateY(90deg)'; 59 return div.style[support.transform] !== ''; 60 } 61 62 var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; 63 64 // Check for the browser's transitions support. 65 support.transition = getVendorPropertyName('transition'); 66 support.transitionDelay = getVendorPropertyName('transitionDelay'); 67 support.transform = getVendorPropertyName('transform'); 68 support.transformOrigin = getVendorPropertyName('transformOrigin'); 69 support.filter = getVendorPropertyName('Filter'); 70 support.transform3d = checkTransform3dSupport(); 71 72 var eventNames = { 73 'transition': 'transitionEnd', 74 'MozTransition': 'transitionend', 75 'OTransition': 'oTransitionEnd', 76 'WebkitTransition': 'webkitTransitionEnd', 77 'msTransition': 'MSTransitionEnd' 78 }; 79 80 // Detect the 'transitionend' event needed. 81 var transitionEnd = support.transitionEnd = eventNames[support.transition] || null; 82 83 // Populate jQuery's `$.support` with the vendor prefixes we know. 84 // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/), 85 // we set $.support.transition to a string of the actual property name used. 86 for (var key in support) { 87 if (support.hasOwnProperty(key) && typeof $.support[key] === 'undefined') { 88 $.support[key] = support[key]; 89 } 90 } 91 92 // Avoid memory leak in IE. 93 div = null; 94 95 // ## $.cssEase 96 // List of easing aliases that you can use with `$.fn.transition`. 97 $.cssEase = { 98 '_default': 'ease', 99 'in': 'ease-in', 100 'out': 'ease-out', 101 'in-out': 'ease-in-out', 102 'snap': 'cubic-bezier(0,1,.5,1)', 103 // Penner equations 104 'easeOutCubic': 'cubic-bezier(.215,.61,.355,1)', 105 'easeInOutCubic': 'cubic-bezier(.645,.045,.355,1)', 106 'easeInCirc': 'cubic-bezier(.6,.04,.98,.335)', 107 'easeOutCirc': 'cubic-bezier(.075,.82,.165,1)', 108 'easeInOutCirc': 'cubic-bezier(.785,.135,.15,.86)', 109 'easeInExpo': 'cubic-bezier(.95,.05,.795,.035)', 110 'easeOutExpo': 'cubic-bezier(.19,1,.22,1)', 111 'easeInOutExpo': 'cubic-bezier(1,0,0,1)', 112 'easeInQuad': 'cubic-bezier(.55,.085,.68,.53)', 113 'easeOutQuad': 'cubic-bezier(.25,.46,.45,.94)', 114 'easeInOutQuad': 'cubic-bezier(.455,.03,.515,.955)', 115 'easeInQuart': 'cubic-bezier(.895,.03,.685,.22)', 116 'easeOutQuart': 'cubic-bezier(.165,.84,.44,1)',
117 'easeInOutQuart': 'cubic-bezier(.77,0,.175,1)', 118 'easeInQuint': 'cubic-bezier(.755,.05,.855,.06)', 119 'easeOutQuint': 'cubic-bezier(.23,1,.32,1)', 120 'easeInOutQuint': 'cubic-bezier(.86,0,.07,1)', 121 'easeInSine': 'cubic-bezier(.47,0,.745,.715)', 122 'easeOutSine': 'cubic-bezier(.39,.575,.565,1)', 123 'easeInOutSine': 'cubic-bezier(.445,.05,.55,.95)', 124 'easeInBack': 'cubic-bezier(.6,-.28,.735,.045)', 125 'easeOutBack': 'cubic-bezier(.175, .885,.32,1.275)', 126 'easeInOutBack': 'cubic-bezier(.68,-.55,.265,1.55)' 127 }; 128 129 // ## 'transform' CSS hook 130 // Allows you to use the `transform` property in CSS. 131 // 132 // $("#hello").css({ transform: "rotate(90deg)" }); 133 // 134 // $("#hello").css('transform'); 135 // //=> { rotate: '90deg' } 136 // 137 $.cssHooks['transit:transform'] = { 138 // The getter returns a `Transform` object. 139 get: function(elem) { 140 return $(elem).data('transform') || new Transform(); 141 }, 142 143 // The setter accepts a `Transform` object or a string. 144 set: function(elem, v) { 145 var value = v; 146 147 if (!(value instanceof Transform)) { 148 value = new Transform(value); 149 } 150 151 // We've seen the 3D version of Scale() not work in Chrome when the 152 // element being scaled extends outside of the viewport. Thus, we're 153 // forcing Chrome to not use the 3d transforms as well. Not sure if 154 // translate is affectede, but not risking it. Detection code from 155 // http://davidwalsh.name/detecting-google-chrome-javascript 156 if (support.transform === 'WebkitTransform' && !isChrome) { 157 elem.style[support.transform] = value.toString(true); 158 } else { 159 elem.style[support.transform] = value.toString(); 160 } 161 162 $(elem).data('transform', value); 163 } 164 }; 165 166 // Add a CSS hook for `.css({ transform: '...' })`. 167 // In jQuery 1.8+, this will intentionally override the default `transform` 168 // CSS hook so it'll play well with Transit. (see issue #62) 169 $.cssHooks.transform = { 170 set: $.cssHooks['transit:transform'].set 171 }; 172 173 // ## 'filter' CSS hook 174 // Allows you to use the `filter` property in CSS. 175 // 176 // $("#hello").css({ filter: 'blur(10px)' }); 177 // 178 $.cssHooks.filter = { 179 get: function(elem) { 180 return elem.style[support.filter]; 181 }, 182 set: function(elem, value) { 183 elem.style[support.filter] = value; 184 } 185 }; 186 187 // jQuery 1.8+ supports prefix-free transitions, so these polyfills will not 188 // be necessary. 189 if ($.fn.jquery < "1.8") { 190 // ## 'transformOrigin' CSS hook 191 // Allows the use for `transformOrigin` to define where scaling and rotation 192 // is pivoted. 193 // 194 // $("#hello").css({ transformOrigin: '0 0' }); 195 // 196 $.cssHooks.transformOrigin = { 197 get: function(elem) { 198 return elem.style[support.transformOrigin]; 199 }, 200 set: function(elem, value) { 201 elem.style[support.transformOrigin] = value; 202 } 203 }; 204 205 // ## 'transition' CSS hook 206 // Allows you to use the `transition` property in CSS. 207 // 208 // $("#hello").css({ transition: 'all 0 ease 0' }); 209 // 210 $.cssHooks.transition = { 211 get: function(elem) { 212 return elem.style[support.transition]; 213 }, 214 set: function(elem, value) { 215 elem.style[support.transition] = value; 216 } 217 }; 218 } 219 220 // ## Other CSS hooks 221 // Allows you to rotate, scale and translate. 222 registerCssHook('scale'); 223 registerCssHook('translate'); 224 registerCssHook('rotate'); 225 registerCssHook('rotateX'); 226 registerCssHook('rotateY'); 227 registerCssHook('rotate3d'); 228 registerCssHook('perspective'); 229 registerCssHook('skewX'); 230 registerCssHook('skewY'); 231 registerCssHook('x', true); 232 registerCssHook('y', true); 233 234 // ## Transform class 235 // This is the main class of a transformation property that powers 236 // `$.fn.css({ transform: '...' })`. 237 // 238 // This is, in essence, a dictionary object with key/values as `-transform` 239 // properties. 240 // 241 // var t = new Transform("rotate(90) scale(4)"); 242 // 243 // t.rotate //=> "90deg" 244 // t.scale //=> "4,4" 245 // 246 // Setters are accounted for. 247 // 248 // t.set('rotate', 4) 249 // t.rotate //=> "4deg" 250 // 251 // Convert it to a CSS string using the `toString()` and `toString(true)` (for WebKit) 252 // functions. 253 // 254 // t.toString() //=> "rotate(90deg) scale(4,4)" 255 // t.toString(true) //=> "rotate(90deg) scale3d(4,4,0)" (WebKit version) 256 // 257 function Transform(str) { 258 if (typeof str === 'string') { this.parse(str); } 259 return this; 260 } 261 262 Transform.prototype = { 263 // ### setFromString() 264 // Sets a property from a string. 265 // 266 // t.setFromString('scale', '2,4'); 267 // // Same as set('scale', '2', '4'); 268 // 269 setFromString: function(prop, val) { 270 var args = 271 (typeof val === 'string') ? val.split(',') : 272 (val.constructor === Array) ? val : 273 [ val ]; 274 275 args.unshift(prop); 276 277 Transform.prototype.set.apply(this, args); 278 }, 279 280 // ### set() 281 // Sets a property. 282 // 283 // t.set('scale', 2, 4); 284 // 285 set: function(prop) { 286 var args = Array.prototype.slice.apply(arguments, [1]); 287 if (this.setter[prop]) { 288 this.setter[prop].apply(this, args); 289 } else { 290 this[prop] = args.join(','); 291 } 292 }, 293 294 get: function(prop) { 295 if (this.getter[prop]) { 296 return this.getter[prop].apply(this); 297 } else { 298 return this[prop] || 0; 299 } 300 }, 301 302 setter: { 303 // ### rotate 304 // 305 // .css({ rotate: 30 }) 306 // .css({ rotate: "30" }) 307 // .css({ rotate: "30deg" }) 308 // .css({ rotate: "30deg" }) 309 // 310 rotate: function(theta) { 311 this.rotate = unit(theta, 'deg'); 312 }, 313 314 rotateX: function(theta) { 315 this.rotateX = unit(theta, 'deg'); 316 }, 317 318 rotateY: function(theta) { 319 this.rotateY = unit(theta, 'deg'); 320 }, 321 322 // ### scale 323 // 324 // .css({ scale: 9 }) //=> "scale(9,9)" 325 // .css({ scale: '3,2' }) //=> "scale(3,2)" 326 // 327 scale: function(x, y) { 328 if (y === undefined) { y = x; } 329 this.scale = x + "," + y; 330 }, 331 332 // ### skewX + skewY 333 skewX: function(x) { 334 this.skewX = unit(x, 'deg'); 335 }, 336 337 skewY: function(y) { 338 this.skewY = unit(y, 'deg'); 339 }, 340 341 // ### perspectvie 342 perspective: function(dist) { 343 this.perspective = unit(dist, 'px'); 344 }, 345 346 // ### x / y 347 // Translations. Notice how this keeps the other value. 348 // 349 // .css({ x: 4 }) //=> "translate(4px, 0)" 350 // .css({ y: 10 }) //=> "translate(4px, 10px)" 351 // 352 x: function(x) { 353 this.set('translate', x, null); 354 }, 355 356 y: function(y) { 357 this.set('translate', null, y); 358 }, 359 360 // ### translate 361 // Notice how this keeps the other value. 362 // 363 // .css({ translate: '2, 5' }) //=> "translate(2px, 5px)" 364 // 365 translate: function(x, y) { 366 if (this._translateX === undefined) { this._translateX = 0; } 367 if (this._translateY === undefined) { this._translateY = 0; } 368 369 if (x !== null && x !== undefined) { this._translateX = unit(x, 'px'); } 370 if (y !== null && y !== undefined) { this._translateY = unit(y, 'px'); } 371 372 this.translate = this._translateX + "," + this._translateY; 373 } 374 }, 375 376 getter: { 377 x: function() { 378 return this._translateX || 0; 379 }, 380 381 y: function() { 382 return this._translateY || 0; 383 }, 384 385 scale: function() { 386 var s = (this.scale || "1,1").split(','); 387 if (s[0]) { s[0] = parseFloat(s[0]); } 388 if (s[1]) { s[1] = parseFloat(s[1]); } 389 390 // "2.5,2.5" => 2.5 391 // "2.5,1" => [2.5,1] 392 return (s[0] === s[1]) ? s[0] : s; 393 }, 394 395 rotate3d: function() { 396 var s = (this.rotate3d || "0,0,0,0deg").split(','); 397 for (var i=0; i<=3; ++i) { 398 if (s[i]) { s[i] = parseFloat(s[i]); } 399 } 400 if (s[3]) { s[3] = unit(s[3], 'deg'); } 401 402 return s; 403 } 404 }, 405 406 // ### parse() 407 // Parses from a string. Called on constructor. 408 parse: function(str) { 409 var self = this; 410 str.replace(/([a-zA-Z0-9]+)\((.*?)\)/g, function(x, prop, val) { 411 self.setFromString(prop, val); 412 }); 413 }, 414 415 // ### toString() 416 // Converts to a `transition` CSS property string. If `use3d` is given, 417 // it converts to a `-webkit-transition` CSS property string instead. 418 toString: function(use3d) { 419 var re = []; 420 421 for (var i in this) { 422 if (this.hasOwnProperty(i)) { 423 // Don't use 3D transformations if the browser can't support it. 424 if ((!support.transform3d) && ( 425 (i === 'rotateX') || 426 (i === 'rotateY') || 427 (i === 'perspective') || 428 (i === 'transformOrigin'))) { continue; } 429 430 if (i[0] !== '_') { 431 if (use3d && (i === 'scale')) { 432 re.push(i + "3d(" + this[i] + ",1)"); 433 } else if (use3d && (i === 'translate')) { 434 re.push(i + "3d(" + this[i] + ",0)"); 435 } else { 436 re.push(i + "(" + this[i] + ")"); 437 } 438 } 439 } 440 } 441 442 return re.join(" "); 443 } 444 }; 445 446 function callOrQueue(self, queue, fn) { 447 if (queue === true) { 448 self.queue(fn); 449 } else if (queue) { 450 self.queue(queue, fn); 451 } else { 452 fn(); 453 } 454 } 455 456 // ### getProperties(dict) 457 // Returns properties (for `transition-property`) for dictionary `props`. The 458 // value of `props` is what you would expect in `$.css(...)`. 459 function getProperties(props) { 460 var re = []; 461 462 $.each(props, function(key) { 463 key = $.camelCase(key); // Convert "text-align" => "textAlign" 464 key = $.transit.propertyMap[key] || $.cssProps[key] || key; 465 key = uncamel(key); // Convert back to dasherized 466 467 // Get vendor specify propertie 468 if (support[key]) 469 key = uncamel(support[key]); 470 471 if ($.inArray(key, re) === -1) { re.push(key); } 472 }); 473 474 return re; 475 } 476 477 // ### getTransition() 478 // Returns the transition string to be used for the `transition` CSS property. 479 // 480 // Example: 481 // 482 // getTransition({ opacity: 1, rotate: 30 }, 500, 'ease'); 483 // //=> 'opacity 500ms ease, -webkit-transform 500ms ease' 484 // 485 function getTransition(properties, duration, easing, delay) { 486 // Get the CSS properties needed. 487 var props = getProperties(properties); 488 489 // Account for aliases (`in` => `ease-in`). 490 if ($.cssEase[easing]) { easing = $.cssEase[easing]; } 491 492 // Build the duration/easing/delay attributes for it. 493 var attribs = '' + toMS(duration) + ' ' + easing; 494 if (parseInt(delay, 10) > 0) { attribs += ' ' + toMS(delay); } 495 496 // For more properties, add them this way: 497 // "margin 200ms ease, padding 200ms ease, ..." 498 var transitions = []; 499 $.each(props, function(i, name) { 500 transitions.push(name + ' ' + attribs); 501 }); 502 503 return transitions.join(', '); 504 } 505 506 // ## $.fn.transition 507 // Works like $.fn.animate(), but uses CSS transitions. 508 // 509 // $("...").transition({ opacity: 0.1, scale: 0.3 }); 510 // 511 // // Specific duration 512 // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500); 513 //
514 // // With duration and easing 515 // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in'); 516 // 517 // // With callback 518 // $("...").transition({ opacity: 0.1, scale: 0.3 }, function() { ... }); 519 // 520 // // With everything 521 // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in', function() { ... }); 522 // 523 // // Alternate syntax 524 // $("...").transition({ 525 // opacity: 0.1, 526 // duration: 200, 527 // delay: 40, 528 // easing: 'in', 529 // complete: function() { /* ... */ } 530 // }); 531 // 532 $.fn.transition = $.fn.transit = function(properties, duration, easing, callback) { 533 var self = this; 534 var delay = 0; 535 var queue = true; 536 537 var theseProperties = jQuery.extend(true, {}, properties); 538 539 // Account for `.transition(properties, callback)`. 540 if (typeof duration === 'function') { 541 callback = duration; 542 duration = undefined; 543 } 544 545 // Account for `.transition(properties, options)`. 546 if (typeof duration === 'object') { 547 easing = duration.easing; 548 delay = duration.delay || 0; 549 queue = duration.queue || true; 550 callback = duration.complete; 551 duration = duration.duration; 552 } 553 554 // Account for `.transition(properties, duration, callback)`. 555 if (typeof easing === 'function') { 556 callback = easing; 557 easing = undefined; 558 } 559 560 // Alternate syntax. 561 if (typeof theseProperties.easing !== 'undefined') { 562 easing = theseProperties.easing; 563 delete theseProperties.easing; 564 } 565 566 if (typeof theseProperties.duration !== 'undefined') { 567 duration = theseProperties.duration; 568 delete theseProperties.duration; 569 } 570 571 if (typeof theseProperties.complete !== 'undefined') { 572 callback = theseProperties.complete; 573 delete theseProperties.complete; 574 } 575 576 if (typeof theseProperties.queue !== 'undefined') { 577 queue = theseProperties.queue; 578 delete theseProperties.queue; 579 } 580 581 if (typeof theseProperties.delay !== 'undefined') { 582 delay = theseProperties.delay; 583 delete theseProperties.delay; 584 } 585 586 // Set defaults. (`400` duration, `ease` easing) 587 if (typeof duration === 'undefined') { duration = $.fx.speeds._default; } 588 if (typeof easing === 'undefined') { easing = $.cssEase._default; } 589 590 duration = toMS(duration); 591 592 // Build the `transition` property. 593 var transitionValue = getTransition(theseProperties, duration, easing, delay); 594 595 // Compute delay until callback. 596 // If this becomes 0, don't bother setting the transition property. 597 var work = $.transit.enabled && support.transition; 598 var i = work ? (parseInt(duration, 10) + parseInt(delay, 10)) : 0; 599 600 // If there's nothing to do... 601 if (i === 0) { 602 var fn = function(next) { 603 self.css(theseProperties); 604 if (callback) { callback.apply(self); } 605 if (next) { next(); } 606 }; 607 608 callOrQueue(self, queue, fn); 609 return self; 610 } 611 612 // Save the old transitions of each element so we can restore it later. 613 var oldTransitions = {}; 614 615 var run = function(nextCall) { 616 var bound = false; 617 618 // Prepare the callback. 619 var cb = function() { 620 if (bound) { self.unbind(transitionEnd, cb); } 621 622 if (i > 0) { 623 self.each(function() { 624 this.style[support.transition] = (oldTransitions[this] || null); 625 }); 626 } 627 628 if (typeof callback === 'function') { callback.apply(self); } 629 if (typeof nextCall === 'function') { nextCall(); } 630 }; 631 632 if ((i > 0) && (transitionEnd) && ($.transit.useTransitionEnd)) { 633 // Use the 'transitionend' event if it's available. 634 bound = true; 635 self.bind(transitionEnd, cb); 636 } else { 637 // Fallback to timers if the 'transitionend' event isn't supported. 638 window.setTimeout(cb, i); 639 } 640 641 // Apply transitions. 642 self.each(function() { 643 if (i > 0) { 644 this.style[support.transition] = transitionValue; 645 } 646 $(this).css(properties); 647 }); 648 }; 649
650 // Defer running. This allows the browser to paint any pending CSS it hasn't 651 // painted yet before doing the transitions. 652 var deferredRun = function(next) { 653 this.offsetWidth; // force a repaint 654 run(next); 655 }; 656 657 // Use jQuery's fx queue. 658 callOrQueue(self, queue, deferredRun); 659 660 // Chainability. 661 return this; 662 }; 663 664 function registerCssHook(prop, isPixels) { 665 // For certain properties, the 'px' should not be implied. 666 if (!isPixels) { $.cssNumber[prop] = true; } 667 668 $.transit.propertyMap[prop] = support.transform; 669 670 $.cssHooks[prop] = { 671 get: function(elem) { 672 var t = $(elem).css('transit:transform'); 673 return t.get(prop); 674 }, 675 676 set: function(elem, value) { 677 var t = $(elem).css('transit:transform'); 678 t.setFromString(prop, value); 679 680 $(elem).css({ 'transit:transform': t }); 681 } 682 }; 683 684 } 685 686 // ### uncamel(str) 687 // Converts a camelcase string to a dasherized string. 688 // (`marginLeft` => `margin-left`) 689 function uncamel(str) { 690 return str.replace(/([A-Z])/g, function(letter) { return '-' + letter.toLowerCase(); }); 691 } 692 693 // ### unit(number, unit) 694 // Ensures that number `number` has a unit. If no unit is found, assume the 695 // default is `unit`. 696 // 697 // unit(2, 'px') //=> "2px" 698 // unit("30deg", 'rad') //=> "30deg" 699 // 700 function unit(i, units) { 701 if ((typeof i === "string") && (!i.match(/^[\-0-9\.]+$/))) { 702 return i; 703 } else { 704 return "" + i + units; 705 } 706 } 707 708 // ### toMS(duration) 709 // Converts given `duration` to a millisecond string. 710 // 711 // toMS('fast') => $.fx.speeds[i] => "200ms" 712 // toMS('normal') //=> $.fx.speeds._default => "400ms" 713 // toMS(10) //=> '10ms' 714 // toMS('100ms') //=> '100ms' 715 // 716 function toMS(duration) { 717 var i = duration; 718 719 // Allow string durations like 'fast' and 'slow', without overriding numeric values. 720 if (typeof i === 'string' && (!i.match(/^[\-0-9\.]+/))) { i = $.fx.speeds[i] || $.fx.speeds._default; } 721 722 return unit(i, 'ms'); 723 } 724 725 // Export some functions for testable-ness. 726 $.transit.getTransitionValue = getTransition; 727})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.