Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on Aug 23rd, 2012  |  syntax: None  |  size: 7.39 KB  |  hits: 7  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /** Transformations Queue operations **/
  2. var OPERATIONS = ['scale', 'rotate', 'translate'];
  3.  
  4. /**
  5.  * A mixin to assist in managing 3d matrix transformations on a dom element
  6.  */
  7.  
  8. var Transformations = exports.Transformations = {
  9.   /**
  10.    * initialize the transformations mixin
  11.    */
  12.   initTransformations: function(el) {
  13.     this._el = el;
  14.     this._queue = [];
  15.     this.pushMatrix();
  16.  
  17.     this._scaleMatrix = new WebKitCSSMatrix();
  18.     this._rotationXMatrix = new WebKitCSSMatrix();
  19.     this._rotationYMatrix = new WebKitCSSMatrix();
  20.     this._rotationZMatrix = new WebKitCSSMatrix();
  21.   },
  22.  
  23.   /**
  24.    * set the opacity to be applied at the next commit
  25.    */
  26.   setOpacity: function(opacity) {
  27.     this._opacity = opacity;
  28.     return this;
  29.   },
  30.  
  31.   /**
  32.    * rotate the current matrix
  33.    */
  34.   rotate: function() {
  35.     var o = this._normalizeArguments.apply(this, arguments);
  36.     this._rotationXMatrix = this._rotationXMatrix.rotate(o.x,0,0);
  37.     this._rotationYMatrix = this._rotationYMatrix.rotate(0,o.y,0);
  38.     this._rotationZMatrix = this._rotationZMatrix.rotate(0,0,o.z);
  39.     this.currentRotation = o;
  40.     return this;
  41.   },
  42.  
  43.   /**
  44.    * scale the current matrix
  45.    */
  46.   scale: function() {
  47.     var o = this._normalizeArguments.apply(this, arguments);
  48.     this._scaleMatrix.m11 = o.x;
  49.     this._scaleMatrix.m22 = o.y;
  50.     this._scaleMatrix.m33 = o.z;
  51.     this.currentScale = o;
  52.     return this;
  53.   },
  54.  
  55.   /**
  56.    * perform a translation on the current matrix
  57.    */
  58.   translate: function() {
  59.     var o = this._normalizeArguments.apply(this, arguments);
  60.     this.setCurrentMatrix(this.getCurrentMatrix().translate(o.x, o.y, o.z));
  61.     return this;
  62.   },
  63.  
  64.   /**
  65.    * retrieve the top matrix from the stack
  66.    */
  67.   getCurrentMatrix: function() {
  68.     if (this._matrices.length === 0) {
  69.       this.pushMatrix();
  70.     }
  71.     return this._matrices[this._matrices.length - 1];
  72.   },
  73.  
  74.   /**
  75.    * update the top matrix on the stack
  76.    */
  77.   setCurrentMatrix: function(val) {
  78.     if (this._matrices.length === 0) {
  79.       this.pushMatrix();
  80.     }
  81.     this._matrices[this._matrices.length - 1] = val;
  82.     return this;
  83.   },
  84.  
  85.   /**
  86.    * push a matrix onto the stack. If no argument is provide an empty
  87.    * matrix is automatically created
  88.    */
  89.   pushMatrix: function(matrix) {
  90.     this._matrices = this._matrices || [];
  91.     this._matrices.push(matrix || new WebKitCSSMatrix());
  92.     return this;
  93.   },
  94.  
  95.   /**
  96.    * remove a matrix from the stack
  97.    */
  98.   popMatrix: function() {
  99.     this._matrices.pop();
  100.     return this;
  101.   },
  102.  
  103.   /**
  104.    * retrieve the current cumulative translation
  105.    */
  106.   cumulativeTranslation: function() {
  107.     var transform = this.cumulativeTransformation();
  108.     return {
  109.       x: transform.m41,
  110.       y: transform.m42,
  111.       z: transform.m43
  112.     };
  113.   },
  114.  
  115.   /**
  116.    * retrieve the current cumulative scale
  117.    */
  118.   cumulativeScale: function() {
  119.     var transform = this.cumulativeTransformation();
  120.     return {
  121.       x: transform.m11,
  122.       y: transform.m22,
  123.       z: transform.m33
  124.     };
  125.   },
  126.  
  127.   /**
  128.    * calculate the cumulative transformation matrix
  129.    */
  130.   cumulativeTransformation: function() {
  131.     if (this._matrices.length === 0) {
  132.       return new WebKitCSSMatrix();
  133.     }
  134.     var matrix = this._matrices.reduce(function(memo, value) {
  135.       return memo.multiply(value);
  136.     });
  137.  
  138.     matrix = matrix.multiply(this._rotationXMatrix);
  139.     matrix = matrix.multiply(this._rotationYMatrix);
  140.     matrix = matrix.multiply(this._rotationZMatrix);
  141.     matrix = matrix.multiply(this._scaleMatrix);
  142.  
  143.     return matrix;
  144.   },
  145.  
  146.   transform: function(options) {
  147.     this._queue.unshift(options);
  148.     if (options.duration) {
  149.       if (!this._flushing) {
  150.         this._flushing = true;
  151.         setTimeout(this._flushQueue, 0, this);
  152.       }
  153.     } else {
  154.       this._flushQueue(this);
  155.     }
  156.     return this;
  157.   },
  158.  
  159.   then: function(callback) {
  160.     this._queue.unshift(callback);
  161.     if (!this._flushing) {
  162.       this._flushing = true;
  163.       setTimeout(this._flushQueue, 0, this);
  164.     }
  165.     return this;
  166.   },
  167.  
  168.   save: function() {
  169.     this._queue.unshift({save: true});
  170.     if (!this._flushing) {
  171.       this._flushing = true;
  172.       setTimeout(this._flushQueue, 0, this);
  173.     }
  174.     return this;
  175.   },
  176.  
  177.   revert: function(options) {
  178.     options = options || {};
  179.     options.revert = true;
  180.     this._queue.unshift(options);
  181.     if (options.duration) {
  182.       if (!this._flushing) {
  183.         this._flushing = true;
  184.         setTimeout(this._flushQueue, 0, this);
  185.       }
  186.     } else {
  187.       this._flushQueue(this);
  188.     }
  189.     return this;
  190.   },
  191.  
  192.   _flushQueue: function(ctx) {
  193.     var command = ctx._queue.pop();
  194.     if (!command) {
  195.       ctx._flushing = false;
  196.       return;
  197.     }
  198.  
  199.     var procede = (function() {
  200.       return function() {
  201.         ctx._flushQueue(ctx);
  202.       };
  203.     })();
  204.  
  205.     if (typeof command === 'function') {
  206.       var wait = command(procede);
  207.       if (!wait) {
  208.         procede();
  209.       }
  210.     } else if (command.save) {
  211.       ctx.pushMatrix();
  212.       procede();
  213.     } else if (command.revert) {
  214.       ctx.popMatrix();
  215.       ctx.commit(command, procede);
  216.     } else {
  217.       ctx._invokeCommand(command, procede)
  218.       ctx.commit(command, procede);
  219.     }
  220.   },
  221.  
  222.   _invokeCommand: function(command) {
  223.     for (var i = 0, len = OPERATIONS.length; i < len; i++) {
  224.       var op = OPERATIONS[i];
  225.       if (typeof command[op] !== 'undefined') {
  226.         this[op].call(this, command[op]);
  227.       }
  228.     }
  229.   },
  230.  
  231.   /**
  232.    * apply the current stack of transformations
  233.    */
  234.   commit: function(options, callback) {
  235.     options = options || {};
  236.  
  237.     if (options.duration || options.timing) {
  238.       this._setupTransition(options, callback);
  239.     }
  240.  
  241.     // commit the style changes
  242.     if (typeof options.opacity !== 'undefined') {
  243.       this._el.style.opacity = options.opacity;
  244.     }
  245.     this._el.style.webkitTransform = this.cumulativeTransformation().toString();
  246.  
  247.     return this;
  248.   },
  249.  
  250.   _setupTransition: function(options, callback) {
  251.     this._el.style.webkitTransitionProperty = '-webkit-transform, opacity';
  252.  
  253.     if (typeof options.duration !== 'undefined') {
  254.       this._el.style.webkitTransitionDuration = this._formatDuration(options.duration);
  255.     }
  256.  
  257.     if (typeof options.timing !== 'undefined') {
  258.       this._el.style.webkitTransitionTimingFunction = options.timing;
  259.     }
  260.  
  261.     if (callback) {
  262.       var _this = this;
  263.       this._el.addEventListener('webkitTransitionEnd', function(e) {
  264.         _this._el.removeEventListener('webkitTransitionEnd', arguments.callee);
  265.         _this._el.style.webkitTransitionProperty = '';
  266.         _this._el.style.webkitTransitionDuration = '';
  267.         _this._el.style.webkitTransitionTimingFunction = '';
  268.         callback();
  269.         e.stopPropagation();
  270.       });
  271.     }
  272.   },
  273.  
  274.   /**
  275.    * take the passed in arguments and convert them to a normalized object
  276.    * with x, y, z properties
  277.    * a single number argument will be converted to x:num, y:num, z:num
  278.    * three number arguments will be converted to x:num1, y:num2, z:num2
  279.    * an object argument will be left intact
  280.    */
  281.   _normalizeArguments: function() {
  282.     var args = Array.prototype.slice.call(arguments, 0);
  283.     if (args.length > 1) {
  284.       return {x: args[0], y: args[1], z: args[2]};
  285.     } else if (Array.isArray(args[0])) {
  286.       var arg = args[0];
  287.       return {x: arg[0], y: arg[1], z: arg[2]};
  288.     } else if (Object(args[0]) === args[0]) {
  289.       return args[0];
  290.     } else {
  291.       return {x: args[0], y: args[0], z: args[0]};
  292.     }
  293.   },
  294.  
  295.   _formatDuration: function(duration) {
  296.     return duration + 's, ' + duration + 's';
  297.   }
  298. };
  299.  
  300. Transformations.xfrm = Transformations.transform;