(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);