Skip to content

Instantly share code, notes, and snippets.

@joelambert
Created August 1, 2011 08:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save joelambert/1117818 to your computer and use it in GitHub Desktop.
Save joelambert/1117818 to your computer and use it in GitHub Desktop.
Morf v0.1.5 Pre Release
/**
* @preserve Morf v0.1.5 (pre-release)
* http://www.joelambert.co.uk/morf
*
* Copyright 2011, Joe Lambert.
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
var Morf = function(elem, css, opts) {
var from = {}, to = {},
fromElem = document.createElement('div'),
toElem = document.createElement('div'),
options = {
timingFunction: 'ease',
duration: null,
increment: 0.01,
debug: false
},
// Define all other var's used in the function
i = rule = ruleName = camel = m1 = m2 = progress = frame = rule = transEvent = val = null, cacheKey = '',
// Setup a scoped reference to ourselves
_this = this,
keyframes = {},
// Create a unique name for this animation
animName = 'anim'+(new Date().getTime()),
/* --- Helper Functions ------------------------------------------------------------------- */
// Event listener for the webkitAnimationEnd Event
animationEndListener = function(event){
elem.removeEventListener('webkitAnimationEnd', animationEndListener, true);
// Dispatch a faux webkitTransitionEnd event to complete the appearance of this being a transition rather than an animation
// TODO: Should we throw an event for each property changed? (event.propertyName = 'opacity' etc)
transEvent = document.createEvent("Event");
transEvent.initEvent("webkitTransitionEnd", true, true);
elem.dispatchEvent(transEvent);
// Reset transition effects after use
elem.style.webkitTransitionTimingFunction = null;
elem.style.webkitTransitionDuration = 0;
if (options.callback) {
options.callback(elem);
}
},
// Adds the CSS to the current page
addKeyframeRule = function(rule) {
if (document.styleSheets && document.styleSheets.length)
document.styleSheets[0].insertRule(rule, 0);
else
{
var style = document.createElement('style');
style.innerHTML = rule;
document.head.appendChild(style);
}
},
// Produces a CSS string representation of the Keyframes
createAnimationCSS = function(kf, name) {
var str = '@-webkit-keyframes '+name+' {\n', f = pos = rule = null, fStr = '';
for(pos in kf)
{
f = kf[pos];
fStr = '\t'+pos+' {\n';
for(rule in f)
fStr += '\t\t'+_this.util.toDash(rule)+': '+f[rule]+';\n';
fStr += "\t}\n\n";
str += fStr;
}
return str + " }";
},
// Replaces scale(0) with 0.0001 to get around the inability to these decompose matrix
sanityCheckTransformString = function(str) {
var scale = /scale[Y|X|Z]*\([0-9, ]*0[,0-9 ]*\)/.exec(str);
if(scale)
{
// There might be multiple scale() properties in the string
for(var i=0; i<scale.length; i++)
str = str.replace(scale[i], scale[i].replace(/([^0-9])0([^0.9])/g, "$10.0001$2"));
}
return str;
};
/* --- Helper Functions End --------------------------------------------------------------- */
// Import the options
for(i in opts)
options[i] = opts[i];
// If timingFunction is a natively supported function then just trigger normal transition
if( options.timingFunction === 'ease' ||
options.timingFunction === 'linear' ||
options.timingFunction === 'ease-in' ||
options.timingFunction === 'ease-out' ||
options.timingFunction === 'ease-in-out' ||
/^cubic-bezier/.test(options.timingFunction)) {
elem.style.webkitTransitionDuration = options.duration;
elem.style.webkitTransitionTimingFunction = options.timingFunction;
// Listen for the transitionEnd event to fire the callback if needed
var transitionEndListener = function(event) {
elem.removeEventListener('webkitTransitionEnd', transitionEndListener, true);
// Clean up after ourself
elem.style.webkitTransitionDuration = 0;
elem.style.webkitTransitionTimingFunction = null;
if (options.callback) {
// Delay execution to ensure the clean up CSS has taken effect
setTimeout(function() {
options.callback(elem);
}, 10);
}
};
elem.addEventListener('webkitTransitionEnd', transitionEndListener, true);
setTimeout(function() {
for(rule in css) {
camel = _this.util.toCamel(rule);
elem.style[camel] = css[rule];
}
}, 10);
this.css = '';
return;
}
else
{
// Reset transition properties for this element
elem.style.webkitTransitionTimingFunction = null;
elem.style.webkitTransitionDuration = 0;
}
// Create the key used to cache this animation
cacheKey += options.timingFunction;
// Setup the start and end CSS state
for(rule in css)
{
camel = this.util.toCamel(rule);
toElem.style[camel] = css[rule];
// Set the from/start state
from[rule] = (camel == 'WebkitTransform') ? new WebKitCSSMatrix( sanityCheckTransformString( window.getComputedStyle(elem)['-webkit-transform'] ) ) : window.getComputedStyle(elem)[rule];
// Set the to/end state
to[rule] = (camel == 'WebkitTransform') ? new WebKitCSSMatrix( sanityCheckTransformString( toElem.style.WebkitTransform ) ) : toElem.style[camel];
// Shifty requires numeric values to be a number rather than a string (e.g. for opacity)
from[rule] = from[rule] == (val = parseInt(from[rule], 10)) ? val : from[rule];
to[rule] = to[rule] == (val = parseInt(from[rule], 10)) ? val : to[rule];
// Update the cacheKey
cacheKey += ';' + rule + ':' + from[rule] + '->' + to[rule];
}
// Check the cache to save expensive calculations
if(Morf.cache[cacheKey])
{
this.css = Morf.cache[cacheKey].css;
animName = Morf.cache[cacheKey].name;
}
else
{
// Produce decompositions of matrices here so we don't have to redo it on each iteration
// Decomposing the matrix is expensive so we need to minimise these requests
if(from['-webkit-transform'])
{
m1 = from['-webkit-transform'].decompose();
m2 = to['-webkit-transform'].decompose();
}
// Produce style keyframes
for(progress = 0; progress <= 1; progress += options.increment) {
// Use Shifty.js to work out the interpolated CSS state
frame = Tweenable.util.interpolate(from, to, progress, options.timingFunction);
// Work out the interpolated matrix transformation
if(m1 !== null && m2 !== null)
frame['-webkit-transform'] = m1.tween(m2, progress, Tweenable.prototype.formula[options.timingFunction]);
keyframes[parseInt(progress*100, 10)+'%'] = frame;
}
// Ensure the last frame has been added
keyframes['100%'] = to;
// Add the new animation to the document
this.css = createAnimationCSS(keyframes, animName);
addKeyframeRule(this.css);
Morf.cache[cacheKey] = {css: this.css, name: animName};
}
// Set the final position state as this should be a transition not an animation & the element should end in the 'to' state
for(rule in to)
elem.style[this.util.toCamel(rule)] = to[rule];
// Trigger the animation
elem.addEventListener('webkitAnimationEnd', animationEndListener, true);
elem.style.webkitAnimationDuration = options.duration;
elem.style.webkitAnimationTimingFunction = 'linear';
elem.style.webkitAnimationName = animName;
// Print the animation to the console if the debug switch is given
if(options.debug && window.console && window.console.log)
console.log(createAnimationCSS(keyframes, animName));
};
/**
* Convenience function for triggering a transition
* @param {HTMLDom} elem The element to apply the transition to
* @param {Object} css Key value pair of CSS properties
* @param {Object} opts Additional configuration options
*
* Configuration options
* - timingFunction: {String} Name of the easing function to perform
* - duration: {integer} Duration in ms
* - increment: {float} How frequently to generate keyframes (Defaults to 0.01, which is every 1%)
* - debug: {Boolean} Should the generated CSS Animation be printed to the console
*
* @returns {Morf} An instance of the Morf object
*/
Morf.transition = function(elem, css, opts){
return new Morf(elem, css, opts);
};
/**
* Object to cache generated animations
*/
Morf.cache = {};
/**
* Current version
*/
Morf.version = '0.1.5';
// Utilities Placeholder
Morf.prototype.util = {};
/**
* Converts a DOM style string to CSS property name
* @param {String} str A DOM style string
* @returns {String} CSS property name
*/
Morf.prototype.util.toDash = function(str){
str = str.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
return /^webkit/.test(str) ? '-'+str : str;
};
/**
* Converts a CSS property name to DOM style string
* @param {String} str A CSS property name
* @returns {String} DOM style string
*/
Morf.prototype.util.toCamel = function(str){
return str.replace(/(\-[a-z])/g, function($1){return $1.toUpperCase().replace('-','');});
};
// Wrap this functionality up to prevent poluting the global namespace
(function(){
/**
* A 4 dimensional vector
* @author Joe Lambert
* @constructor
*/
var Vector4 = function(x, y, z, w)
{
this.x = x ? x : 0;
this.y = y ? y : 0;
this.z = z ? z : 0;
this.w = w ? w : 0;
/**
* Ensure that values are not undefined
* @author Joe Lambert
* @returns null
*/
this.checkValues = function() {
this.x = this.x ? this.x : 0;
this.y = this.y ? this.y : 0;
this.z = this.z ? this.z : 0;
this.w = this.w ? this.w : 0;
};
/**
* Get the length of the vector
* @author Joe Lambert
* @returns {float}
*/
this.length = function() {
this.checkValues();
return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
};
/**
* Get a normalised representation of the vector
* @author Joe Lambert
* @returns {Vector4}
*/
this.normalise = function() {
var len = this.length(),
v = new Vector4(this.x / len, this.y / len, this.z / len);
return v;
};
/**
* Vector Dot-Product
* @param {Vector4} v The second vector to apply the product to
* @author Joe Lambert
* @returns {float} The Dot-Product of this and v.
*/
this.dot = function(v) {
return this.x*v.x + this.y*v.y + this.z*v.z + this.w*v.w;
};
/**
* Vector Cross-Product
* @param {Vector4} v The second vector to apply the product to
* @author Joe Lambert
* @returns {Vector4} The Cross-Product of this and v.
*/
this.cross = function(v) {
return new Vector4(this.y*v.z - this.z*v.y, this.z*v.x - this.x*v.z, this.x*v.y - this.y*v.x);
};
/**
* Helper function required for matrix decomposition
* A Javascript implementation of pseudo code available from http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition
* @param {Vector4} aPoint A 3D point
* @param {float} ascl
* @param {float} bscl
* @author Joe Lambert
* @returns {Vector4}
*/
this.combine = function(aPoint, ascl, bscl) {
return new Vector4( (ascl * this.x) + (bscl * aPoint.x),
(ascl * this.y) + (bscl * aPoint.y),
(ascl * this.z) + (bscl * aPoint.z) );
}
};
/**
* Object containing the decomposed components of a matrix
* @author Joe Lambert
* @constructor
*/
var CSSMatrixDecomposed = function(obj) {
obj === undefined ? obj = {} : null;
var components = {perspective: null, translate: null, skew: null, scale: null, rotate: null};
for(var i in components)
this[i] = obj[i] ? obj[i] : new Vector4();
/**
* Tween between two decomposed matrices
* @param {CSSMatrixDecomposed} dm The destination decomposed matrix
* @param {float} progress A float value between 0-1, representing the percentage of completion
* @param {function} fn An easing function following the prototype function(pos){}
* @author Joe Lambert
* @returns {WebKitCSSMatrix} A new matrix for the tweened state
*/
this.tween = function(dm, progress, fn) {
if(fn === undefined)
fn = function(pos) {return pos;}; // Default to a linear easing
if(!dm)
dm = new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose());
var r = new CSSMatrixDecomposed(),
i = index = null,
trans = '';
progress = fn(progress);
for(index in components)
for(i in {x:'x', y:'y', z:'z', w:'w'})
r[index][i] = parseFloat((this[index][i] + (dm[index][i] - this[index][i]) * progress ).toFixed(10));
trans = 'matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, '+r.perspective.x+', '+r.perspective.y+', '+r.perspective.z+', '+r.perspective.w+') ' +
'translate3d('+r.translate.x+'px, '+r.translate.y+'px, '+r.translate.y+'px) ' +
'rotateX('+r.rotate.x+'rad) rotateY('+r.rotate.y+'rad) rotateZ('+r.rotate.z+'rad) ' +
'matrix3d(1,0,0,0, 0,1,0,0, 0,'+r.skew.z+',1,0, 0,0,0,1) ' +
'matrix3d(1,0,0,0, 0,1,0,0, '+r.skew.y+',0,1,0, 0,0,0,1) ' +
'matrix3d(1,0,0,0, '+r.skew.x+',1,0,0, 0,0,1,0, 0,0,0,1) ' +
'scale3d('+r.scale.x+', '+r.scale.y+', '+r.scale.z+')';
try { r = new WebKitCSSMatrix(trans); return r; }
catch(e) { console.error('Invalid matrix string: '+trans); return '' };
};
};
/**
* Tween between two matrices
* @param {WebKitCSSMatrix} matrix The destination matrix
* @param {float} progress A float value between 0-1, representing the percentage of completion
* @param {function} fn An easing function following the prototype function(pos){}
* @author Joe Lambert
* @returns {WebKitCSSMatrix} A new matrix for the tweened state
*/
WebKitCSSMatrix.prototype.tween = function(matrix, progress, fn) {
if(fn === undefined)
fn = function(pos) {return pos;}; // Default to a linear easing
var m = new WebKitCSSMatrix,
m1 = this.decompose(),
m2 = matrix.decompose(),
r = m.decompose()
trans = '',
index = i = null;
// Tween between the two decompositions
return m1.tween(m2, progress, fn);
};
/**
* Transform a Vector4 object using the current matrix
* @param {Vector4} v The vector to transform
* @author Joe Lambert
* @returns {Vector4} The transformed vector
*/
WebKitCSSMatrix.prototype.transformVector = function(v) {
// TODO: Do we need to mod this for Vector4?
return new Vector4( this.m11*v.x + this.m12*v.y + this.m13*v.z,
this.m21*v.x + this.m22*v.y + this.m23*v.z,
this.m31*v.x + this.m32*v.y + this.m33*v.z );
};
/**
* Transposes the matrix
* @author Joe Lambert
* @returns {WebKitCSSMatrix} The transposed matrix
*/
WebKitCSSMatrix.prototype.transpose = function() {
var matrix = new WebKitCSSMatrix(), n = m = 0;
for (n = 0; n <= 4-2; n++)
{
for (m = n + 1; m <= 4-1; m++)
{
matrix['m'+(n+1)+(m+1)] = this['m'+(m+1)+(n+1)];
matrix['m'+(m+1)+(n+1)] = this['m'+(n+1)+(m+1)];
}
}
return matrix;
};
/**
* Calculates the determinant
* @author Joe Lambert
* @returns {float} The determinant of the matrix
*/
WebKitCSSMatrix.prototype.determinant = function() {
return this.m14 * this.m23 * this.m32 * this.m41-this.m13 * this.m24 * this.m32 * this.m41 -
this.m14 * this.m22 * this.m33 * this.m41+this.m12 * this.m24 * this.m33 * this.m41 +
this.m13 * this.m22 * this.m34 * this.m41-this.m12 * this.m23 * this.m34 * this.m41 -
this.m14 * this.m23 * this.m31 * this.m42+this.m13 * this.m24 * this.m31 * this.m42 +
this.m14 * this.m21 * this.m33 * this.m42-this.m11 * this.m24 * this.m33 * this.m42 -
this.m13 * this.m21 * this.m34 * this.m42+this.m11 * this.m23 * this.m34 * this.m42 +
this.m14 * this.m22 * this.m31 * this.m43-this.m12 * this.m24 * this.m31 * this.m43 -
this.m14 * this.m21 * this.m32 * this.m43+this.m11 * this.m24 * this.m32 * this.m43 +
this.m12 * this.m21 * this.m34 * this.m43-this.m11 * this.m22 * this.m34 * this.m43 -
this.m13 * this.m22 * this.m31 * this.m44+this.m12 * this.m23 * this.m31 * this.m44 +
this.m13 * this.m21 * this.m32 * this.m44-this.m11 * this.m23 * this.m32 * this.m44 -
this.m12 * this.m21 * this.m33 * this.m44+this.m11 * this.m22 * this.m33 * this.m44;
};
/**
* Decomposes the matrix into its component parts.
* A Javascript implementation of the pseudo code available from http://www.w3.org/TR/css3-2d-transforms/#matrix-decomposition
* @author Joe Lambert
* @returns {Object} An object with each of the components of the matrix (perspective, translate, skew, scale, rotate) or identity matrix on failure
*/
WebKitCSSMatrix.prototype.decompose = function() {
var matrix = new WebKitCSSMatrix(this.toString()),
perspectiveMatrix = rightHandSide = inversePerspectiveMatrix = transposedInversePerspectiveMatrix =
perspective = translate = row = i = scale = skew = pdum3 = rotate = null;
if (matrix.m33 == 0)
return new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose()); // Return the identity matrix
// Normalize the matrix.
for (i = 1; i <= 4; i++)
for (j = 1; j <= 4; j++)
matrix['m'+i+j] /= matrix.m44;
// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
perspectiveMatrix = matrix;
for (i = 1; i <= 3; i++)
perspectiveMatrix['m'+i+'4'] = 0;
perspectiveMatrix.m44 = 1;
if (perspectiveMatrix.determinant() == 0)
new CSSMatrixDecomposed(new WebKitCSSMatrix().decompose()); // Return the identity matrix
// First, isolate perspective.
if (matrix.m14 != 0 || matrix.m24 != 0 || matrix.m34 != 0)
{
// rightHandSide is the right hand side of the equation.
rightHandSide = new Vector4(matrix.m14, matrix.m24, matrix.m34, matrix.m44);
// Solve the equation by inverting perspectiveMatrix and multiplying
// rightHandSide by the inverse.
inversePerspectiveMatrix = perspectiveMatrix.inverse();
transposedInversePerspectiveMatrix = inversePerspectiveMatrix.transpose();
perspective = transposedInversePerspectiveMatrix.transformVector(rightHandSide);
// Clear the perspective partition
matrix.m14 = matrix.m24 = matrix.m34 = 0;
matrix.m44 = 1;
}
else
{
// No perspective.
perspective = new Vector4(0,0,0,1);
}
// Next take care of translation
translate = new Vector4(matrix.m41, matrix.m42, matrix.m43);
matrix.m41 = 0;
matrix.m42 = 0;
matrix.m43 = 0;
// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
row = [
new Vector4(), new Vector4(), new Vector4()
];
for (i = 1; i <= 3; i++)
{
row[i-1].x = matrix['m'+i+'1'];
row[i-1].y = matrix['m'+i+'2'];
row[i-1].z = matrix['m'+i+'3'];
}
// Compute X scale factor and normalize first row.
scale = new Vector4();
skew = new Vector4();
scale.x = row[0].length();
row[0] = row[0].normalise();
// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew.x = row[0].dot(row[1]);
row[1] = row[1].combine(row[0], 1.0, -skew.x);
// Now, compute Y scale and normalize 2nd row.
scale.y = row[1].length();
row[1] = row[1].normalise();
skew.x /= scale.y;
// Compute XZ and YZ shears, orthogonalize 3rd row
skew.y = row[0].dot(row[2]);
row[2] = row[2].combine(row[0], 1.0, -skew.y);
skew.z = row[1].dot(row[2]);
row[2] = row[2].combine(row[1], 1.0, -skew.z);
// Next, get Z scale and normalize 3rd row.
scale.z = row[2].length();
row[2] = row[2].normalise();
skew.y /= scale.z;
skew.y /= scale.z;
// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip. If the determinant
// is -1, then negate the matrix and the scaling factors.
pdum3 = row[1].cross(row[2])
if (row[0].dot(pdum3) < 0)
{
for (i = 0; i < 3; i++)
{
scale.x *= -1;
row[i].x *= -1;
row[i].y *= -1;
row[i].z *= -1;
}
}
// Now, get the rotations out
rotate = new Vector4();
rotate.y = Math.asin(-row[0].z);
if (Math.cos(rotate.y) != 0)
{
rotate.x = Math.atan2(row[1].z, row[2].z);
rotate.z = Math.atan2(row[0].y, row[0].x);
}
else
{
rotate.x = Math.atan2(-row[2].x, row[1].y);
rotate.z = 0;
}
return new CSSMatrixDecomposed({
perspective: perspective,
translate: translate,
skew: skew,
scale: scale,
rotate: rotate
});
};
})();
/**
Mifty - A custom build of Shifty for use with Morf.js.
By Jeremy Kahn - jeremyckahn@gmail.com
v0.4.1
For instructions on how to use Shifty, please consult the README: https://github.com/jeremyckahn/shifty/blob/master/README.md
MIT Lincense. This code free to use, modify, distribute and enjoy.
*/
(function(e){function a(){return+new Date}function b(a,c){for(var j in a)a.hasOwnProperty(j)&&c(a,j)}function h(a,c){b(c,function(c,f){a[f]=c[f]});return a}function k(a,c){b(c,function(c,f){typeof a[f]==="undefined"&&(a[f]=c[f])});return a}function i(a,c,j){var f,a=(a-c.timestamp)/c.duration;for(f in j.current)j.current.hasOwnProperty(f)&&c.to.hasOwnProperty(f)&&(j.current[f]=c.originalState[f]+(c.to[f]-c.originalState[f])*c.easingFunc(a));return j.current}function n(a,c,j,f){var b;for(b=0;b<c[a].length;b++)c[a][b].apply(j,
f)}function l(a,c,j){b(e.Tweenable.prototype.filter,function(b,e){b[e][a]&&b[e][a].apply(c,j)})}function m(d,c){var b;b=a();b<d.timestamp+d.duration&&c.isAnimating?(l("beforeTween",d.owner,[c.current,d.originalState,d.to]),i(b,d,c),l("afterTween",d.owner,[c.current,d.originalState,d.to]),d.hook.step&&n("step",d.hook,d.owner,[c.current]),d.step.call(c.current),c.loopId=setTimeout(function(){m(d,c)},1E3/d.owner.fps)):d.owner.stop(!0)}function g(a){a=a||{};this._hook={};this._tweenParams={owner:this,
hook:this._hook};this._state={};this._state.current=a.initialState||{};this.fps=a.fps||30;this.easing=a.easing||"linear";this.duration=a.duration||500;return this}g.prototype.tween=function(d,c,b,e,g){var i=this;if(!this._state.isAnimating)return this._state.loopId=0,this._state.pausedAtTime=null,c?(this._tweenParams.step=function(){},this._state.current=d||{},this._tweenParams.to=c||{},this._tweenParams.duration=b||this.duration,this._tweenParams.callback=e||function(){},this._tweenParams.easing=
g||this.easing):(this._tweenParams.step=d.step||function(){},this._tweenParams.callback=d.callback||function(){},this._state.current=d.from||{},this._tweenParams.to=d.to||d.target||{},this._tweenParams.duration=d.duration||this.duration,this._tweenParams.easing=d.easing||this.easing),this._tweenParams.timestamp=a(),this._tweenParams.easingFunc=this.formula[this._tweenParams.easing]||this.formula.linear,k(this._state.current,this._tweenParams.to),k(this._tweenParams.to,this._state.current),l("tweenCreated",
this._tweenParams.owner,[this._state.current,this._tweenParams.originalState,this._tweenParams.to]),this._tweenParams.originalState=h({},this._state.current),this._state.isAnimating=!0,setTimeout(function(){m(i._tweenParams,i._state)},1E3/this.fps),this};g.prototype.to=function(a,c,b,e){typeof c==="undefined"?(a.from=this.get(),this.tween(a)):this.tween(this.get(),a,c,b,e);return this};g.prototype.get=function(){return this._state.current};g.prototype.set=function(a){this._state.current=a||{};return this};
g.prototype.stop=function(a){clearTimeout(this._state.loopId);this._state.isAnimating=!1;a&&(h(this._state.current,this._tweenParams.to),this._tweenParams.callback.call(this._state.current));return this};g.prototype.pause=function(){clearTimeout(this._state.loopId);this._state.pausedAtTime=a();this._state.isPaused=!0;return this};g.prototype.resume=function(){var a=this;this._state.isPaused&&(this._tweenParams.timestamp+=this._state.pausedAtTime-this._tweenParams.timestamp);setTimeout(function(){m(a._tweenParams,
a._state)},1E3/this.fps);return this};g.prototype.hookAdd=function(a,c){this._hook.hasOwnProperty(a)||(this._hook[a]=[]);this._hook[a].push(c)};g.prototype.hookRemove=function(a,c){var b;if(this._hook.hasOwnProperty(a))if(c)for(b=this._hook[a].length;b>=0;b++)this._hook[a][b]===c&&this._hook[a].splice(b,1);else this._hook[a]=[]};g.prototype.filter={};g.util={now:a,each:b,tweenProps:i,applyFilter:l,simpleCopy:h};g.prototype.formula={linear:function(a){return a}};e.Tweenable=g})(this);
(function(e){e.Tweenable.util.simpleCopy(e.Tweenable.prototype.formula,{easeInQuad:function(a){return Math.pow(a,2)},easeOutQuad:function(a){return-(Math.pow(a-1,2)-1)},easeInOutQuad:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,2);return-0.5*((a-=2)*a-2)},easeInCubic:function(a){return Math.pow(a,3)},easeOutCubic:function(a){return Math.pow(a-1,3)+1},easeInOutCubic:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,3);return 0.5*(Math.pow(a-2,3)+2)},easeInQuart:function(a){return Math.pow(a,4)},easeOutQuart:function(a){return-(Math.pow(a-
1,4)-1)},easeInOutQuart:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,4);return-0.5*((a-=2)*Math.pow(a,3)-2)},easeInQuint:function(a){return Math.pow(a,5)},easeOutQuint:function(a){return Math.pow(a-1,5)+1},easeInOutQuint:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,5);return 0.5*(Math.pow(a-2,5)+2)},easeInSine:function(a){return-Math.cos(a*(Math.PI/2))+1},easeOutSine:function(a){return Math.sin(a*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a)-1)},easeInExpo:function(a){return a==
0?0:Math.pow(2,10*(a-1))},easeOutExpo:function(a){return a==1?1:-Math.pow(2,-10*a)+1},easeInOutExpo:function(a){if(a==0)return 0;if(a==1)return 1;if((a/=0.5)<1)return 0.5*Math.pow(2,10*(a-1));return 0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return-(Math.sqrt(1-a*a)-1)},easeOutCirc:function(a){return Math.sqrt(1-Math.pow(a-1,2))},easeInOutCirc:function(a){if((a/=0.5)<1)return-0.5*(Math.sqrt(1-a*a)-1);return 0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeOutBounce:function(a){return a<1/2.75?7.5625*
a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},easeInBack:function(a){return a*a*(2.70158*a-1.70158)},easeOutBack:function(a){return(a-=1)*a*(2.70158*a+1.70158)+1},easeInOutBack:function(a){var b=1.70158;if((a/=0.5)<1)return 0.5*a*a*(((b*=1.525)+1)*a-b);return 0.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},elastic:function(a){return-1*Math.pow(4,-8*a)*Math.sin((a*6-1)*2*Math.PI/2)+1},swingFromTo:function(a){var b=1.70158;return(a/=0.5)<
1?0.5*a*a*(((b*=1.525)+1)*a-b):0.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},swingFrom:function(a){return a*a*(2.70158*a-1.70158)},swingTo:function(a){return(a-=1)*a*(2.70158*a+1.70158)+1},bounce:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},bouncePast:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?2-(7.5625*(a-=1.5/2.75)*a+0.75):a<2.5/2.75?2-(7.5625*(a-=2.25/2.75)*a+0.9375):2-(7.5625*(a-=2.625/2.75)*
a+0.984375)},easeFromTo:function(a){if((a/=0.5)<1)return 0.5*Math.pow(a,4);return-0.5*((a-=2)*Math.pow(a,3)-2)},easeFrom:function(a){return Math.pow(a,4)},easeTo:function(a){return Math.pow(a,0.25)}})})(this);
(function(e){if(e.Tweenable)e.Tweenable.util.interpolate=function(a,b,h,k){var i;if(a&&a.from)b=a.to,h=a.position,k=a.easing,a=a.from;i=e.Tweenable.util.simpleCopy({},a);e.Tweenable.util.applyFilter("tweenCreated",i,[i,a,b]);e.Tweenable.util.applyFilter("beforeTween",i,[i,a,b]);h=e.Tweenable.util.tweenProps(h,{originalState:a,to:b,timestamp:0,duration:1,easingFunc:e.Tweenable.prototype.formula[k]||e.Tweenable.prototype.formula.linear},{current:i});e.Tweenable.util.applyFilter("afterTween",h,[h,a,
b]);return h},e.Tweenable.prototype.interpolate=function(a,b,h){a=e.Tweenable.util.interpolate(this.get(),a,b,h);this.set(a);return a}})(this);
/**
* @preserve
* Extra easing functions borrowed from scripty2 (c) 2005-2010 Thomas Fuchs (MIT Licence)
* https://raw.github.com/madrobby/scripty2/master/src/effects/transitions/transitions.js
*/
(function(){
var scripty2 = {
spring: function(pos) {
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
},
sinusoidal: function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5;
}
};
// Load the Scripty2 functions
for(var t in scripty2)
Tweenable.prototype.formula[t] = scripty2[t];
})();
@thednp
Copy link

thednp commented Feb 6, 2020

Hello there!!

This is the only script (decompose) I found to actually calculate the rotation properly. However,

.tf-test {
    transform: perspective(400px) rotateX(45deg) translateY(170px);
}

I get this object:

CSSMatrixDecomposed {
 perspective: Vector4 {x: 0, y: 0, z: 0, w: 1}
 rotate: Vector4 {x: 0.7853981633974483, y: -0, z: 0, w: 0}
 scale: Vector4 {x: 1.429633441985475, y: 1.4296338843840455, z: 1.4296338843840455, w: 0}
 skew: Vector4 {x: 0, y: 0, z: 0, w: 0}
 translate: Vector4 {x: 0, y: 171.85337679418998, z: 171.85337679418998, w: 0}
}
  • I don't get the right translation, it's off by around ~0.5%, and where does translateZ come from?
  • I'm getting 1.4 scale on all axes but I didn't set any
  • I didn't know skewZ is a thing apparently
  • no perspective

I want to know if you have any plans on correcting these issues and I thank you for your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment