Canvas笔记

复习

初识canvas


  • <canvas>HTML5 新增的,一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。
  • 示例<canvas id="canvas1" width="500" height="500"></canvas>
    • <canvas> 看起来和 <img> 标签一样,只是 <canvas> 只有两个可选的属性 width、heigth 属性,而没有 src、alt 属性。
    • 如果不给 <canvas> 设置 widht、height 属性时,则默认 width为300、height 为 150,单位都是 px。也可以使用 css 属性来设置宽高,但是如宽高属性和初始比例不一致,他会出现扭曲。所以,建议永远不要使用 css 属性来设置 <canvas> 的宽高。
  • 替换内容
    • 支持 <canvas> 的浏览器会只渲染 <canvas> 标签,而忽略其中的替代内容。不支持 <canvas> 的浏览器则 会直接渲染替代内容。<canvas id="canvas1" width="500" height="500">您的破浏览器不支持canvas</canvas>
    • 替换的内容还可以使用img标签

基本使用


  • <canvas> 会创建一个固定大小的画布,会公开一个或多个渲染上下文(画笔),使用渲染上下文来绘制和处理要展示的内容。获取绘图上下文2Dvar pen1 = canvas1.getContext("2d");

绘制形状


栅格和坐标空间

  • canvas 元素默认被网格所覆盖。通常来说网格中的一个单元相当于 canvas 元素中的一像素。栅格的起点为左上角,坐标为 (0,0) (X向右Y向下增大)。所有元素的位置都相对于原点来定位。

绘制矩形

  • <canvas> 只支持一种原生的图形绘制:矩形。所有其他图形都至少需要生成一种路径 (path)。不过,我们拥有众多路径生成的方法让复杂图形的绘制成为了可能。

  • 三种绘制矩形的方法

    • 1、fillRect(x, y, width, height):绘制一个填充的矩形。
    • 2、strokeRect(x, y, width, height):绘制一个矩形的边框。
    • 3、clearRect(x, y, widh, height):清除指定的矩形区域,然后这块区域会变的完全透明。

    说明:这 3 个方法具有相同的参数。

    • x, y:指的是矩形的左上角的坐标。(相对于canvas的坐标原点)
    • width, height:指的是绘制的矩形的宽和高。

绘制路径

  • 路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。

  • 一个路径,甚至一个子路径,都是闭合的。

  • 绘制路径的步骤

    • 创建路径起始点
    • 调用绘制方法去绘制出路径
    • 把路径封闭
    • 一旦路径生成,通过描边或填充路径区域来渲染图形。
  • 绘制路径对应的方法

    • beginPath()

      新建一条路径,路径一旦创建成功,图形绘制命令被指向到路径上生成路径

    • moveTo(x, y)

      把画笔移动到指定的坐标(x, y)。相当于设置路径的起始点坐标。

    • lineTo(x, y)

      绘制一条从指定位置到当前位置的路径

    • closePath()

      闭合路径之后,图形绘制命令又重新指向到上下文中

    • stroke()

      通过线条来绘制图形轮廓

    • fill()

      通过填充路径的内容区域生成实心的图形

  • 示例

        <script>
            var canvas1 = document.getElementById('canvas1');
    
            var pen1 = canvas1.getContext("2d");
    
            pen1.beginPath();
            pen1.moveTo(200,200);
            pen1.lineTo(300,200);
            pen1.lineTo(300,400);
            pen1.closePath(); // 闭合路径
            pen1.stroke(); // 描边
        </script>
    
  • pen1.fill();会填充闭合区域,若该区域没有closePath则会自动闭合

绘制圆弧

  • arc(x, y, r, startAngle, endAngle, anticlockwise): 以(x, y) 为圆心,以r 为半径,从 startAngle 弧度开始到endAngle弧度结束。anticlosewise 是布尔值,true 表示逆时针,false 表示顺时针(默认是顺时针)。

    • 这里的度数都是弧度。
    • 0 弧度是指的 x 轴正方向。radians=(Math.PI/180)*degrees //角度转换成弧度
  • arcTo(x1, y1, x2, y2, radius): 根据给定的控制点和半径画一段圆弧,最后再以直线连接两个控制点。

    • arcTo 方法的说明:

      • 这个方法可以这样理解。绘制的弧形是由两条切线所决定。
      • 第 1 条切线:起始点和控制点1决定的直线。
      • 第 2 条切线:控制点1 和控制点2决定的直线。
      • 其实绘制的圆弧就是与这两条直线相切的圆弧。

绘制贝塞尔曲线

  • 二次贝塞尔曲线 quadraticCurveTo(cp1x, cp1y, x, y)

    • 参数 1 和 2:控制点坐标

    • 参数 3 和 4:结束点坐标

    • 示例

          <script>
              var canvas1 = document.getElementById('canvas1');
      
              var pen1 = canvas1.getContext("2d");
      
              pen1.beginPath();
              pen1.moveTo(100,100); //起始点
              var cp1x = 150, cp1y = 200; // 控制点
              var x = 500, y = 300; // 结束点
              pen1.quadraticCurveTo(cp1x, cp1y, x, y);
              pen1.stroke();
          </script>
      
  • 三次贝塞尔曲线 bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

    • 参数 1 和 2:控制点 1 的坐标

    • 参数 3 和 4:控制点 2 的坐标

    • 参数 5 和 6:结束点的坐标

    • 示例

          <script>
              var canvas1 = document.getElementById('canvas1');
      
              var pen1 = canvas1.getContext("2d");
      
              pen1.beginPath();
              pen1.moveTo(100,100); // 起始点
              var cp1x = 150, cp1y = 200; // 控制点1
              var cp2x = 250, cp2y = 220; // 控制点2
              var x = 400, y = 300; // 结束点
              pen1.quadraticCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
              pen1.stroke();
          </script>
      

添加样式和颜色


  • 如果想要给图形上色,有两个重要的属性可以做到。
    1. fillStyle = color 设置图形的填充颜色
    2. strokeStyle = color 设置图形轮廓的颜色
  • 备注:
    • color 可以是表示 css 颜色值的字符串、渐变对象或者图案对象。(rgb或者rgba都可以)
    • 默认情况下,线条和填充颜色都是黑色。
    • 一旦您设置了 strokeStyle 或者 fillStyle 的值,那么这个新值就会成为新绘制的图形的默认值。如果你要给每个图形上不同的颜色,你需要重新设置 fillStyle 或 strokeStyle 的值。

fillStyle

function draw(){
  var canvas = document.getElementById('tutorial');
  if (!canvas.getContext) return;
  var ctx = canvas.getContext("2d");
  for (var i = 0; i < 6; i++){
    for (var j = 0; j < 6; j++){
      ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ',' +
        Math.floor(255 - 42.5 * j) + ',0)';
      ctx.fillRect(j * 50, i * 50, 50, 50);
    }
  }
}
draw();

strokeStyle

function draw(){
    var canvas = document.getElementById('tutorial');
    if (!canvas.getContext) return;
    var ctx = canvas.getContext("2d");
    for (var i = 0; i < 6; i++){
        for (var j = 0; j < 6; j++){
            ctx.strokeStyle = `rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`;
            ctx.strokeRect(j * 50, i * 50, 40, 40);
        }
    }
}
draw();
/**
 返回随机的 [from, to] 之间的整数(包括from,也包括to)
 */
function randomInt(from, to){
    return parseInt(Math.random() * (to - from + 1) + from);
}

transparency

  • globalAlpha = transparencyValue: 这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。
  • globalAlpha 属性在需要绘制大量拥有相同透明度的图形时候相当高效。不过,我认为使用rgba()设置透明度更加好一些。

lineStyle

  • 线宽。只能是正值。默认是 1.0。
  • 起始点和终点的连线为中心,上下各占线宽的一半
  • pen1.lineWidth = 10;

lineCap

  • 线条末端样式。
  • 共有 3 个值:
    1. butt:线段末端以方形结束
    2. round:线段末端以圆形结束
    3. square:线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域
  • pen1.lineCap = 'round';

lineJoin

  • 同一个 path 内,设定线条与线条间接合处的样式。

  • 共有 3 个值 round, bevelmiter

    1. round 通过填充一个额外的,圆心在相连部分末端的扇形,绘制拐角的形状。 圆角的半径是线段的宽度。
    2. bevel 在相连部分的末端填充一个额外的以三角形为底的区域, 每个部分都有各自独立的矩形拐角。
    3. miter(默认) 通过延伸相连部分的外边缘,使其相交于一点,形成一个额外的菱形区域。
  • pen1.lineJoin = 'round';

虚线

  • setLineDash 方法和 lineDashOffset 属性来制定虚线样式。 setLineDash 方法接受一个数组,来指定线段与间隙的交替;lineDashOffset属性设置起始偏移量。

  • 示例

        <script>
            var canvas1 = document.getElementById('canvas1');
    
            var pen1 = canvas1.getContext("2d");
            pen1.strokeStyle = 'rgba(121,222,34,0.5)';
            pen1.lineWidth = 2;
            pen1.lineCap = 'round';
            pen1.lineJoin = 'round';
    
            pen1.beginPath();ji
            pen1.setLineDash([20,5]); // 数组内部的参数为实线长度,间隙长度
            pen1.lineDashOffset = -0;
            pen1.strokeRect(100,100,200,200);
        </script>
    
  • 备注: getLineDash() 返回一个包含当前虚线样式,长度为非负偶数的数组。

