zoukankan      html  css  js  c++  java
  • Canvas transform浅析

    没有前奏,直接进入主题


    transform调用方法:

    ctx.transform(a,b,c,d,e,f);如下

    var ctx = document.getElementById("myCanvas").getContext("2d");
    
    //调用
    ctx.transform(1,1,-1,1,1,1);
    
    //画个圆形的路径
    ctx.arc(200,100,25,0,2*Math.PI);
    
    //画个已填充的矩形
    ctx.fillRect(200,150,50,50);
    
    //对路径描边
    ctx.stroke();

    结果如下(只截取了主要部分):

     使用transform前                       使用transform后

    咋一看,三个变化:

      ①两个图形的位置变化了  

      ②矩形的角度变化了  

      ③图形的大小变化

    这里正好反应了三个属性:位移旋转缩放,对应canvas的另外三个API则是translate()、rotate()、scale()。这三个API使用起来直观,但是却不能实现某些特定的效果,比如斜切

    var canvas = document.getElementById("myCanvas");
    
    var ctx = canvas.getContext("2d");
    
    //Y轴逐渐拉伸,得到上图右边状态
    ctx.transform(1,Math.PI/18,0,1,0,0);
    
    ctx.fillRect(200,150,50,50);

    仅通过原有的三个比较直观的API并不能实现这个效果,然而或许你在别的地方看过,transform除了可以实现斜切,也可以直接替代掉原有的三个API实现它们对应的效果。

    (一)位移

    用transform来替代就是修改最后两个参数: ctx.transform(a,b,c,d,e,f) 其中的e和f两个参数。

    (二)旋转

    旋转呢,就需要配合四个参数来实现了,ctx.transform(a,b,c,d,e,f)其中的abcd四个参数(为什么要四个?先别急,后面再说,总之你记住就好了)。

    (三)缩放

    缩放仅需要修改前两个参数即可,ctx.transform(a,b,c,d,e,f);

    --------------------------------------

    请记住三种情况对应的相关参数

    --------------------------------------

    接下来咱们说说为什么?

    先说简单的:位移和缩放

    对于坐标轴上的任意一点 A(x,y)移动到B(x1,y1),则B点的坐标值可以用如下的等式表示:

    x1 = x + e

    y1 = y + f

    同理,对于坐标轴上的任意一点 A(x,y)缩放到B(x1,y1),则B点的坐标值可以用如下的等式表示:

    x= x * a

    y1 = y * d

    其中a、d对应transform中的第一和第四个参数,位移的时候e和f代表具体的数值,而缩放的时候a和d则代表了对应x或者h轴上的缩放倍数,所以默认的倍数当然是1(也就是原来的大小),默认的位移当然是0,所以我们现在知道transform(a,b,c,d,e,f)的参数中a,d为1,e,f为0。

    接下来看比较复杂的旋转,如图:

    以点(0,0)为中心,将点A(100,-100)旋转45°后求点B的坐标轴是多少?(因为在我们的Canvas中y轴向下才是正数,所以请换位思考一下)。

    如果你会解答这道题,并且能推演一个坐标随角度变化而变化的公式,那么你很对得起你的几何老师,可惜我推不出来,我数学是挂科的!!!所以我就直接把答案搁这儿了:

    x = x * cos(弧度)  -  y * sin(弧度);

    y = y * cos(弧度)  +  x * sin(弧度); 

    弧度 = Math.PI * 180 / 角度

    -------------------------------------------------------------

    最终的公式

    x= ax + cy + e

    y1 = bx + dy + f

    调用:

    ctx.transform(cos(弧度),sin(弧度),-sin(弧度),cos(弧度),0,0);

    var canvas = document.getElementById("myCanvas");
    
    var ctx = canvas.getContext("2d");
    
    var deg = Math.PI/180;
    
    //旋转45°
    ctx.transform(Math.cos(deg*45),Math.sin(deg*45),-Math.sin(deg*45),Math.cos(deg*45),0,0);
    
    ctx.fillRect(200,0,50,50);

    如果我要旋转45°并且放大两倍,位移到(100,100)呢?我并没有在别的地方找到答案。我以为a和d既然是控制缩放的,那么乘以2如何?

    var canvas = document.getElementById("myCanvas");
    
    var ctx = canvas.getContext("2d");
    
    var deg = Math.PI/180;
    
    ctx.fillRect(200,0,50,50);  //第一个正方形(未变形)
    
    
    ctx.setTransform(1,0,0,1,0,0);
    ctx.beginPath();
    ctx.transform(2*Math.cos(deg*45),Math.sin(deg*45),-Math.sin(deg*45),2*Math.cos(deg*45),0,0);
    
    ctx.fillRect(200,0,50,50);   //第二个正方形(仅a和d乘以2)
    
    ctx.setTransform(1,0,0,1,0,0);
    ctx.beginPath();
    ctx.transform(2*Math.cos(deg*45),2*Math.sin(deg*45),-2*Math.sin(deg*45),2*Math.cos(deg*45),0,0);
    
    ctx.fillRect(200,0,50,50);  //第三个正方形(正确的解答)

    你没看错,abcd四个参数全部乘以2才是正确的(对于数学不太好的我,花了四五个小时也想不通,最后斗胆一试全部乘以2居然神奇的正确了O(∩_∩)O~~)

    与下面两种方法得到相同的结果:

    //第一:直接使用缩放和旋转的API
    ctx.scale(2,2);
    ctx.rotate(Math.PI/180*45);
    
    
    //第二:使用transform分别替换上面两种API
    var deg = Math.PI/180;
    ctx.transform(2,0,0,2,0,0);
    ctx.transform(Math.cos(45*deg),Math.sin(45*deg),-Math.sin(45*deg),Math.cos(45*deg),100,100);

    总结:

    1. 使用transform完全可以替换原有的三个API并且能够做出更多的效果,比如斜切;
    2. transform如果既有平移又有旋转,则先平移后旋转(平移和旋转顺序不同,结果不同,我拿着纸画了,确实不同 - -|);
    3. 如果transform的第二第三个参数中,一个为零一个非零,则会得到斜切;第二个参数拉伸Y轴,第三个X轴
    4. 旋转效果中,如果第二个为负数,第三个为正数,则旋转方向是逆时针的
    5. transform的效果可以叠加,setTransform则可以用来将画布归回原位后再进行相应的效果处理;
  • 相关阅读:
    HDOJ 1846 Brave Game
    并查集模板
    HDU 2102 A计划
    POJ 1426 Find The Multiple
    POJ 3278 Catch That Cow
    POJ 1321 棋盘问题
    CF 999 C.Alphabetic Removals
    CF 999 B. Reversing Encryption
    string的基础用法
    51nod 1267 4个数和为0
  • 原文地址:https://www.cnblogs.com/gradolabs/p/4766811.html
Copyright © 2011-2022 走看看