(function () { // http://stackoverflow.com/questions/1004475/jquery-css-plugin-that-returns-computed-style-of-element-to-pseudo-clone-that-el#answer-17218752 (function($) { var nativeCss = $.fn.css; var camelCase = $.camelCase || function(str) { return str.replace(/\-([a-z])/g, function($0, $1) { return $1.toUpperCase(); }); }; $.fn.css = function(name, value) { if (name == null || name === '*') { var elem = this[0]; var css; var returns = {}; if (window.getComputedStyle) { css = window.getComputedStyle(elem, null); for (var i = 0, l = css.length; i < l; i++) { returns[camelCase(css[i])] = css.getPropertyValue(css[i]); } return returns; } else if (elem.currentStyle) { css = elem.currentStyle; for (var prop in css) { returns[prop] = css[prop]; } } return returns; } else { return nativeCss.apply(this, arguments); } } })(jQuery); var ZERO_OPACITY = 0.5; var DATA_KEY = 'transitionab_origcssText'; function hide (el) { var $el = $(el); el = $el[0]; // backup original styles="" var origcssText = $el.data(DATA_KEY); if (typeof origcssText === 'undefined') { $el.data(DATA_KEY, el.style.cssText); // only once } $el.css('opacity', ZERO_OPACITY); } function unhide(el) { var $el = $(el); el = $el[0]; var origcssText = $el.data(DATA_KEY); if (typeof origcssText !== 'undefined') { el.style.cssText = origcssText; } } // http://stackoverflow.com/questions/5661671/detecting-transform-translate3d-support#answer-12621264 function has3d() { var el = document.createElement('p'), has3d, transforms = { 'webkitTransform':'-webkit-transform', 'OTransform':'-o-transform', 'msTransform':'-ms-transform', 'MozTransform':'-moz-transform', 'transform':'transform' }; // Add it to the body to get the computed style. document.body.insertBefore(el, null); for (var t in transforms) { if (el.style[t] !== undefined) { el.style[t] = "translate3d(1px,1px,1px)"; has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]); } } document.body.removeChild(el); return (has3d !== undefined && has3d.length > 0 && has3d !== "none"); } function TransitionAB(elA, elB, options) { options = $.extend({}, { duration: 4000 }, options); this.options = options; this.$elA = $(elA); this.elA = elA; this.$elB = $(elB); this.elB = elB; // appendTo element: where the clones will be inserted into this.$appendTo = $('
').appendTo(document.body); hide(this.$appendTo); // bind this this.A2B = this.A2B.bind(this); this.B2A = this.B2A.bind(this); } TransitionAB.has3d = has3d(); TransitionAB.prototype.clone = function (el, cb) { if (!el) return; var $el = $(el); var that = this; var $clone = $el.clone(); // get position (without transform/margin-left/margin-right) var cssTextBackup = el.style.cssText; // backup $el.css({transform:'none',marginLeft:0,marginTop:0}); var offset = $el.offset(); el.style.cssText = cssTextBackup; // restore // position with translate3d if available var position; if (this.constructor.has3d) { // computed transform (return 'none' or 'matrix(,,,,,) or matrix3d(,,,,,,,,,,,,,,,,)') var transform = $el.css('transform'); if (transform === 'none') { transform = ''; // reject 'none' } position = { position:'absolute',left:0,top:0, transform:('translate3d(' + offset.left + 'px,' + offset.top + 'px,0) ' + transform) // prepend css transform (http://stackoverflow.com/questions/21366252/prepend-a-css-transform/21366398) }; } else { position = {position:'absolute',left:offset.left+'px',top:offset.top+'px'}; } // copy computed styles $clone.css($el.css()) // position it within $appendTo .appendTo(this.$appendTo).css(position) // transition:all .each(function (i, el) {setTimeout(function () { // each is just used to chain command // It's important to set 'transition:all' AFTER (setTimeout) being appended to the DOM, so the transition does not apply immediately $clone.css({transition: 'all ' + that.options.duration + 'ms'}); cb && cb(el); }, 0);}) ; return $clone; }; TransitionAB.prototype.A2B = function () { this.src2dst(this.$elA, this.$elB); }; TransitionAB.prototype.B2A = function () { this.src2dst(this.$elB, this.$elA); }; TransitionAB.prototype.src2dst = function ($src, $dst) { /* 1. clone src 2. clone dst 3. copy cloneDst styles 4. remove cloneDst 5. hide originals 6. unhide wrapper 7. apply copied styles 8. wait for transition ends 9. hide wrapper 10. reveal back original elements 11. remove cloneDst */ //if (this.currentState === $dst[0]) return; if (this.currentState) return; this.currentState = $dst[0]; // make sure $src and $dst are not hidden (may be hidden if a transition is currently being played) //unhide($src); //unhide($dst); // Once cloneSrc is created function then() { // 2. clone dst this.clone($dst[0], function (cloneDst) { // 3. copy cloneDst styles var cloneDstStyles = cloneDst.style.cssText; // 4. remove cloneDst $(cloneDst).remove(); // 5. hide originals hide($src); hide($dst); // 6. unhide wrapper unhide(this.$appendTo); // 7. apply copied styles this.cloneSrc.style.cssText = cloneDstStyles; // 8. [...] clearTimeout(this.hideInt); this.hideInt = setTimeout(function () { // 9. hide wrapper hide(this.$appendTo); // 10. reveal back original elements unhide($src); unhide($dst); // 11. remove cloneSrc this.$cloneSrc.remove(); this.$cloneSrc = this.cloneSrc = undefined; this.currentState = undefined; }.bind(this), this.options.duration); }.bind(this)); } then = then.bind(this); // 1. lazy clone src if (!this.$cloneSrc) { this.clone($src[0], function (cloneSrc) { this.cloneSrc = cloneSrc; this.$cloneSrc = $(cloneSrc); then(); }.bind(this)); } else { then(); } }; // exports this.TransitionAB = TransitionAB; if (typeof module !== "undefined" && module !== null) { module.exports = this.TransitionAB; } }).call(this);