绘制文本


  • canvas 提供了两种方法来渲染文本:

    1. fillText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。
    2. strokeText(text, x, y [, maxWidth]) 在指定的 (x,y) 位置绘制文本边框,绘制的最大宽度是可选的。
  • 设置文本样式

    • font = value 当前我们用来绘制文本的样式。这个字符串使用和 CSS font 属性相同的语法。 默认的字体是 10px sans-serif
    • textAlign = value 文本对齐选项。 可选的值包括:start, end, left, right or center。 默认值是 start
    • textBaseline = value 基线对齐选项,可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic。
    • direction = value 文本方向。可能的值包括:ltr, rtl, inherit。默认值是 inherit

绘制图片


  • 示例

        <script>
            var canvas1 = document.getElementById('canvas1');
            var img = new Image();
            img.src = 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
    
            var pen1 = canvas1.getContext("2d");
    
            img.onload = function()
            {
                pen1.drawImage(img, 50,50);
            }
    
        </script>
    
  • 注意:考虑到图片是从网络加载,如果 drawImage 的时候图片还没有完全加载完成,则什么都不做,个别浏览器会抛异常。所以我们应该保证在 img 绘制完成之后再 drawImage

  • img 可以 new 也可以来源于我们页面的 <img>标签。

  • 图片的缩放

    • drawImage(image, x, y, width, height)
    • 这个方法多了 2 个参数:widthheight,这两个参数用来控制 当像 canvas 画入时应该缩放的大小。
  • 切片

  • drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

  • 第一个参数和其它的是相同的,都是一个图像或者另一个 canvas 的引用。

  • 其他 8 个参数:

    • 前 4 个是定义图像源的切片位置和大小,后 4 个则是定义切片的目标显示位置和大小。

状态的保存和恢复


  • Saving and restoring state 是绘制复杂图形时必不可少的操作。

  • save() 和 `restore()“

  • “saverestore方法是用来保存和恢复canvas` 状态的,都没有参数。

  • Canvas 的状态就是当前画面应用的所有样式和变形的一个快照。

  • 关于 save() :Canvas状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存。

  • 一个绘画状态包括:

    • 当前应用的变形(即移动,旋转和缩放)
    • strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值
    • 当前的裁切路径(clipping path),可以调用任意多次 save方法(类似数组的 push())。
  • 2、关于restore():每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复(类似数组的 pop())。

  • 示例

    var ctx;
    function draw(){
        var canvas = document.getElementById('tutorial');
        if (!canvas.getContext) return;
        var ctx = canvas.getContext("2d");
     
        ctx.fillRect(0, 0, 150, 150);   // 使用默认设置绘制一个矩形
        ctx.save();                  // 保存默认状态
     
        ctx.fillStyle = 'red'       // 在原有配置基础上对颜色做改变
        ctx.fillRect(15, 15, 120, 120); // 使用新的设置绘制一个矩形
     
        ctx.save();                  // 保存当前状态
        ctx.fillStyle = '#FFF'       // 再次改变颜色配置
        ctx.fillRect(30, 30, 90, 90);   // 使用新的配置绘制一个矩形
     
        ctx.restore();               // 重新加载之前的颜色状态
        ctx.fillRect(45, 45, 60, 60);   // 使用上一次的配置绘制一个矩形
     
        ctx.restore();               // 加载默认颜色配置
        ctx.fillRect(60, 60, 30, 30);   // 使用加载的配置绘制一个矩形
    }
    draw();
    

变形


tanslate

  • translate(x, y)用来移动 canvas原点到指定的位置(移动的是整个画布)

  • translate 方法接受两个参数。x 是左右偏移量,y 是上下偏移量

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

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

  • 示例

        <script>
            var canvas1 = document.getElementById('canvas1');
    
            var pen1 = canvas1.getContext('2d');
    
            pen1.save();
            pen1.translate(200,200);
            pen1.fillRect(0,0,100,100);
    
            pen1.restore();
            pen1.fillRect(0,0,100,100);
        </script>
    

rotate

  • 旋转坐标轴。rotate(angle)

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

  • 示例

            var pen1 = canvas1.getContext('2d');
    
            pen1.save();
            pen1.rotate((Math.PI/180)*45);;
            pen1.fillRect(100,100,50,50);
    
            pen1.restore();
            pen1.fillRect(100,100,50,50);
    

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 倍。

