HTML5 Canvas get transform matrix?

前端 未结 4 1912
一向
一向 2020-12-14 06:57

Is there a way to get the current transformation matrix for a canvas? There is a context.setTransform() function, but there does not seem to be a getTransform() equivalent a

4条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-14 07:49

    EDIT(1/10/2020): MDN now indicates that getTransform() is supported in most major browsers; the code below may still have value as a part of implementing a polyfill for Internet Explorer, Edge, and Android Firefox.

    EDIT(6/27/2016): The WHATWG spec now has a function getTransform() instead of currentTransform and it appears semantically clear that getTransform() creates a copy of the transformation matrix. Looks like it is still missing from major browsers.

    EDIT, again:

    Here's a rough implementation:

    //in theory, SVGMatrix will be used by the Canvas API in the future;
    //in practice, we can borrow an SVG matrix today!
    var createMatrix = function() {
      var svgNamespace = "http://www.w3.org/2000/svg";
      return document.createElementNS(svgNamespace, "g").getCTM();
    }
    
    //`enhanceContext` takes a 2d canvas context and wraps its matrix-changing
    //functions so that `context._matrix` should always correspond to its
    //current transformation matrix.
    //Call `enhanceContext` on a freshly-fetched 2d canvas context for best
    //results.
    var enhanceContext = function(context) {
      var m = createMatrix();
      context._matrix = m;
    
      //the stack of saved matrices
      context._savedMatrices = [m];
    
      var super_ = context.__proto__;
      context.__proto__ = ({
    
        //helper for manually forcing the canvas transformation matrix to
        //match the stored matrix.
        _setMatrix: function() {
          var m = this._matrix;
          super_.setTransform.call(this, m.a, m.b, m.c, m.d, m.e, m.f);
        },
    
        save: function() {
          this._savedMatrices.push(this._matrix);
          super_.save.call(this);
        },
    
        //if the stack of matrices we're managing doesn't have a saved matrix,
        //we won't even call the context's original `restore` method.
        restore: function() {
          if(this._savedMatrices.length == 0)
            return;
          super_.restore.call(this);
          this._matrix = this._savedMatrices.pop();
          this._setMatrix();
        },
    
        scale: function(x, y) {
          this._matrix = this._matrix.scaleNonUniform(x, y);
          super_.scale.call(this, x, y);
        },
    
        rotate: function(theta) {
          //canvas `rotate` uses radians, SVGMatrix uses degrees.
          this._matrix = this._matrix.rotate(theta * 180 / Math.PI);
          super_.rotate.call(this, theta);
        },
    
        translate: function(x, y) {
          this._matrix = this._matrix.translate(x, y);
          super_.translate.call(this, x, y);
        },
    
        transform: function(a, b, c, d, e, f) {
          var rhs = createMatrix();
          //2x2 scale-skew matrix
          rhs.a = a; rhs.b = b;
          rhs.c = c; rhs.d = d;
    
          //translation vector
          rhs.e = e; rhs.f = f;
          this._matrix = this._matrix.multiply(rhs);
          super_.transform.call(this, a, b, c, d, e, f);
        },
    
        //warning: `resetTransform` is not implemented in at least some browsers
        //and this is _not_ a shim.
        resetTransform: function() {
          this._matrix = createMatrix();
          super_.resetTransform.call(this);
        },
    
        __proto__: super_
      });
    
      return context;  
    };
    

    EDIT: The attribute currentTransform has been added to the spec; it is reported to be supported in Firefox and Opera. I checked on Firefox and found it vendor-prefixed as mozCurrentTransform. Presumably it can be used to both get and set the transform matrix.

    OLDER STUFF, STILL MOSTLY TRUE:

    If you want to get the current transformation matrix, you'll have to keep track of it yourself. One way of doing this would be to use Javascript's prototypical inheritance to add a getMatrix() method and augment the methods which modify the matrix:

    var context = canvas.getContext("2d");
    var super = context.__proto__;
    context.__proto__ = ({
    
      __proto__: super, //"inherit" default behavior
    
      getMatrix: function() { return this.matrix; },
    
      scale: function(x, y) {
    
        //assuming the matrix manipulations are already defined...
        var newMatrix = scaleMatrix(x, y, this.getMatrix());
        this.matrix = newMatrix;
        return super.scale.call(this, x, y);
      },
      /* similar things for rotate, translate, transform, setTransform */
      /* ... */
    });
    context.matrix = makeDefaultMatrix();
    

    To really get it right, you'd need to track multiple matrices when the save() and restore() methods of the context are used.

提交回复
热议问题