【问题标题】:canvas matrix setTransform with pivot带枢轴的画布矩阵 setTransform
【发布时间】:2017-05-30 14:50:57
【问题描述】:

我在代码下做了仿射变换。

   /**
   * @description getAffineTransform using SRT
   * @param {Number} xScale xScale
   * @param {Number} yScale yScale
   * @param {Number} radian radian
   * @param {Point} position position
   * @param {Point} pivot pivot
   * @return {CanvasMatrix} CanvasMatrix
   * @member CoordinateSystem#getAffineTransform
   */
  getAffineTransform(xScale, yScale, radian, position, pivot) {
    let a, b, c, d;
    let matrix = new CanvasMatrix();

    // origin to pivot
    if (pivot) {
      matrix = matrix.multiply(new CanvasMatrix(1, 0, 0, 1, pivot.x, pivot.y));
    }

    // Scale
    matrix = matrix.multiply(new CanvasMatrix(xScale, 0, 0, yScale, 0, 0));

    // Rotate
    if (this._opt.orientation === ORIENTATION.CCW) {
      a = Math.cos(radian);
      b = -Math.sin(radian);
      c = Math.sin(radian);
      d = Math.cos(radian);
    } else {
      a = Math.cos(radian);
      b = Math.sin(radian);
      c = -Math.sin(radian);
      d = Math.cos(radian);
    }
    matrix = matrix.multiply(new CanvasMatrix(a, b, c, d, 0, 0));

    // Translate
    matrix = matrix.multiply(new CanvasMatrix(1, 0, 0, 1, position.x, position.y));

    // pivot to origin'
    if (pivot) {
      matrix = matrix.multiply(new CanvasMatrix(1, 0, 0, 1, -pivot.x, -pivot.y));
    }

    return matrix;
  }

它在不移动枢轴的情况下工作得很好。 但是,如果我将旧枢轴移动到新枢轴,它就不起作用。 它被新的枢轴重置。 我怎样才能保持老支点。

【问题讨论】:

    标签: canvas matrix transform


    【解决方案1】:

    二维矩阵

    您拥有的函数过于复杂,使用 6 个矩阵来获得一个加所有乘法对于单个转换来说是很多工作。

    最好一次性处理二维矩阵

    // radian is negative if CCW
    // Pivot and position do not effect the first 4 of the matrix a,b,c,d
    // xScale and yScale only effect the x and y axis respectively so can be combine 
    // with the rotation
    // Returns matrix as a 6 item array [a,b,c,d,e,f]
    function getAffineTransform(xScale, yScale, radian, position, pivot){
        const m = [  // m for matrix
             Math.cos(radian) * xScale,
             Math.sin(radian) * xScale,
            -Math.sin(radian) * yScale,
             Math.cos(radian) * yScale,
        ];
        m[4] = pivot.x - position.x * m[0] - position.y * m[2];
        m[5] = pivot.y - position.x * m[1] - position.y * m[3];
        return m;
    }
    

    虽然我不确定你所说的枢轴(假设它是旋转点)是什么意思,而位置是从那个偏移量。

    如果反过来,则在返回前交换最后两行。

    我发现理解 2D 变换最好将值 a,b,c,d,e,f 视为定义 x 轴的比例和方向的向量 ( a,b) 定义 y 轴 (c,d) 的比例和方向以及原点坐标 (e,f) 的向量

    因此变换标识为 1,0(x 轴)、0,1(y 轴)和 0,0(原点),按 2 缩放为 2,0,0,2,0,0 以顺时针旋转 90是 0,2,-2,0,0,0

    使用滑块改变变换的示例

    const ctx = can.getContext("2d");
    function getAffineTransform(xScale, yScale, radian, position, pivot){
        const m = [  // m for matrix
             Math.cos(radian) * xScale,
             Math.sin(radian) * xScale,
            -Math.sin(radian) * yScale,
             Math.cos(radian) * yScale,
        ];
        m[4] = pivot.x - position.x * m[0] - position.y * m[2];
        m[5] = pivot.y - position.x * m[1] - position.y * m[3];
        return m;
    }
    const vals = {
        XScale : 1,
        YScale : 1,
        PosX : 0,
        PosY : 0,
        PivX : 0,
        PivY : 0,
        Rot : 0,
    }
    const props = Object.keys(vals);
    const mouse = { down : false };
    inputs.addEventListener("mousedown",()=>mouse.down = true);
    inputs.addEventListener("mouseup",()=>mouse.down = false);
    inputs.addEventListener("mousemove",()=>{
        if(mouse.down){
            updateDom();
        }
    })
    
    function updateDom(){
        props.forEach(key => { window["el"+key].textContent = vals[key] = Number(window["in"+ key].value); })
        drawBox()
    }
    function setupDom(){
        props.forEach(key => { window["el"+key].textContent = window["in"+ key].value = vals[key]; })
        drawBox()
    
    }
    
    
    function drawBox(){
        if(can.width !== innerWidth || can.height !== innerHeight){
            can.width = innerWidth;
            can.height = innerHeight;
        }
        ctx.setTransform(1,0,0,1,0,0);
        ctx.clearRect(0,0,innerWidth,innerHeight);
        const mat = getAffineTransform(
            vals.XScale,
            vals.YScale,
            vals.Rot,
            {x : vals.PosX, y: vals.PosY},
            {x : vals.PivX, y: vals.PivY}
        );
        ctx.setTransform(mat[0],mat[1],mat[2],mat[3],mat[4],mat[5]);
        ctx.strokeRect(0,0,100,100);
        ctx.strokeRect(vals.PosX-2,vals.PosY-2,4,4);
    
    }
    setupDom();
    canvas {
        position : absolute;
        top : 0px;
        left : 0px;
        z-index : -10;
    }
    <canvas id=can></canvas>
    <div id="inputs">
    <div><input id=inXScale type=range min=0.1 max=3 step=0.1></input><span id=elXScale></span> xScale</div>
    <div><input id=inYScale type=range min=0.1 max=3 step=0.1></input><span id=elYScale></span> yScale</div>
    <div><input id=inPosX type=range min=0 max=100></input><span id=elPosX></span> pos X</div>
    <div><input id=inPosY type=range min=0 max=100></input><span id=elPosY></span> pos Y</div>
    <div><input id=inPivX type=range min=0 max=500></input><span id=elPivX></span> pivot X</div>
    <div><input id=inPivY type=range min=0 max=500></input><span id=elPivY></span> pivot Y</div>
    <div><input id=inRot type=range  min=0 max=13 step=0.1></input><span id=elRot></span> rotate</div>
    </div>

    【讨论】:

      猜你喜欢
      • 2014-04-06
      • 2014-05-18
      • 2013-03-22
      • 1970-01-01
      • 1970-01-01
      • 2018-11-01
      • 1970-01-01
      • 2022-12-21
      相关资源
      最近更新 更多