合成


  • 对合成的图形来说,绘制顺序会有限制。不过,我们可以利用 globalCompositeOperation 属性来改变这种状况。globalCompositeOperation = type
  • 对应的属性值有:
    • 默认设置,新图像会覆盖在原有图像。
    • source-in 仅仅会出现新图像与原来图像重叠的部分,其他区域都变成透明的。(包括其他的老图像区域也会透明)
    • source-out 仅仅显示新图像与老图像没有重叠的部分,其余部分全部透明。(老图像也不显示)
    • source-atop 新图像仅仅显示与老图像重叠区域。老图像仍然可以显示。
    • destination-over 新图像会在老图像的下面。
    • destination-in 仅仅新老图像重叠部分的老图像被显示,其他区域全部透明。
    • destination-out 仅仅老图像与新图像没有重叠的部分。 注意显示的是老图像的部分区域。
    • destination-atop 老图像仅仅仅仅显示重叠部分,新图像会显示在老图像的下面。
    • lighter 新老图像都显示,但是重叠区域的颜色做加处理。
    • darken 保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的)
    • lighten 保证重叠部分最量的像素。(每个颜色位进行比较,得到最大的)
    • xor 重叠部分会变成透明。
    • copy 只有新图像会被保留,其余的全部被清除(边透明)。

裁剪路径


  • clip()

  • 把已经创建的路径转换成裁剪路径。裁剪路径的作用是遮罩。只显示裁剪路径内的区域,裁剪路径外的区域会被隐藏。

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

  • 示例

            var canvas1 = document.getElementById('canvas1');
    
            var pen1 = canvas1.getContext('2d');
    
            pen1.beginPath();
            pen1.arc(100,100,100,0,Math.PI*2);
            pen1.clip();
    
            pen1.fillStyle = 'red';
            pen1.fillRect(100,100,200,200);
    

渐变


  • 线性渐变

    • 示例
            var canvas1 = document.getElementById('canvas1');
    
            var pen1 = canvas1.getContext('2d');
    
            var grd = pen1.createLinearGradient(0,0,100,100); // 参数:起始X,Y和结束X,Y
            grd.addColorStop(0,'blue'); // 设置渐变颜色
            grd.addColorStop(1,'green');
            pen1.fillStyle = grd;其实  	
            pen1.fillRect(0,0,100,100);
    
  • 径向渐变

    • 示例
            var canvas1 = document.getElementById('canvas1');
    
            var pen1 = canvas1.getContext('2d');
    
            var grd = pen1.createRadialGradient(100,100,50,100,100,100); // 参数:前三个参数为第一个渐变圆圆心XYR,后三个参数为第二个圆XYR
            grd.addColorStop(0,'blue');
            grd.addColorStop(1,'green');
            pen1.fillStyle = grd;
            pen1.fillRect(0,0,200,200);
    

阴影


shadowColor 设置或返回用于阴影的颜色
shadowBlur 设置或返回用于阴影的模糊级别
shadowOffsetX 设置或返回阴影距形状的水平距离
shadowOffsetY 设置或返回阴影距形状的垂直距离
  • 示例
        var canvas1 = document.getElementById('canvas1');

        var pen1 = canvas1.getContext('2d');

        pen1.shadowBlur = 30; 
        pen1.shadowColor = 'red'; 
        pen1.fillStyle = 'blue'; 
        pen1.shadowOffsetX = 10;
        pen1.shadowOffsetY = 10;
        pen1.fillRect(100,100,100,100);

像素的操作


  • 示例1

        <script>
            var canvas1 = document.getElementById('canvas1');
    
            var pen1 = canvas1.getContext('2d');
    
            pen1.save();
            pen1.fillStyle = 'gold';
            pen1.fillRect(0,0,100,100);
    
            // 读取指定矩形区域内的像素信息
            var imgData1 = pen1.getImageData(0,0,100,100);
            //imgData1属性:width height data,其中data数组中的每四个元素表示一个像素内的颜色信息,分别是rgba
    
            // 遍历修改每个像素的颜色值
            for(let i=0; i<imgData1.data.length; i+=4)
            {
                imgData1.data[i] += 10; 
                imgData1.data[i+1] += 20; 
                imgData1.data[i+2] += 30; 
                imgData1.data[i+4] += 40; 
            }
    
            // 将修改完成的像素信息写入到指定位置
            pen1.putImageData(imgData1,200,200);
        </script>
    
  • 示例2

        <script>
            var canvas1 = document.getElementById('canvas1');
    
            var pen1 = canvas1.getContext('2d');
    
            // 设置像素点尺寸
            var imgData2 = pen1.createImageData(100,100);;
            // 设置像素点颜色信息
            for(let i=0;i<imgData2.data.length;i+=4)
            {
                imgData2.data[i+0]=255;
                imgData2.data[i+1]=155;
                imgData2.data[i+2]=222;
                imgData2.data[i+3]=100;
            }
    
            pen1.putImageData(imgData2,0,0);
        </script>
    

版权声明:本文为pstar原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/pstar/p/13405610.html