画布矩阵setTransform with pivot

时间:2017-05-30 14:50:57

标签: canvas matrix transform

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

   /**
   * @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;
  }

没有移动枢轴,它工作得很好。 但是,如果我将旧枢轴移动到新枢轴,它就不起作用。 它由新的枢轴重置。 我怎样才能保持原状。

1 个答案:

答案 0 :(得分:0)

2D矩阵

你所拥有的函数过于复杂,6个矩阵得到一个加上所有乘法对于单个变换来说是很多工作。

2D矩阵最好一次性处理

// 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变换最好考虑值abcde,{{1定义x轴(a,b)矢量的矢量,定义y轴(c,d)的尺度和方向以及原点坐标(e,f)

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

示例使用滑块更改转换

f
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;
}