• 第156天:canvas(三)


    一、变形

    1.1 translate

    translate(x, y)

    ​ 用来移动 canvas 原点到指定的位置

    ​ translate方法接受两个参数。x 是左右偏移量,y 是上下偏移量,如右图所示。

    在做变形之前先保存状态是一个良好的习惯。大多数情况下,调用 restore 方法比手动恢复原先的状态要简单得多。又如果你是在一个循环中做位移但没有保存和恢复canvas 的状态,很可能到最后会发现怎么有些东西不见了,那是因为它很可能已经超出 canvas 范围以外了。

    ​ 注意:translate移动的是canvas的坐标原点。(坐标变换)

     1 var ctx;
     2 function draw(){
     3     var canvas = document.getElementById('tutorial1');
     4     if (!canvas.getContext) return;
     5     var ctx = canvas.getContext("2d");
     6     ctx.save(); //保存坐原点平移之前的状态
     7     ctx.translate(100, 100);
     8     ctx.strokeRect(0, 0, 100, 100)
     9     ctx.restore(); //恢复到最初状态
    10     ctx.translate(220, 220);
    11     ctx.fillRect(0, 0, 100, 100)
    12 }
    13 draw();

    1.2 rotate

    rotate(angle)

    ​ 旋转坐标轴。

    ​ 这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。

    ​ 旋转的中心是坐标原点。

     1 var ctx;
     2 function draw(){
     3   var canvas = document.getElementById('tutorial1');
     4   if (!canvas.getContext) return;
     5   var ctx = canvas.getContext("2d");
     6 
     7   ctx.fillStyle = "red";
     8   ctx.save();
     9 
    10   ctx.translate(100, 100);
    11   ctx.rotate(Math.PI / 180 * 45);
    12   ctx.fillStyle = "blue";
    13   ctx.fillRect(0, 0, 100, 100);
    14   ctx.restore();
    15 
    16   ctx.save();
    17   ctx.translate(0, 0);
    18   ctx.fillRect(0, 0, 50, 50)
    19   ctx.restore();
    20 }
    21 draw();

    1.3 scale

    scale(x, y)

    ​ 我们用它来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。

    ​ scale方法接受两个参数。x,y分别是横轴和纵轴的缩放因子,它们都必须是正值。值比 1.0 小表示缩 小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。

    ​ 默认情况下,canvas 的 1 单位就是 1 个像素。举例说,如果我们设置缩放因子是 0.5,1 个单位就变成对应 0.5 个像素,这样绘制出来的形状就会是原先的一半。同理,设置为 2.0 时,1 个单位就对应变成了 2 像素,绘制的结果就是图形放大了 2 倍。

    1.4 transform(变形矩阵)

    transform(a, b, c, d, e, f)

    a (m11)

    ​ Horizontal scaling.

    b (m12)

    ​ Horizontal skewing.

    c (m21)

    ​ Vertical skewing.

    d (m22)

    ​ Vertical scaling.

    e (dx)

    ​ Horizontal moving.

    f (dy)

    ​ Vertical moving.

    1 var ctx;
    2 function draw(){
    3     var canvas = document.getElementById('tutorial1');
    4     if (!canvas.getContext) return;
    5     var ctx = canvas.getContext("2d");
    6     ctx.transform(1, 1, 0, 1, 0, 0);
    7     ctx.fillRect(0, 0, 100, 100);
    8 }
    9 draw();

      

    二、合成

    ​ 在前面的所有例子中、,我们总是将一个图形画在另一个之上,对于其他更多的情况,仅仅这样是远远不够的。比如,对合成的图形来说,绘制顺序会有限制。不过,我们可以利用 globalCompositeOperation 属性来改变这种状况。

    globalCompositeOperation = type

     1   var ctx;
     2     function draw(){
     3         var canvas = document.getElementById('tutorial1');
     4         if (!canvas.getContext) return;
     5         var ctx = canvas.getContext("2d");
     6 
     7         ctx.fillStyle = "blue";
     8         ctx.fillRect(0, 0, 200, 200);
     9 
    10         ctx.globalCompositeOperation = "source-over"; //全局合成操作
    11         ctx.fillStyle = "red";
    12         ctx.fillRect(100, 100, 200, 200);
    13     }
    14     draw();
    15 
    16 </script>

    注:下面的展示中,蓝色是原有的,红色是新的。

    type `是下面 13 种字符串值之一:

    1. source-over(default)

    这是默认设置,新图像会覆盖在原有图像。

    2. source-in

    仅仅会出现新图像与原来图像重叠的部分,其他区域都变成透明的。(包括其他的老图像区域也会透明)

    3. source-out

    仅仅显示新图像与老图像没有重叠的部分,其余部分全部透明。(老图像也不显示)

    4. source-atop

    新图像仅仅显示与老图像重叠区域。老图像仍然可以显示。

    5. destination-over

    新图像会在老图像的下面

    6. destination-in

    仅仅新老图像重叠部分的老图像被显示,其他区域全部透明。

    7. destination-out

    仅仅老图像与新图像没有重叠的部分。 注意显示的是老图像的部分区域。

    8. destination-atop

    老图像仅仅仅仅显示重叠部分,新图像会显示在老图像的下面。

    9. lighter

    新老图像都显示,但是重叠区域的颜色做加处理

    10. darken

    保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的)

    blue: #0000ff

    red: #ff0000

    所以重叠部分的颜色:#000000

    11. lighten

    保证重叠部分最量的像素。(每个颜色位进行比较,得到最大的)

    blue: #0000ff

    red: #ff0000

    所以重叠部分的颜色:#ff00ff

    12. xor

    重叠部分会变成透明

    13. copy

    只有新图像会被保留,其余的全部被清除(边透明)

    三、裁剪路径

    clip()

    ​ 把已经创建的路径转换成裁剪路径。

    ​ 裁剪路径的作用是遮罩。只显示裁剪路径内的区域,裁剪路径外的区域会被隐藏。

    ​ 注意:clip()只能遮罩在这个方法调用之后绘制的图像,如果是clip()方法调用之前绘制的图像,则无法实现遮罩。

     1 var ctx;
     2 function draw(){
     3     var canvas = document.getElementById('tutorial1');
     4     if (!canvas.getContext) return;
     5     var ctx = canvas.getContext("2d");
     6 
     7     ctx.beginPath();
     8     ctx.arc(20,20, 100, 0, Math.PI * 2);
     9     ctx.clip();
    10 
    11     ctx.fillStyle = "pink";
    12     ctx.fillRect(20, 20, 100,100);
    13 }
    14 draw();

    四、动画

    动画的基本步骤

    1. 清空canvas

      再绘制每一帧动画之前,需要清空所有。清空所有最简单的做法就是clearRect()方法

    2. 保存canvas状态

      如果在绘制的过程中会更改canvas的状态(颜色、移动了坐标原点等),又在绘制每一帧时都是原始状态的话,则最好保存下canvas的状态

    3. 绘制动画图形

      这一步才是真正的绘制动画帧

    4. 恢复canvas状态

      如果你前面保存了canvas状态,则应该在绘制完成一帧之后恢复canvas状态。

    控制动画

    我们可用通过canvas的方法或者自定义的方法把图像会知道到canvas上。正常情况,我们能看到绘制的结果是在脚本执行结束之后。例如,我们不可能在一个 for 循环内部完成动画。

    也就是,为了执行动画,我们需要一些可以定时执行重绘的方法。

    一般用到下面三个方法:

    1. setInterval()
    2. setTimeout()
    3. requestAnimationFrame()

    案例1:太阳系

     1 let sun;
     2 let earth;
     3 let moon;
     4 let ctx;
     5 function init(){
     6     sun = new Image();
     7     earth = new Image();
     8     moon = new Image();
     9     sun.src = "sun.png";
    10     earth.src = "earth.png";
    11     moon.src = "moon.png";
    12 
    13     let canvas = document.querySelector("#solar");
    14     ctx = canvas.getContext("2d");
    15 
    16     sun.onload = function (){
    17         draw()
    18     }
    19 
    20 }
    21 init();
    22 function draw(){
    23     ctx.clearRect(0, 0, 300, 300); //清空所有的内容
    24     /*绘制 太阳*/
    25     ctx.drawImage(sun, 0, 0, 300, 300);
    26 
    27     ctx.save();
    28     ctx.translate(150, 150);
    29 
    30     //绘制earth轨道
    31     ctx.beginPath();
    32     ctx.strokeStyle = "rgba(255,255,0,0.5)";
    33     ctx.arc(0, 0, 100, 0, 2 * Math.PI)
    34     ctx.stroke()
    35 
    36     let time = new Date();
    37     //绘制地球
    38     ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds())
    39     ctx.translate(100, 0);
    40     ctx.drawImage(earth, -12, -12)
    41 
    42     //绘制月球轨道
    43     ctx.beginPath();
    44     ctx.strokeStyle = "rgba(255,255,255,.3)";
    45     ctx.arc(0, 0, 40, 0, 2 * Math.PI);
    46     ctx.stroke();
    47 
    48     //绘制月球
    49     ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds());
    50     ctx.translate(40, 0);
    51     ctx.drawImage(moon, -3.5, -3.5);
    52     ctx.restore();
    53 
    54     requestAnimationFrame(draw);
    55 }

    案例2:模拟时钟

      1 <!DOCTYPE html>
      2 <html lang="en">
      3 <head>
      4     <meta charset="UTF-8">
      5     <title>Title</title>
      6     <style>
      7         body {
      8             padding: 0;
      9             margin: 0;
     10             background-color: rgba(0, 0, 0, 0.1)
     11         }
     12 
     13         canvas {
     14             display: block;
     15             margin: 200px auto;
     16         }
     17     </style>
     18 </head>
     19 <body>
     20 <canvas id="solar" width="300" height="300"></canvas>
     21 <script>
     22     init();
     23 
     24     function init(){
     25         let canvas = document.querySelector("#solar");
     26         let ctx = canvas.getContext("2d");
     27         draw(ctx);
     28     }
     29 
     30     function draw(ctx){
     31         requestAnimationFrame(function step(){
     32             drawDial(ctx); //绘制表盘
     33             drawAllHands(ctx); //绘制时分秒针
     34             requestAnimationFrame(step);
     35         });
     36     }
     37     /*绘制时分秒针*/
     38     function drawAllHands(ctx){
     39         let time = new Date();
     40 
     41         let s = time.getSeconds();
     42         let m = time.getMinutes();
     43         let h = time.getHours();
     44 
     45         let pi = Math.PI;
     46         let secondAngle = pi / 180 * 6 * s;  //计算出来s针的弧度
     47         let minuteAngle = pi / 180 * 6 * m + secondAngle / 60;  //计算出来分针的弧度
     48         let hourAngle = pi / 180 * 30 * h + minuteAngle / 12;  //计算出来时针的弧度
     49 
     50         drawHand(hourAngle, 60, 6, "red", ctx);  //绘制时针
     51         drawHand(minuteAngle, 106, 4, "green", ctx);  //绘制分针
     52         drawHand(secondAngle, 129, 2, "blue", ctx);  //绘制秒针
     53     }
     54     /*绘制时针、或分针、或秒针
     55      * 参数1:要绘制的针的角度
     56      * 参数2:要绘制的针的长度
     57      * 参数3:要绘制的针的宽度
     58      * 参数4:要绘制的针的颜色
     59      * 参数4:ctx
     60      * */
     61     function drawHand(angle, len, width, color, ctx){
     62         ctx.save();
     63         ctx.translate(150, 150); //把坐标轴的远点平移到原来的中心
     64         ctx.rotate(-Math.PI / 2 + angle);  //旋转坐标轴。 x轴就是针的角度
     65         ctx.beginPath();
     66         ctx.moveTo(-4, 0);
     67         ctx.lineTo(len, 0);  // 沿着x轴绘制针
     68         ctx.lineWidth = width;
     69         ctx.strokeStyle = color;
     70         ctx.lineCap = "round";
     71         ctx.stroke();
     72         ctx.closePath();
     73         ctx.restore();
     74     }
     75 
     76     /*绘制表盘*/
     77     function drawDial(ctx){
     78         let pi = Math.PI;
     79 
     80         ctx.clearRect(0, 0, 300, 300); //清除所有内容
     81         ctx.save();
     82 
     83         ctx.translate(150, 150); //一定坐标原点到原来的中心
     84         ctx.beginPath();
     85         ctx.arc(0, 0, 148, 0, 2 * pi); //绘制圆周
     86         ctx.stroke();
     87         ctx.closePath();
     88 
     89         for (let i = 0; i < 60; i++){//绘制刻度。
     90             ctx.save();
     91             ctx.rotate(-pi / 2 + i * pi / 30);  //旋转坐标轴。坐标轴x的正方形从 向上开始算起
     92             ctx.beginPath();
     93             ctx.moveTo(110, 0);
     94             ctx.lineTo(140, 0);
     95             ctx.lineWidth = i % 5 ? 2 : 4;
     96             ctx.strokeStyle = i % 5 ? "blue" : "red";
     97             ctx.stroke();
     98             ctx.closePath();
     99             ctx.restore();
    100         }
    101         ctx.restore();
    102     }
    103 </script>
    104 </body>
    105 </html>

  • 相关阅读:
    安装chrome driver(14)
    爬虫-selenium实现验证码自动登录(14)
    爬虫-反爬与反反爬(12)
    爬虫-模拟登录(13)
    爬虫-GIL与线程同步问题(11)
    爬虫-多进程(10)
    爬取csdn的数据与解析存储(9)
    Exchange Server 2016邮件系统建设方案
    Exchange 2016高可用及容灾架构选型参考
    Installing Exchange 2016 on Windows Server 2016 Step by Step
  • 原文地址:https://www.cnblogs.com/le220/p/8342317.html
Copyright © 2020-2023  润新知