/** * @ignore * animation for transform property * @author yiminghe@gmail.com */ KISSY.add('anim/timer/transform', function (S, Dom, Fx) { function toMatrixArray(matrix) { matrix = matrix.split(/,/); matrix = S.map(matrix, function (v) { return myParse(v); }); return matrix; } function decomposeMatrix(matrix) { matrix = toMatrixArray(matrix); var scaleX, scaleY , skew , A = matrix[0], B = matrix[1] , C = matrix[2], D = matrix[3]; // Make sure matrix is not singular if (A * D - B * C) { // step (3) scaleX = Math.sqrt(A * A + B * B); A /= scaleX; B /= scaleX; // step (4) skew = A * C + B * D; C -= A * skew; D -= B * skew; // step (5) scaleY = Math.sqrt(C * C + D * D); C /= scaleY; D /= scaleY; skew /= scaleY; // step (6) if (A * D < B * C) { A = -A; B = -B; skew = -skew; scaleX = -scaleX; } // matrix is singular and cannot be interpolated } else { // In this case the elem shouldn't be rendered, hence scale == 0 scaleX = scaleY = skew = 0; } // The recomposition order is very important // see http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp#l971 return { 'translateX': myParse(matrix[4]), 'translateY': myParse(matrix[5]), 'rotate': myParse(Math.atan2(B, A) * 180 / Math.PI), 'skewX': myParse(Math.atan(skew) * 180 / Math.PI), 'skewY': 0, 'scaleX': myParse(scaleX), 'scaleY': myParse(scaleY) }; } function defaultDecompose() { return { 'translateX': 0, 'translateY': 0, 'rotate': 0, 'skewX': 0, 'skewY': 0, 'scaleX': 1, 'scaleY': 1 }; } function myParse(v) { return Math.round(parseFloat(v) * 1e5) / 1e5; } function getTransformInfo(transform) { transform = transform.split(")"); var trim = S.trim, i = -1, l = transform.length - 1, split, prop, val, ret = defaultDecompose(); // Loop through the transform properties, parse and multiply them while (++i < l) { split = transform[i].split("("); prop = trim(split[0]); val = split[1]; switch (prop) { case "translateX": case "translateY": case 'scaleX': case 'scaleY': ret[prop] = myParse(val); break; case 'rotate': case 'skewX': case 'skewY': var v = myParse(val); if (!S.endsWith(val, 'deg')) { v = v * 180 / Math.PI; } ret[prop] = v; break; case 'translate': case 'translate3d': val = val.split(","); ret.translateX = myParse(val[0]); ret.translateY = myParse(val[1] || 0); break; case 'scale': val = val.split(","); ret.scaleX = myParse(val[0]); ret.scaleY = myParse(val[1] || val[0]); break; case 'matrix': return decomposeMatrix(val); break; } } return ret; } function TransformFx() { TransformFx.superclass.constructor.apply(this, arguments); } S.extend(TransformFx, Fx, { load: function () { var self = this; TransformFx.superclass.load.apply(self, arguments); // user value has priority over computed value self.from = Dom.style(self.anim.node, 'transform') || self.from; if (self.from && self.from != 'none') { self.from = getTransformInfo(self.from); } else { self.from = defaultDecompose(); } if (self.to) { self.to = getTransformInfo(self.to); } else { self.to = defaultDecompose(); } }, interpolate: function (from, to, pos) { var interpolate = TransformFx.superclass.interpolate; var ret = {}; ret.translateX = interpolate(from.translateX, to.translateX, pos); ret.translateY = interpolate(from.translateY, to.translateY, pos); ret.rotate = interpolate(from.rotate, to.rotate, pos); ret.skewX = interpolate(from.skewX, to.skewX, pos); ret.skewY = interpolate(from.skewY, to.skewY, pos); ret.scaleX = interpolate(from.scaleX, to.scaleX, pos); ret.scaleY = interpolate(from.scaleY, to.scaleY, pos); return S.substitute('translate3d({translateX}px,{translateY}px,0) ' + 'rotate({rotate}deg) ' + 'skewX({skewX}deg) ' + 'skewY({skewY}deg) ' + 'scale({scaleX},{scaleY})', ret); } }); Fx.Factories.transform = TransformFx; return TransformFx; }, { requires: ['dom', './fx'] }); /** * @ignore * refer: * - http://louisremi.github.io/jquery.transform.js/index.html * - http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp#l971 */