loading

Canvas


Canvas

canvas 是HTML5新出的标签,可以用来做小游戏,特效,作图等,自己并没有作画能力,只能通过Javascript脚本来操控

Canvas标准

http://www.w3c.org/TR/2dcontext/
https://html.spec.whatwg.org/

创建Canvas

创建canvas几个主要的问题:

1.不能通过CSS设置画布的大小,否则会造成画布拉伸变形等问题,只能设置本身自带width、height属性,也可以在js里设置

2.兼容性:对一些不支持的浏览器,可以在标签内输入提示,不支持的浏览器会显示此提示、支持的浏览器会自动忽略掉

3.创建并设置好宽高后,通过js获取,还要设置其getContext,成功返回一个对象后即可作画,这里用js也可判断其是否支持canvas

语法格式:

<canvas width="1024" height="570" class="canvas">由于您的浏览器版本过低,此图片不能加载</canvas>
<script>
    var ctx = document.querySelector(".canvas").getContext("2d|3d");
</script>

手册参考网址

线

画布的x、y起点默认在右上角的位置(0,0), 分别对应的最大值是画布的宽和高

绘制线条的函数:

moveTo(x,y) : 开始位置

lineTo(x,y):结束位置

stroke :准备好后,开始画线条

设置线条样式的函数:

lineWidth:设置线条粗细

strokeStyle:设置线条的颜色

lineCap:设置线条首尾处的形状 俗称帽子

lineJoin:设置连接处的样式

miterLimit:内角与外角的距离。默认值是10,此属性只有在lineJoin = “miter”并且有设置线条粗细情况下才有效,且斜接长度大于miterLimit ,线条连接处自动斜切(lineJoin =”bevel”)

另起一条路径的函数:

beginPath:起始一条路径,或重置当前路径

closePath:创建从当前点回到起始点的路径

语法格式:

<canvas width="1024" height="760" class="canvas">由于您的浏览器版本过低,此图片不能加载</canvas><script>
   var ctx = document.querySelector(".canvas").getContext("2d|3d");
    ctx.beginPath();
    ctx.moveTo(100,100);
    ctx.lineTo(700,700);
    ctx.lineTo(100,700);
    ctx.lineTo(100,100);
    ctx.lineWidth = 5;
    ctx.strokeStyle = "green";
    ctx.stroke();
    ctx.closePath();

    ctx.beginPath();
    ctx.lineCap = "round" //butt(default) round圆头 square方头
    //lineCap 有时可以填补连接处的空缺
    ctx.moveTo(200,200);
    ctx.lineTo(400,400);
    ctx.lineTo(200,400);
    ctx.lineTo(200,200);
    ctx.lineWidth = 5;
    ctx.strokeStyle = "yellow";
    ctx.stroke();
    ctx.closePath();
</script>
===============================================================
ctx.lineWidth = 15;
DrawStart(ctx, 100, 20, 400, 400, -30);
      ctx.lineJoin = "miter"; //可以设置的样式有:miter(default)、 bevel斜接、 round圆角
      ctx.miterLimit = 7; //如果斜接长度超过 miterLimit 的值,边角会以 lineJoin 的 "bevel" 类型来显示。,这里等于7时,边角会以lineJoin的bevel显示,等于8时则会以miter显示
    //斜接长度指的是在两条线交汇处内角和外角之间的距离。


      ctx.stroke();
      function DrawStart(context, R, r, x, y, rotate) {
        //绘画对象 大圆的半径 小圆半径  x轴 y轴
        ctx.beginPath();
        for (var i = 0; i < 5; i++) {
          ctx.lineTo(
            Math.cos(((18 + 72 * i - rotate) / 180) * Math.PI) * R + x,
            -Math.sin(((18 + 72 * i - rotate) / 180) * Math.PI) * R + y
          );
          ctx.lineTo(
            Math.cos(((54 + 72 * i - rotate) / 180) * Math.PI) * r + x,
            -Math.sin(((54 + 72 * i - rotate) / 180) * Math.PI) * r + y
          );
        }
        ctx.closePath();
      }

填充

填充函数:

fill :填充当前路径 (注意:如果路径未关闭,那么 fill() 方法会从路径结束点到开始点之间添加一条线,以关闭该路径,然后填充该路径)

填充样式:

fillStyle:填充颜色

语法格式:

<canvas width="1024" height="760" class="canvas">由于您的浏览器版本过低,此图片不能加载</canvas><script>
   var ctx = document.querySelector(".canvas").getContext("2d|3d");
    ctx.beginPath();
    ctx.moveTo(100,100);
    ctx.lineTo(700,700);
    ctx.lineTo(100,700);
    ctx.lineTo(100,100);
    ctx.lineWidth = 5;
    ctx.strokeStyle = "green";
    ctx.stroke();
    ctx.fillStyle = "red";
    ctx.fill()
    ctx.closePath();

    ctx.beginPath();
    ctx.moveTo(200,200);
    ctx.lineTo(400,400);
    ctx.lineTo(200,400);
    ctx.lineTo(200,200);
    ctx.lineWidth = 5;
    ctx.strokeStyle = "yellow";
    ctx.stroke();
    ctx.closePath();
</script>

案例:四个正方形组成的大方形

   var ctx = document.querySelector(".canvas").getContext("2d");
let box = [
        {p:[{x:0,y:0},{x:512,y:0},{x:512,y:380},{x:0,y:380},{x:0,y:0}],color:"red"},
        {p:[{x:512,y:0},{x:1024,y:0},{x:1024,y:380},{x:512,y:380},{x:512,y:0}],color:"green"},
        {p:[{x:0,y:380},{x:512,y:380},{x:512,y:760},{x:0,y:760},{x:0,y:380}],color:"blue"},
        {p:[{x:512,y:380},{x:1024,y:380},{x:1024,y:760},{x:512,y:760},{x:512,y:380}],color:"yellow"},

      ]
      for(let i=0;i<box.length;i++)
      {
        draw(box[i],ctx);
      }

      function draw(p,c)
      {
        c.beginPath();
          c.moveTo(p.p[0].x,p.p[0].y);
          for(let i=1;i<p.p.length;i++)
          {
            c.lineTo(p.p[i].x,p.p[i].y);
          }
          c.lineWidth = 5;
          c.strokeStyle = p.color;
          c.fillStyle = p.color;
        c.stroke();
        c.fill();
        c.closePath();
      }

注意点:

  1. closePath可以解决闭合图形的空隙问题,也有自动闭合的作用。
  2. 当边框或者填充被遮挡掉的时候,这时可以先填充在画边框,或者先画边框再画线
  3. 后绘制的图形会顶替掉前面的图形

画圆的函数:

arc(x,y,r,start,end,true|false):画圆\弧

arcTo(x1,y1,x2,y2,r):绘制圆弧

语法格式:

 var ctx = document.querySelector(".canvas").getContext("2d");
      var i = 0;
      setInterval(() => {
        i++;
        ctx.fillStyle = "yellow";
        ctx.strokeStyle = "red";
        ctx.beginPath();                       
        ctx.arc(512, 380, 100, 0, Math.PI * 2 - (Math.PI * 2 * i / 30), true);
            //  x    y  半径 开始位置  结束位置                      是否逆时针旋转
        ctx.lineWidth = 10;
        ctx.stroke();
        ctx.fill();
      }, 1);
//顺时针:Math.PI * 2 * i / 30
//逆时针:Math.PI * 2 - (Math.PI * 2 * i / 30)

//使用arcTo绘圆角矩形
var ctx = document.querySelector("canvas").getContext("2d");
      // ctx.translate(200,200)
      // ctx.beginPath();
      // ctx.moveTo(100,0);
      // ctx.arcTo(400,0,400,800,100);
      // ctx.lineTo(400,400);
      // ctx.arcTo(400,500,350,500,100);
      // ctx.lineTo(100,500);
      // ctx.arcTo(0,500,0,450,100);
      // ctx.lineTo(0,100);
      // ctx.arcTo(0,0,100,0,100)
      // ctx.closePath();

      function RoundRect(ctx, width, height, r) 
      {
        ctx.beginPath();
        ctx.moveTo(r, 0);
        ctx.arcTo(width, 0, width, height - r, r);
        ctx.lineTo(width, height);
        ctx.arcTo(width, height + r, width - 50, height + r, r);
        ctx.lineTo(r, height + r);
        ctx.arcTo(0, height + r, 0, height - 50, r);
        ctx.lineTo(0, r);
        ctx.arcTo(0, 0, r, 0, r);
        ctx.closePath();
      }
      RoundRect(ctx, 200, 100, 10);
      ctx.stroke();

矩形

矩形函数:

reac:绘制矩形附带填充和边框

fillRect:绘制只填充的矩形

strokeRect:绘制只带边框的矩形

语法格式:

//  参数:  x , y , width , height  
var ctx = document.querySelector(".canvas").getContext("2d");
      ctx.fillStyle = "red";
      ctx.strokeStyle = "yellow";
      ctx.lineWidth = 5;
      var x = 0;
      ctx.beginPath();
      ctx.rect(100, 100, 300, 300);
      ctx.stroke();
      ctx.fill();

      ctx.beginPath();
      ctx.fillStyle = "rgba(0,255,155,0.5)";
      ctx.strokeStyle = "yellow";
      ctx.fillRect(150, 150, 300, 300);
      ctx.strokeRect(100, 100, 400, 400);

案例:绘制五角星

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <canvas width="800" height="800"></canvas>
    <script>
      var ctx = document.querySelector("canvas").getContext("2d");
      ctx.lineWidth = 10;
      ctx.beginPath();
      for (var i = 0; i <= 5; i++) {
        ctx.lineTo(
          Math.cos(((18 + 72 * i) / 180) * Math.PI) * 100 + 200,
                    //步骤:先求出角度在转弧度
                    //cos(18deg)*R   sin(18deg)*R
                    //角度转弧度公式:角度/180*r
                    //由于数学中的坐标系与canvas的坐标不同所以y轴要为负
          -Math.sin(((18 + 72 * i) / 180) * Math.PI) * 100 + 200
        );
        ctx.lineTo(
          Math.cos(((54 + 72 * i) / 180) * Math.PI) * 50 + 200,
          -Math.sin(((54 + 72 * i) / 180) * Math.PI) * 50 + 200
        );
      }
      ctx.closePath();
      ctx.stroke();
     =================================================================================
         //五角星封装函数
      DrawStart(ctx, 200, 100, 400, 400 , 30);
      ctx.stroke();
      function DrawStart(context, R, r, x, y,rotate) {
                        //绘画对象 大圆的半径 小圆半径  x轴 y轴  旋转角度
        ctx.beginPath();
        for (var i = 0; i < 5; i++) {
          ctx.lineTo(
            Math.cos(((18 + 72 * i -rotate) / 180) * Math.PI) * R + x,
            -Math.sin(((18 + 72 * i -rotate) / 180) * Math.PI) * R + y
          );
          ctx.lineTo(
            Math.cos(((54 + 72 * i -rotate) / 180) * Math.PI) * r + x,
            -Math.sin(((54 + 72 * i -rotate) / 180) * Math.PI) * r + y
          );
        }
        ctx.closePath();
      }
    </script>
  </body>
</html>

参考链接:https://www.baidu.com/link?url=3j7UmP9XbkLh0ww6tMOChL_7CATbIoQ15GBatuaJdx2_yCn-jcdshTRF9-l6kMmbONf5cgLEviZEpV4QhjLf2q&wd=&eqid=e9fcd7b0000436fd000000065d2e7488

随机不重复、不切边五角星

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <canvas width="800" height="800" class="canvas"></canvas>
    <script src="../node/jquery.js"></script>
    <script>
      var ctx = document.querySelector("canvas").getContext("2d");
      ctx.fillStyle = "black";
      ctx.fillRect(
        0,
        0,
        document.querySelector("canvas").width,
        document.querySelector("canvas").height
      );

      ctx.fillStyle = "#fc1";
      ctx.strokeStyle = "#fb5";
      ctx.lineWidth = 5;
      ctx.lineJoin = "round";
      var ele = [];
      start(ele);
      end(ele);

      function start(arr) {
        for (var i = 0; i < 200; i++) {
          var flag = true,
            ran = Math.random() * 10 + 10,
            x =
              Math.random() *
                (document.querySelector("canvas").width - ran * 2) +
              ran,
            y =
              Math.random() *
                (document.querySelector("canvas").height - ran * 2) +
              ran,
            rotate = Math.random() * 360;
          for (var j = 0; j < ele.length; j++) {
            var oldx = Math.pow(x - arr[j][1], 2);
            var oldy = Math.pow(y - arr[j][2], 2);
            var oldr = Math.pow(ran + arr[j][0], 2);
            if (oldx + oldy < oldr) {
              flag = false;
            }
          }

          flag ? arr.push([ran, x, y, rotate]) : i--;
        }
      }
      function end(arr) {
        for (var i = 0; i < arr.length; i++) {
          DrawStart(
            ctx,
            arr[i][0],
            arr[i][0] / 2,
            arr[i][1],
            arr[i][2],
            arr[i][3]
          );
          ctx.fill();
          ctx.stroke();
        }
      }
      function DrawStart(context, R, r, x, y, rotate) {
        //绘画对象 大圆的半径 小圆半径  x轴 y轴
        ctx.beginPath();
        for (var i = 0; i < 5; i++) {
          ctx.lineTo(
            Math.cos(((18 + 72 * i - rotate) / 180) * Math.PI) * R + x,
            -Math.sin(((18 + 72 * i - rotate) / 180) * Math.PI) * R + y
          );
          ctx.lineTo(
            Math.cos(((54 + 72 * i - rotate) / 180) * Math.PI) * r + x,
            -Math.sin(((54 + 72 * i - rotate) / 180) * Math.PI) * r + y
          );
        }
        ctx.closePath();
      }
    </script>
  </body>
</html>

绘画状态保存与还原

状态的保存与还原可以使操作更加简便,快速,主要作用于将绘画的状态来回快速切换,状态保存,可以保存如:线条的样式、颜色、填充、图形变换等,特别是配合图形变换,两者可以互补长短

状态保存和还原的对应函数:

save:保存当前的状态,以便restore还原

restore:还原到之前sava保存的状态

语法格式:

//这里配合图形变换来演示
//图形变换系列的函数,重复使用是叠加效果,而不是替换,所以使用图形变换+状态保存还原可以更快速简便

//正常方式
  var ctx = document.querySelector(".canvas").getContext("2d");
      ctx.beginPath();
      ctx.fillStyle = "red";
      ctx.translate(100, 100);
      ctx.fillRect(0, 0, 400, 400);
      ctx.translate(-100, -100);
      ctx.fillStyle = "red";
      ctx.fillRect(400, 400, 400, 400);
      ctx.closePath();
      //图形变换函数连续使用会进行叠加效果,会影响到下面绘制的图形,为了不影响,必须再次使用变换函数来还原

//这里运用了canvas的状态保存还原后,可以更方便使用变换函数
var ctx = document.querySelector(".canvas").getContext("2d");
      ctx.beginPath();
      ctx.fillStyle = "red";
      ctx.save();
      ctx.translate(100, 100);
      ctx.fillRect(0, 0, 400, 400);
      ctx.restore();
      ctx.fillRect(400, 400, 400, 400);
      ctx.closePath();

图形变换

图形变换系列函数:

translate(x,y):偏移、移动

rotate(?deg):旋转

scale(sx,sy):缩放

变换矩阵:

transform(a,b,c,d,e,f):图形变换矩阵函数是一个把所有变换效果结于一身的函数,连续使用会造成链集和叠加效果

setTransform(a,b,c,d,e,f):可以重置transform函数,使之前的失效

语法格式:

//改用图形变换函数 translate rotate scale 后的无重复五角星
//scale 会有许多副作用如:边框将会变大、长宽比也好增大、x、y也会随之增大
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <canvas width="800" height="800" class="canvas"></canvas>
    <script src="../node/jquery.js"></script>
    <script>
      var ctx = document.querySelector("canvas").getContext("2d");
      ctx.fillStyle = "black";
      ctx.fillRect(
        0,
        0,
        document.querySelector("canvas").width,
        document.querySelector("canvas").height
      );

      ctx.fillStyle = "#fc1";
      ctx.strokeStyle = "#fb5";
      ctx.lineWidth = 5;
      ctx.lineJoin = "round";
      var ele = [];
      start(ele);
      end(ele);

      function start(arr) {
        for (var i = 0; i < 200; i++) {
          var flag = true,
            ran = Math.random() * 10 + 10,
            x =
              Math.random() *
                (document.querySelector("canvas").width - ran * 2) +
              ran,
            y =
              Math.random() *
                (document.querySelector("canvas").height - ran * 2) +
              ran,
            rotate = Math.random() * 360;
          for (var j = 0; j < ele.length; j++) {
            var oldx = Math.pow(x - arr[j][1], 2);
            var oldy = Math.pow(y - arr[j][2], 2);
            var oldr = Math.pow(ran + arr[j][0], 2);
            if (oldx + oldy < oldr) {
              flag = false;
            }
          }

          flag ? arr.push([ran, x, y, rotate]) : i--;
        }
      }
      function end(arr) {
        for (var i = 0; i < arr.length; i++) {
          ctx.save();
          ctx.beginPath();
          ctx.translate(arr[i][1], arr[i][2]);
          ctx.rotate(arr[i][3]);
          ctx.scale(0.5,0.5)
          DrawStart(ctx, arr[i][0]);
          ctx.fill();
          ctx.stroke();
          ctx.closePath();
          ctx.restore();
        }
      }
      function DrawStart(context, r) {
        //绘画对象 大圆的半径 小圆半径  x轴 y轴
        for (var i = 0; i <= 5; i++) {
          ctx.lineTo(
            Math.cos(((18 + 72 * i) / 180) * Math.PI) * r,
            -Math.sin(((18 + 72 * i) / 180) * Math.PI) * r
          );
          ctx.lineTo(
            (Math.cos(((54 + 72 * i) / 180) * Math.PI) * r) / 2,
            (-Math.sin(((54 + 72 * i) / 180) * Math.PI) * r) / 2
          );
        }
      }
    </script>
  </body>
</html>


//transform
 var ctx = document.querySelector(".canvas").getContext("2d");
      ctx.beginPath();
      ctx.fillStyle = "red";
      ctx.save();
      ctx.transform(1.5,0,0,1.5,200,100);
                  a:水平缩放 b:水平倾斜 c:垂直倾斜 d:垂直缩放 e:水平位移 f:垂直位移
      ctx.fillRect(0, 0, 400, 400);
      ctx.restore();
      ctx.fillRect(400, 400, 400, 400);
      ctx.closePath();

渐变

渐变系列函数:

createLinearGradient(x,y,EndX,EndY):创建渐变

addColorStop(0-1,color) :添加颜色

createRadialGradient(x1,y1,r1,x2,y2,r2):创建一个径向的渐变,整个径向渐变发生在俩个圆中间

语法格式:


var ctx = document.querySelector("canvas").getContext("2d");
      document.querySelector("canvas").width = document.body.clientWidth;
      document.querySelector("canvas").height = document.body.clientHeight;
      //创建渐变对象
      var grd = ctx.createLinearGradient(
        0,   //x
        0,    //y
        0,    //endX
        document.querySelector("canvas").height //endY
        //这里的渐变方向是从上往下
      );


      //下面的是径向渐变,在定义的两个圆之间产生渐变
      //第一个圆是外圆,内圆则是第二个
      //var grd =             ctx.createRadialGradient(document.querySelector("canvas").width/2,document.querySelector("canvas").height/2,400,document.querySelector("canvas").width/2,document.querySelector("canvas").height/2,0)


      //添加颜色,取值范围(0,1),必须有起点,终点
      grd.addColorStop(0, "black");
      grd.addColorStop(1, "#035");
      //最后把渐变对象添加到要应用的样式填充上
      ctx.fillStyle = grd;
      ctx.fillRect(
        0,
        0,
        document.querySelector("canvas").width,
        document.querySelector("canvas").height
      );

三角函数讲解

绘制圆形

1角度 = 1* Math.PI/ 180弧度

1弧度 = 1* 180 / Math.PI 角度

arc(x,y,r,0,360,false)

x,y 圆心坐标位置

r 圆半径

0,360

从0度到360度所对应的弧度 (弧度: 角度值*Math.PI/180) true/false 逆时针/顺时针绘图

添加图片、视频、canvas

相关函数:

createPattern(ele,repeat|repeat-x|repeat-y|no-repeat):添加图片、视频、画布到指定的场景

语法格式:

//添加图片
 var ctx = document.querySelector("canvas").getContext("2d");
      var img = new Image();
      img.src = "../newIMg.png";
      img.onload = () => {
        var createImg = ctx.createPattern(img, "repeat");
        ctx.fillStyle = createImg;
        ctx.fillRect(0, 0, 800, 800);
      };
 //添加图片时注意:必须图片加载完后在载入createPattern,否则载入失败

 //添加画布
 function createImgCanvas() {
        var c = document.createElement("canvas");
        var ctx = c.getContext("2d");

        c.width = 400;
        c.height = 400;
        ctx.fillStyle = "yellow";
        ctx.transform(1, 0, 0, 1, c.width / 2, c.height / 2);
        DrawStart(ctx, 200);
        ctx.fill();

        return c;
      }
      var ctx = document.querySelector("canvas").getContext("2d");

      document.querySelector("canvas").width = 1400;
      document.querySelector("canvas").height = 1400;
      ctx.fillStyle = ctx.createPattern(createImgCanvas(), "repeat");
      ctx.fillRect(0, 0, 1400, 1400);

案例:绘制圆角矩形

      /*
          把圆分为4份,顺时针顺序绘制 
          Math.PI / 2 半圆分1为2
          Math.PI 一个圆中的一半
          Math.PI*3/2 || Math.PI*1.5  1个半圆加一个1分为2的半圆
          Math.PI*2 完整的圆 
      */

var ctx = document.querySelector("canvas").getContext("2d");
      ctx.beginPath();
      ctx.arc(400 - 50, 400 - 50, 50, 0, Math.PI / 2);
      ctx.lineTo(50, 400);
      ctx.arc(50, 400 - 50, 50, Math.PI / 2, Math.PI);
      ctx.lineTo(0, 50);
      ctx.arc(50, 50, 50, Math.PI, (Math.PI * 3) / 2);
      ctx.lineTo(400 - 50, 0);
      ctx.arc(400 - 50, 50, 50, (Math.PI * 3) / 2, Math.PI * 2);
      ctx.closePath();
      ctx.stroke();

  //封装函数
  function PathRoundRect(ctx, width, height, r) 
      {
        ctx.beginPath();
        ctx.arc(width - r, height - r, r, 0, Math.PI / 2);
        ctx.lineTo(r, height);
        ctx.arc(r, height - r, r, Math.PI / 2, Math.PI);
        ctx.lineTo(0, r);
        ctx.arc(r, r, r, Math.PI, Math.PI * 1.5);
        ctx.lineTo(width - r, 0);
        ctx.arc(width - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
        ctx.closePath();
      }
  //升级版
  function PathRoundRect(ctx, width, height, r,optionColor,optionStyle)
      {
        if(r*2>width||r*2>height)return;
        if(optionStyle)
        {
        optionStyle.stroke === true ? (optionColor.stroke ? (ctx.strokeStyle =optionColor.stroke ):null) : null;
        optionStyle.fill === true ?(optionColor.fill ? (ctx.fillStyle =optionColor.fill):null) : null;
        }
        ctx.beginPath();
        ctx.arc(width - r, height - r, r, 0, Math.PI / 2);
        ctx.lineTo(r, height);
        ctx.arc(r, height - r, r, Math.PI / 2, Math.PI);
        ctx.lineTo(0, r);
        ctx.arc(r, r, r, Math.PI, Math.PI * 1.5);
        ctx.lineTo(width - r, 0);
        ctx.arc(width - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
        ctx.closePath();
      }
   //最终版
    function RoundRect(ctx, width, height, r, optionColor, optionStyle) {
        var flagfill,flagstroke;
        if (r*2 > width || r*2 > height) return;
        if (optionStyle) {
          optionStyle.stroke === true
            ? optionColor.stroke
              ? (ctx.strokeStyle = optionColor.stroke)
              : null
            : null;
          optionStyle.fill === true
            ? optionColor.fill
              ? (ctx.fillStyle = optionColor.fill)
              : null
            : null;
          flagfill = optionColor.fill ?true:false;
          flagstroke = optionColor.stroke ?true:false;
        }
        ctx.beginPath();
        ctx.arc(width - r, height - r, r, 0, Math.PI / 2);
        ctx.lineTo(r, height);
        ctx.arc(r, height - r, r, Math.PI / 2, Math.PI);
        ctx.lineTo(0, r);
        ctx.arc(r, r, r, Math.PI, Math.PI * 1.5);
        ctx.lineTo(width - r, 0);
        ctx.arc(width - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
        ctx.closePath();

        flagstroke ? ctx.stroke() : null;
        flagfill ? ctx.fill() : null;
      }

案例:2048九宫格

 var ctx = document.querySelector("canvas").getContext("2d");
      ctx.translate(800/4,800/4)
      RoundRect(
        ctx,
        480,
        470,
        10,
        { fill: "red", stroke: "green" },
        { fill: true, stroke: true }
      );
      for (var i = 0; i < 4; i++) {
        ctx.save();
        ctx.translate(10, i * 15);
        for (var j = 0; j < 4; j++) {
          ctx.save();
          ctx.translate(j * 120, i * 100 +15);
          RoundRect(
            ctx,
            100,
            100,
            10,
            { fill: "green", stroke: "green" },
            { fill: true, stroke: true }
          );
          ctx.restore();
        }
        ctx.restore();
      }

      function RoundRect(ctx, width, height, r, optionColor, optionStyle) {
        var flagfill, flagstroke;
        if (r > width || r > height) return;
        if (optionStyle) {
          optionStyle.stroke === true
            ? optionColor.stroke
              ? (ctx.strokeStyle = optionColor.stroke)
              : null
            : null;
          optionStyle.fill === true
            ? optionColor.fill
              ? (ctx.fillStyle = optionColor.fill)
              : null
            : null;
          flagfill = optionColor.fill ? true : false;
          flagstroke = optionColor.stroke ? true : false;
        }
        ctx.beginPath();
        ctx.arc(width - r, height - r, r, 0, Math.PI / 2);
        ctx.lineTo(r, height);
        ctx.arc(r, height - r, r, Math.PI / 2, Math.PI);
        ctx.lineTo(0, r);
        ctx.arc(r, r, r, Math.PI, Math.PI * 1.5);
        ctx.lineTo(width - r, 0);
        ctx.arc(width - r, r, r, (Math.PI * 3) / 2, Math.PI * 2);
        ctx.closePath();

        flagstroke ? ctx.stroke() : null;
        flagfill ? ctx.fill() : null;
      }

案例:绘制弯月

 pathMoon(ctx, 2, 300, 300, 200);
      function pathMoon(ctx, d, x, y, r, deg, color) {
        ctx.save();
        ctx.translate(x, y);
        ctx.scale(r, r);
        ctx.rotate(deg || 0);
        ctx.beginPath();
        Moon(d, ctx);
        ctx.fillStyle = color ? color : "yellow";
        ctx.fill();
        ctx.closePath();
        ctx.restore();
        function Moon(d, ctx) {
          ctx.arc(0, 0, 1, Math.PI * 0.5, Math.PI * 1.5, true);
          ctx.moveTo(0, -1);
          ctx.arcTo(d, 0, 0, 1, dis(0, -1, d, 0) / d);
        }
        function dis(x1, y1, x2, y2) {
          return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }
      }

贝塞尔曲线

贝塞尔曲线相关函数:

quadraticCurveTo(x1,y1,x2,y2):二次贝塞尔曲线 一个控制点 1

bezierCurveTo(x1,y1,x2,y2,x3,y3):三次贝塞尔曲线 两个控制点 1 2

//起始点不由函数来定,可以用moveTo等画路径的函数来定

//二次贝塞尔曲线
        ctx.moveTo(0, 0);
          ctx.quadraticCurveTo(800,400,400,800)
          ctx.stroke()

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      html,
      body {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas"></canvas>
    <script src="../node/jquery.js"></script>
    <script>


      var ctx = document.querySelector("canvas").getContext("2d");

      document.querySelector("canvas").width = document.body.clientWidth;
      document.querySelector("canvas").height = document.body.clientHeight;
        ctx.save();
        var grd = ctx.createLinearGradient(0,0,0,document.querySelector("canvas").height);
        grd.addColorStop(0,"black");
        grd.addColorStop(1,"#035");
        ctx.fillStyle = grd;
        ctx.fillRect(0,0,document.querySelector("canvas").width,document.querySelector("canvas").height);
        ctx.restore();
      ctx.fillStyle = "#fc1";
      ctx.strokeStyle = "#fb5";
      ctx.lineWidth = 5;
      ctx.lineJoin = "round";
      var ele = [];
      start(ele,200);
      end(ele);
      pathMoon(ctx, 2, document.querySelector("canvas").width*0.85, document.querySelector("canvas").height/4,80,0,"#fc1");
      RectLoda(ctx);





      function RectLoda(ctx)
      {
        ctx.save();
        ctx.beginPath();
        ctx.moveTo(0,document.querySelector("canvas").height*0.65);

//三次贝塞尔曲线应用       ctx.bezierCurveTo(document.querySelector("canvas").width/2,650,document.querySelector("canvas").width/2,document.querySelector("canvas").height/3,document.querySelector("canvas").width,document.querySelector("canvas").height-200);
        ctx.lineTo(document.querySelector("canvas").width,document.querySelector("canvas").height)
        ctx.lineTo(0,document.querySelector("canvas").height)


       var bgcolor = ctx.createLinearGradient(0,document.querySelector("canvas").height,0,0);
        bgcolor.addColorStop(0,"#030");
        bgcolor.addColorStop(1,"#580");
        ctx.fillStyle = bgcolor;
        ctx.fill();
        ctx.closePath();
        ctx.restore();
      }
      function pathMoon(ctx, d, x, y, r, deg, color) {
        ctx.save();
        ctx.translate(x, y);
        ctx.scale(r, r);
        ctx.rotate(deg || 0);
        ctx.beginPath();
        Moon(d, ctx);
        ctx.fillStyle = color ? color : "yellow";
        ctx.fill();
        ctx.closePath();
        ctx.restore();
        function Moon(d, ctx) {
          ctx.arc(0, 0, 1, Math.PI * 0.5, Math.PI * 1.5, true);
          ctx.moveTo(0, -1);
          ctx.arcTo(d, 0, 0, 1, dis(0, -1, d, 0) / d);
        }
        function dis(x1, y1, x2, y2) {
          return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }
      }

      function start(arr,num) {
        for (var i = 0; i < num; i++) {
          var flag = true,
            ran = Math.random() * 10 + 5,
            x =
              Math.random() *
                (document.querySelector("canvas").width - ran * 2) +
              ran,
            y =
              (Math.random() *
                (document.querySelector("canvas").height - ran * 2) +
                ran) *
              0.65,
            rotate = Math.random() * 360;
          for (var j = 0; j < ele.length; j++) {
            var oldx = Math.pow(x - arr[j][1], 2);
            var oldy = Math.pow(y - arr[j][2], 2);
            var oldr = Math.pow(ran + arr[j][0], 2);
            if (oldx + oldy < oldr) {
              flag = false;
            }
            if(x>document.querySelector("canvas").width*0.85&&x<document.querySelector("canvas").width*0.85+80*1.15)
            {
              flag = false;

            }

          }

          flag ? arr.push([ran, x, y, rotate]) : i--;
        }
      }
      function end(arr) {
        for (var i = 0; i < arr.length; i++) {
          ctx.save();
          ctx.beginPath();
          ctx.translate(arr[i][1], arr[i][2]);
          ctx.rotate(arr[i][3]);
          ctx.scale(0.7, 0.7);
          DrawStart(ctx, arr[i][0]);
          ctx.fill();
          ctx.stroke();
          ctx.closePath();
          ctx.restore();
        }
      }

      function DrawStart(ctx, r) {
        //绘画对象 大圆的半径 小圆半径  x轴 y轴
        for (var i = 0; i <= 5; i++) {
          ctx.lineTo(
            Math.cos(((18 + 72 * i) / 180) * Math.PI) * r,
            -Math.sin(((18 + 72 * i) / 180) * Math.PI) * r
          );
          ctx.lineTo(
            (Math.cos(((54 + 72 * i) / 180) * Math.PI) * r) / 2,
            (-Math.sin(((54 + 72 * i) / 180) * Math.PI) * r) / 2
          );
        }
      }
    </script>
  </body>
</html>

字体

字体绘制相关函数:

font:设置字体样式属性 五个参数依次是font-style(italic oblique)、font-variant(small-caps)、font-weight(bold lighter bolder)、font-size、font-family

fillText(string,x,y,length):绘制带填充字体

strokeText(string,x,y,length):绘制带边框的字体

textAlign:设置字体水平对齐 参数有:right center left

textBaseline:设置字体垂直对齐 参数有:top middle bottom alphabetic(defalut) ideographic hanging

measureText(String).width:获取文本宽度(会根据font设置的字体、字号来决定)

语法格式:

 var ctx = document.querySelector(".canvas").getContext("2d");
    var grd = ctx.createLinearGradient(0,0,800,0);
    grd.addColorStop(0,"red");
    grd.addColorStop(0.5,"yellow");
    grd.addColorStop(1,"green");
    ctx.fillStyle = grd;
    ctx.font = "bold 40px 微软雅黑 ";
    ctx.fillText("How are you!",100,100,300);
    //字体可以填充渐变色、图形纹理、另一个画布

  ===================================================================
  //只带边框的字体
   var ctx = document.querySelector(".canvas").getContext("2d");
    var grd = ctx.createLinearGradient(0,0,800,0);
    grd.addColorStop(0,"red");
    grd.addColorStop(0.5,"yellow");
    grd.addColorStop(1,"green");
    ctx.lineWidth =2;
    ctx.strokeStyle = grd;
    ctx.font = "bold 40px 微软雅黑 ";
    ctx.strokeText("How are you!",100,100,300);
    ===============================================
   //带纹理的字体
    var ctx = document.querySelector(".canvas").getContext("2d");
      // var grd = ctx.createLinearGradient(0,0,800,0);
      // grd.addColorStop(0,"red");
      // grd.addColorStop(0.5,"yellow");
      // grd.addColorStop(1,"green");

      var img = new Image();
      img.src = "../newIMg.png";
      img.onload = function() {
        var pattern = ctx.createPattern(img, "repeat");
        ctx.fillStyle = pattern;
        ctx.strokeStyle = "red";
        ctx.lineWidth =1;
        ctx.font = "bold 40px 微软雅黑 ";
        ctx.strokeText("How are you!", 100, 100, 300);
        ctx.fillText("How are you!", 100, 100, 300);
      };

水平对齐效果:

垂直对齐效果:

案例:水平垂直居中

 var ctx = document.querySelector(".canvas").getContext("2d");
      ctx.fillStyle = "red";
      ctx.strokeStyle = "red";
      ctx.moveTo(400, 0);
      ctx.lineTo(400, 800);
      ctx.moveTo(0, 400);
      ctx.lineTo(800, 400);
      ctx.stroke();

      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      ctx.font = "bold 100px 微软雅黑 ";
      ctx.strokeText("How are you!", 400, 400);

阴影

阴影相关属性:

shadowColor:设置阴影颜色

shadowOffsetX:设置阴影X偏移

shadowOffsetY:设置阴影Y偏移

shadowBlur:设置阴影模糊扩散程度

语法格式:

  var ctx = document.querySelector("canvas").getContext("2d");
      ctx.shadowColor = "black";
     ctx.fillStyle = "red"
      var num = 0;
      var flag = true;
      setInterval(function() {
        ctx.shadowOffsetX = -20+num;
      ctx.shadowOffsetY = -20+num;
        num == 100 ? (flag = false) : num == 0 ? (flag = true) : null;
        flag ? num++ : num--;
        ctx.shadowBlur = num;
        ctx.clearRect(0, 0, 800, 800);
        ctx.fillRect(100, 100, 400, 400);
      }, 50);

透明

透明相关函数:

在canvas中要实现透明,可以使用rgba颜色,也可以使用以下方法

globalAlpha:设置透明度 范围(1-0) 全局透明

语法格式:

  var ctx = document.querySelector("canvas").getContext("2d");
      var color = ["red","green","yellow","blue","black"]
     for(var i=0;i<200;i++)
     {
       var randomX = Math.random()*800;
       var randomY = Math.random()*800;
       var randomR = Math.random()*30;
      var R = Math.floor(Math.random()*255),G = Math.floor(Math.random()*255),B = Math.floor(Math.random()*255);
       ctx.save();
       ctx.beginPath();
       ctx.globalAlpha = 0.7;
       ctx.fillStyle = `rgb(${R},${G},${B})`;
       ctx.arc(randomX,randomY,randomR,0,Math.PI*2);
       ctx.fill();
       ctx.closePath();
       ctx.restore();
     }

遮盖顺序

xx.globalCompositeOperation = “source-over” (默认,后绘制的图形会压在先绘制的图形上) / “destination-over”(先绘制的图形压在后绘制的图形上)

相关属性:

globalCompositeOperation :改变元素显示效果与遮盖顺序

各个参数的实现效果可以参考菜鸟,或有canvas手册的网址,或以下链接

https://blog.csdn.net/fe_dev/article/details/81985367

此链接,有水滴扩散、刮刮卡案例参考

参数:

语法格式:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      html,
      body {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas" width="800" height="800"></canvas>
    <script src="../node/jquery.js"></script>
    <script>
      var ctx = document.querySelector("canvas").getContext("2d");
      var color = ["red","green","yellow","blue","black"]
      var Balls = [];
     for(var i=0;i<200;i++)
     {
      var randomR = Math.random()*30+10;
       var randomX = Math.random()*(800-randomR*2)+randomR;
       var randomY = Math.random()*(800-randomR*2)+randomR;
      var R = Math.floor(Math.random()*255),G = Math.floor(Math.random()*255),B = Math.floor(Math.random()*255);

        let balls ={
          x:randomX,
          y:randomY,
          r:randomR,
          color:`rgb(${R},${G},${B})`,
          vx:(Math.random()*5+5)*(Math.pow(-1,Math.floor(Math.random()*100))),
          vy:(Math.random()*5+5)*(Math.pow(-1,Math.floor(Math.random()*100))),
        }
      Balls.push(balls);
     }

     setInterval(()=>{
       draw(ctx)
     },50)

     function draw(ctx)
     {
       ctx.clearRect(0,0,800,800)
       for(let i=0;i<Balls.length;i++)
       {
       ctx.save();
       ctx.beginPath();
      ctx.globalCompositeOperation = "xor";
       ctx.fillStyle = Balls[i].color;
       ctx.arc(Balls[i].x,Balls[i].y,Balls[i].r,0,Math.PI*2);
       ctx.fill();
       ctx.closePath();
       ctx.restore();
       Balls[i].x+=Balls[i].vx;
       Balls[i].y+=Balls[i].vy;
       if(Balls[i].x-Balls[i].r<=0)
       {
         Balls[i].vx = -Balls[i].vx;
          Balls[i].x = Balls[i].r;
       }
       if(Balls[i].x+Balls[i].r >= 800)
       {
         Balls[i].vx = -Balls[i].vx;
         Balls[i].x = 800-Balls[i].r;
       }
       if(Balls[i].y-Balls[i].r<=0)
       {
         Balls[i].vy = -Balls[i].vy;
         Balls[i].y = Balls[i].r;
       }
       if(Balls[i].y+Balls[i].r>=800)
       {
         Balls[i].vy = -Balls[i].vy;
         Balls[i].y = 800-Balls[i].r;
       }
       }
     }

    </script>
  </body>
</html>

剪切

相关函数:

clip:相对于上个填充路径做剪切效果

语法格式:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      html,
      body {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas" width="800" height="800"></canvas>
    <script src="../node/jquery.js"></script>
    <script>
      var ctx = document.querySelector("canvas").getContext("2d");
      var r = 150;
      ctx.fillStyle = "black";
      ctx.fillRect(0, 0, 800, 800);
      ctx.canvas.onmousemove = e => {
        ctx.clearRect(0, 0, 800, 800);
        ctx.beginPath();
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, 800, 800);
        ctx.closePath();

        ctx.save();
        ctx.beginPath();
        ctx.fillStyle = "rgba(255,255,255,1)";
        var x = e.pageX - r <= 0 ? r : e.pageX;
        var y = e.pageY - r <= 0 ? r : e.pageY;
        x = e.pageX + r > 800 ? 800 - r : x;
        y = e.pageY + r > 800 ? 800 - r : y;
        ctx.arc(x, y, r, 0, Math.PI * 2);
        ctx.fill();
        ctx.clip();
        ctx.closePath();

        ctx.beginPath();
        ctx.fillStyle = "red";
        ctx.font = "200px bold 微软雅黑";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText("Canvas", 400, 400);
        ctx.closePath();
        ctx.restore();
        document.onkeyup = ev => {
          ev.keyCode === 38 ? (r += 20) : null;
          ev.keyCode === 40 ? (r -= 20) : null;
          if (r >= 390) r = 390;
          ctx.canvas.onmousemove(e);
          return false;
        };
      };
      ctx.canvas.onmouseout = () => {
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, 800, 800);
        document.onkeyup = null;
      };
    </script>
  </body>
</html>

实现剪纸效果

剪纸效果参考非零环绕原侧,自动识别里、外、面,一个面不同时出现顺时针和逆时针就填充,如果同时出现顺时针和逆时针就不填充,这也就说明了图形的绘制方向,会影响其是否填充

语法格式:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      html,
      body {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas" width="800" height="800"></canvas>
    <script src="../node/jquery.js"></script>
    <script>
      var ctx = document.querySelector("canvas").getContext("2d");
      ctx.beginPath();
      ctx.rect(200, 200, 400, 200);
      drawRect(ctx, 220, 220, 100, 100);
      ctx.arc(400, 270, 50, Math.PI * 2, 0, true);
      Triangle(ctx, 510, 220, 460, 320, 560, 320);
      ctx.closePath();
      ctx.fillStyle = "red";
      ctx.shadowColor = "black";
      ctx.shadowOffsetX = 10;
      ctx.shadowOffsetY = 10;
      ctx.fill();
      function drawRect(ctx, x, y, w, h) {
        ctx.moveTo(x, y);
        ctx.lineTo(x, y + h);
        ctx.lineTo(x + w, y + h);
        ctx.lineTo(x + w, y);
        ctx.lineTo(x, y);
      }
      function Triangle(ctx, x1, y1, x2, y2, x3, y3) {
        ctx.moveTo(x1, y1);
        ctx.lineTo(x2, y2);
        ctx.lineTo(x3, y3);
        ctx.lineTo(x1, y1);
      }
    </script>
  </body>
</html>

交互

canvas交互相关函数:

isPointInPath(x,y):检测指定的坐标是否在绘制元素内,只能判断最后一个绘制的封闭路径

语法格式:


//点击填充颜色
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      html,
      body {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas" width="800" height="800"></canvas>
    <script src="../node/jquery.js"></script>
    <script>
      var ctx = document.querySelector("canvas").getContext("2d");
      var Balls = [];
      for (var i = 0; i < 10; i++) {
        ctx.beginPath();
        Balls.push({
          x: Math.random() * 800,
          y: Math.random() * 800,
          r: Math.random() * 100
        });
      }
      Draw(ctx);
      function Draw(ctx) {
        for (let i = 0; i < Balls.length; i++) {
          ctx.beginPath();
          ctx.arc(Balls[i].x, Balls[i].y, Balls[i].r, 0, Math.PI * 2);
          ctx.stroke();
        }

        ctx.canvas.addEventListener("click", function(event) {
          for (let i = 0; i < Balls.length; i++) {
            ctx.beginPath();
            ctx.arc(Balls[i].x, Balls[i].y, Balls[i].r, 0, Math.PI * 2);
            if (ctx.isPointInPath(event.pageX, event.pageY)) {
              ctx.fillStyle = "yellow";
              ctx.fill();
            }
          }
        });
      }
    </script>
  </body>
</html>


//鼠标滑入填充颜色,滑出清空颜色


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      html,
      body {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas" width="800" height="800"></canvas>
    <script src="../node/jquery.js"></script>
    <script>
      var ctx = document.querySelector("canvas").getContext("2d");
      var Balls = [];
      for (var i = 0; i < 10; i++) {
        ctx.beginPath();
        Balls.push({
          x: Math.random() * 800,
          y: Math.random() * 800,
          r: Math.random() * 100
        });
      }
      Draw(ctx);
      function Draw(ctx) {
        for (let i = 0; i < Balls.length; i++) {
          ctx.beginPath();
          ctx.arc(Balls[i].x, Balls[i].y, Balls[i].r, 0, Math.PI * 2);
          ctx.stroke();
        }

        ctx.canvas.addEventListener("mousemove", function(event) {
          ctx.clearRect(0, 0, 800, 800);
          for (let i = 0; i < Balls.length; i++) {
            ctx.beginPath();
            ctx.arc(Balls[i].x, Balls[i].y, Balls[i].r, 0, Math.PI * 2);
            if (ctx.isPointInPath(event.pageX, event.pageY)) {
              ctx.fillStyle =
                "rgb(" +
                Math.random() * 255 +
                "," +
                Math.random() * 255 +
                "," +
                Math.random() * 255 +
                ")";
              ctx.fill();
            } else ctx.stroke();
          }
        });
      }
    </script>
  </body>
</html>

案例:小球滚动(面向对象+控制面板)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      html,
      body {
        width: 100%;
        height: 100%;
      }
      .canvasBox {
        width: 800px;
        height: 800px;
        position: relative;
      }
      .canvasBox > .SetupPanel {
        width: 300px;
        height: 200px;
        background: #99b4e487;
        border-radius: 30px;
        position: absolute;
        left: 0;
        top: 0;
        color: white;
      }
      .canvasBox > .SetupPanel > h1 {
        text-align: center;
        line-height: 25px;
      }
      .canvasBox > .SetupPanel > a {
        float: left;
        color: #478aff87;
        background: #e5eeff;
        border-radius: 5px;
        padding: 10px 0;
        margin: 3px 0;
        text-decoration: none;
        text-align: center;
        line-height: 15px;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div class="canvasBox">
      <canvas class="canvas" width="800" height="800"></canvas>
      <div class="SetupPanel">
        <h1>控制面板</h1>
        <a href="javascript:void(0)">点击停止</a>
        <a href="javascript:void(0)">点击切换背景颜色</a>
        <a href="javascript:void(0)">点击切换覆盖模式</a>
      </div>
    </div>
    <script src="../node/jquery.js"></script>
    <script>
      class Animate {
        constructor(canvas) {
          this.canvas = document.querySelector(canvas);
          this.ctx = this.canvas.getContext("2d");
          this.initAttr();
          this.initDraw();
          this.initEvent();
        }
        initAttr() {
          this.color = ["red", "green", "yellow", "blue", "black"];
          this.bgcolor = null;
          this.Balls = [];
          this.flag = true;
          this.module = ["xor", "lighter", null];
          this.option = null;
          this.time = null;
        }
        initEvent() {
          document.querySelectorAll(
            ".canvasBox>.SetupPanel>a"
          )[0].onclick = () => {
            if (this.flag) {
              clearInterval(this.time);
              this.flag = false;
              document.querySelectorAll(
                ".canvasBox>.SetupPanel>a"
              )[0].innerHTML = "点击开始";
              document.querySelectorAll(
                ".canvasBox>.SetupPanel>a"
              )[0].style.background = "red";
            } else {
              this.time = setInterval(() => {
                this.draw(this.bgcolor);
              }, 50);
              this.flag = true;
              document.querySelectorAll(
                ".canvasBox>.SetupPanel>a"
              )[0].innerHTML = "点击停止";
              document.querySelectorAll(
                ".canvasBox>.SetupPanel>a"
              )[0].style.background = "";
            }
          };
          document.querySelectorAll(
            ".canvasBox>.SetupPanel>a"
          )[1].onclick = () => {
            this.bgcolor = this.color[
              parseInt(Math.random() * this.color.length - 1)
            ];
          };
          document.querySelectorAll(
            ".canvasBox>.SetupPanel>a"
          )[2].onclick = () => {
            this.option = this.module[
              Math.ceil(Math.random() * this.module.length - 1)
            ];
          };
        }
        initDraw() {
          for (let i = 0; i < 200; i++) {
            let randomR = Math.random() * 30 + 10;
            let randomX = Math.random() * (800 - randomR * 2) + randomR;
            let randomY = Math.random() * (800 - randomR * 2) + randomR;
            let R = Math.floor(Math.random() * 255),
              G = Math.floor(Math.random() * 255),
              B = Math.floor(Math.random() * 255);

            let balls = {
              x: randomX,
              y: randomY,
              r: randomR,
              color: `rgb(${R},${G},${B})`,
              vx:
                (Math.random() * 5 + 5) *
                Math.pow(-1, Math.floor(Math.random() * 100)),
              vy:
                (Math.random() * 5 + 5) *
                Math.pow(-1, Math.floor(Math.random() * 100))
            };
            this.Balls.push(balls);
          }
        }
        draw(bgcolor) {
          this.ctx.clearRect(0, 0, 800, 800);

          this.bgDraw(bgcolor);
          for (let i = 0; i < this.Balls.length; i++) {
            this.ctx.save();
            this.ctx.beginPath();
            this.ctx.globalCompositeOperation = this.option;
            this.ctx.fillStyle = this.Balls[i].color;
            this.ctx.arc(
              this.Balls[i].x,
              this.Balls[i].y,
              this.Balls[i].r,
              0,
              Math.PI * 2
            );
            this.ctx.fill();
            this.ctx.closePath();
            this.ctx.restore();
            this.Balls[i].x += this.Balls[i].vx;
            this.Balls[i].y += this.Balls[i].vy;
            if (this.Balls[i].x - this.Balls[i].r <= 0) {
              this.Balls[i].vx = -this.Balls[i].vx;
              this.Balls[i].x = this.Balls[i].r;
            }
            if (this.Balls[i].x + this.Balls[i].r >= 800) {
              this.Balls[i].vx = -this.Balls[i].vx;
              this.Balls[i].x = 800 - this.Balls[i].r;
            }
            if (this.Balls[i].y - this.Balls[i].r <= 0) {
              this.Balls[i].vy = -this.Balls[i].vy;
              this.Balls[i].y = this.Balls[i].r;
            }
            if (this.Balls[i].y + this.Balls[i].r >= 800) {
              this.Balls[i].vy = -this.Balls[i].vy;
              this.Balls[i].y = 800 - this.Balls[i].r;
            }
          }
        }
        bgDraw(bgcolor) {
          this.ctx.beginPath();
          this.ctx.fillStyle = bgcolor;
          this.ctx.fillRect(0, 0, 800, 800);
          this.ctx.closePath();
        }
      }

      //初始化对象
      let animate = new Animate("canvas");
      var ctx = animate.ctx;
      //开始执行动画
      animate.time = setInterval(() => {
        animate.draw(animate.bgcolor);
      }, 50);
    </script>
  </body>
</html>

清除

清除也称重绘,在动画应用当中是不可分割的,动画是一帧一帧的播放,中间少不了清除的步骤,否则将达不到动画的播放帧数标准

清除画布相关函数:

clearRect(startX,startY,endX,endY):清除参数指定范围内绘制的元素

语法格式:

ctx.clearRect(0,0,canvas.width,canvas.height);

扩充Canvas 2d方法

在有些时候,我们自定义的函数,不能像canvas自带的函数一样,不用传入绘制上下午对象,而我们自定义的必须把绘制对象传入函数里才可以使用,那么有没有方法可以解决呢,在canvas实例对象的原型上添加方法即可

语法格式:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      html,
      body {
        width: 100%;
        height: 100%;
      }
      .canvasBox {
        width: 800px;
        height: 800px;
        position: relative;
      }
      .canvasBox > .SetupPanel {
        width: 300px;
        height: 200px;
        background: #99b4e487;
        border-radius: 30px;
        position: absolute;
        left: 0;
        top: 0;
        color: white;
      }
      .canvasBox > .SetupPanel > h1 {
        text-align: center;
        line-height: 25px;
      }
      .canvasBox > .SetupPanel > a {
        float: left;
        color: #478aff87;
        background: #e5eeff;
        border-radius: 5px;
        padding: 10px 0;
        margin: 3px 0;
        text-decoration: none;
        text-align: center;
        line-height: 15px;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div class="canvasBox">
      <canvas class="canvas" width="800" height="800"></canvas>
    </div>
    <script src="../node/jquery.js"></script>
    <script>
      let ctx = document.querySelector(".canvas").getContext("2d");
      ctx.__proto__.fillStar = function(r)  

      //ctx.__proto__ === CanvasRenderingContext2D.prototype

      {
          this.beginPath();
        for(let i=0;i<=5;i++)
        {
          this.lineTo(
            Math.cos((18+i*72)/180*Math.PI)*r+this.lastMoveTo.x,
            -Math.sin((18+i*72)/180*Math.PI)*r+this.lastMoveTo.y
          );
          this.lineTo(
            Math.cos((54+i*72)/180*Math.PI)*r/2+this.lastMoveTo.x,
            -Math.sin((54+i*72)/180*Math.PI)*r/2+this.lastMoveTo.y
          );
        }
          this.closePath();
          this.fill();
      }
      ctx.lastMoveTo = {};
      CanvasRenderingContext2D.prototype.oldMoveTo = CanvasRenderingContext2D.prototype.moveTo;
      CanvasRenderingContext2D.prototype.moveTo = function(x,y)
      {
        this.oldMoveTo(x,y);
        this.lastMoveTo.x = x;
        this.lastMoveTo.y = y;
      }
      ctx.moveTo(400,400)
      ctx.fillStar(100);
    </script>
  </body>
</html>

图像处理

图像不同于图形,图像是位图是由无数个彩色像素点组成,图形是点线面结合而成,两者的处理方式也不相同,所干涉的区域也不相同

图形处理相关函数:

drawImage(img,sx,sy,sw,sh,dx,dy,dw,dh) :导入指定图片到canvas当中。 九个参数 首个是图片资源,s开头的是原图像的参数,d开头的是指在canvas绘制的参数

语法格式:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <canvas
      class="canvas"
      width="800"
      height="800"
      style="border:1px solid blue"
    ></canvas>
    <script>
      var ctx = document.querySelector(".canvas").getContext("2d");
      var img = new Image();
      img.src = "../PHP操作MySQL.png";
      img.onload = () => {
        ctx.drawImage(img, 740, 450, 260, 70, 0, 0, 800, 800);
      };
    </script>
  </body>
</html>

案例:图像放大

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body,
      html {
        background: black;
      }

input[type=range] {
    -webkit-appearance: none;
    width: 300px;
    border-radius: 10px; /*这个属性设置使填充进度条时的图形为圆角*/
    margin-top: 10px;
}
input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
} 

input[type=range]::-webkit-slider-runnable-track {
    height: 10px;
    border-radius: 10px; /*将轨道设为圆角的*/
    box-shadow: 0 1px 1px #def3f8, inset 0 .125em .125em #0d1112; /*轨道内置阴影效果*/
}
input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none;
    height: 25px;
    width: 25px;
    margin-top: -5px; /*使滑块超出轨道部分的偏移量相等*/
    background: #ffffff; 
    border-radius: 50%; /*外观设置为圆形*/
    border: solid 0.125em rgba(205, 224, 230, 0.5); /*设置边框*/
    box-shadow: 0 .125em .125em #3b4547; /*添加底部阴影*/
}

    </style>
  </head>
  <body>
    <canvas
      class="canvas"
      style="border:1px solid blue;margin: 0 auto;display: block"
    ></canvas>
    <input
      type="range"
      style="display:block;width:100%;"
      max="3.0"
      min="0.5"
      step="0.1"
      value="1"
      class="scale"
    />
    <script>
      var ctx = document.querySelector(".canvas").getContext("2d");
      var img = new Image();
      var scale = document.querySelector(".scale");
      img.src = "../1.jpg";
      img.onload = () => {
        ctx.canvas.width = img.width;
        ctx.canvas.height = img.height;
        drawImageByScale(ctx, scale.value, img.width, img.height);

        // scale.onchange = function() {
        //   ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        //   drawImageByScale(ctx, scale.value, img.width, img.height);
        // };
        scale.onmousemove = function() {
          ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
          drawImageByScale(ctx, scale.value, img.width, img.height);
        };
      };

      function drawImageByScale(ctx, scale, w, h) {
        var imageWidth = w * scale;
        var imageHeight = h * scale;

        let x = ctx.canvas.width / 2 - imageWidth / 2,
          y = ctx.canvas.height / 2 - imageHeight / 2;
        ctx.drawImage(img, x, y, imageWidth, imageHeight);
      }
    </script>
  </body>
</html>
//水印版
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body,
      html {
        background: black;
      }
      .bottom {
        width: 100%;
        position: fixed;
        bottom: 30px;
      }
      input[type="range"] {
        -webkit-appearance: none;
        width: 300px;
        border-radius: 10px; /*这个属性设置使填充进度条时的图形为圆角*/
        margin-top: 10px;
        position: relative;
      }
      input[type="range"]::-webkit-slider-thumb {
        -webkit-appearance: none;
      }

      input[type="range"]::-webkit-slider-runnable-track {
        height: 10px;
        border-radius: 10px; /*将轨道设为圆角的*/
        box-shadow: 0 1px 1px #def3f8, inset 0 0.125em 0.125em #0d1112; /*轨道内置阴影效果*/
      }
      input[type="range"]::-webkit-slider-thumb {
        -webkit-appearance: none;
        height: 25px;
        width: 25px;
        margin-top: -5px; /*使滑块超出轨道部分的偏移量相等*/
        background: #ffffff;
        border-radius: 50%; /*外观设置为圆形*/
        border: solid 0.125em rgba(205, 224, 230, 0.5); /*设置边框*/
        box-shadow: 0 0.125em 0.125em #3b4547; /*添加底部阴影*/
      }
    </style>
  </head>
  <body>
    <canvas
      class="canvas"
      style="border:1px solid blue;margin: 0 auto;display: block"
    ></canvas>
    <div class="bottom">
      <input
        type="range"
        style="display:block;width:100%;"
        max="3.0"
        min="0.5"
        step="0.1"
        value="1"
        class="scale"
      />
    </div>
    <script>
      var ctx = document.querySelector(".canvas").getContext("2d");
      var img = new Image();
      var scale = document.querySelector(".scale");
      img.src = "./img.jpg";
      var shuiying = document.createElement("canvas").getContext("2d");
      shuiying.canvas.width = 400;
      shuiying.canvas.height = 100;
      shuiying.font = "bold 50px 微软雅黑";
      shuiying.fillStyle = "white";
      shuiying.textAlign = "center";
      shuiying.textBaseline = "middle";
      shuiying.fillText("xuyuxin", 200, 50, 400);
      img.onload = () => {
        ctx.canvas.width = img.width;
        ctx.canvas.height = img.height;
        drawImageByScale(ctx, scale.value, img.width, img.height,shuiying);

        // scale.onchange = function() {
        //   ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        //   drawImageByScale(ctx, scale.value, img.width, img.height);
        // };
        scale.onmousemove = function() {
          ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

          drawImageByScale(ctx, scale.value, img.width, img.height, shuiying);
        };
      };

      function drawImageByScale(ctx, scale, w, h, c) {
        var imageWidth = w * scale;
        var imageHeight = h * scale;

        let x = ctx.canvas.width / 2 - imageWidth / 2,
          y = ctx.canvas.height / 2 - imageHeight / 2;

        ctx.drawImage(img, x, y, imageWidth, imageHeight);
        if(scale>=1)
        {
        ctxdrawImage(c.canvas, -40, ctx.canvas.height - 100);
        }
        else
        {
        ctx.drawImage(c.canvas, x, imageHeight-50);
        }
      }
    </script>
  </body>
</html>

案例:放大镜

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body,
      html {
        background: black;
      }
      .bottom {
        width: 100%;
        position: fixed;
        bottom: 30px;
      }
      input[type="range"] {
        -webkit-appearance: none;
        width: 300px;
        border-radius: 10px; /*这个属性设置使填充进度条时的图形为圆角*/
        margin-top: 10px;
        position: relative;
      }
      input[type="range"]::-webkit-slider-thumb {
        -webkit-appearance: none;
      }

      input[type="range"]::-webkit-slider-runnable-track {
        height: 10px;
        border-radius: 10px; /*将轨道设为圆角的*/
        box-shadow: 0 1px 1px #def3f8, inset 0 0.125em 0.125em #0d1112; /*轨道内置阴影效果*/
      }
      input[type="range"]::-webkit-slider-thumb {
        -webkit-appearance: none;
        height: 25px;
        width: 25px;
        margin-top: -5px; /*使滑块超出轨道部分的偏移量相等*/
        background: #ffffff;
        border-radius: 50%; /*外观设置为圆形*/
        border: solid 0.125em rgba(205, 224, 230, 0.5); /*设置边框*/
        box-shadow: 0 0.125em 0.125em #3b4547; /*添加底部阴影*/
      }
    </style>
  </head>
  <body>
    <canvas
      class="canvas"
      style="border:1px solid blue;margin: 0 auto;display: block"
    ></canvas>

    <script>
      var ctx = document.querySelector(".canvas").getContext("2d");
      var offsetCanvas = document.createElement("canvas").getContext("2d");
      var img = new Image();
      var scale;
      img.src = "./img-lg.jpg";

      img.onload = () => {
        ctx.canvas.width = 1152;
        ctx.canvas.height = 768;

        offsetCanvas.canvas.width = img.width;
        offsetCanvas.canvas.height = img.height;

        scale = offsetCanvas.canvas.width / ctx.canvas.width;
        ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
        offsetCanvas.drawImage(img, 0, 0);
      };

      ctx.canvas.onmousedown = function(e) {
        var x = e.clientX - this.getBoundingClientRect().left,
            y = e.clientY - this.getBoundingClientRect().top;
          Draw(ctx, true, x, y);

        this.onmousemove = e => {
          var x = e.clientX - this.getBoundingClientRect().left,
            y = e.clientY - this.getBoundingClientRect().top;
          Draw(ctx, true, x, y);
        };
        this.onmouseup = () => {
          ctx.canvas.onmousemove = null;
          Draw(ctx, false);
        };
        this.onmouseout = () => {
          ctx.canvas.onmousemove = null;
          Draw(ctx, false);
        };
      };
      function Draw(ctx, flag, x, y) {
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
        if (flag) {
          drawImageRect(ctx, x, y);
        }
      }

      function drawImageRect(ctx, x, y) {
        var imageLgX = x * scale,
          imageLgY = y * scale;

        var r = 200;

        var sx = imageLgX - r,
          sy = imageLgY - r;

        var dx = x - r,
          dy = y - r;

        ctx.save();
        ctx.beginPath();
        ctx.arc(x,y,r,0,Math.PI*2);
        ctx.stroke();
        ctx.clip()
        ctx.drawImage(offsetCanvas.canvas, sx, sy, r * 2, r * 2, dx, dy, r * 2, r * 2);
        ctx.closePath();
        ctx.restore();
      }
    </script>
  </body>
</html>

像素处理

相关函数:

getImageData(x,y,w,h):获取指定图像像素数据 返回一个对象,里面有 data像素数据

putImageData(img,dx,dy,dirtx,dirty,dirtw,dirth):输出处理后的图像到指定位置

createImageData(w,h):创建一个空的图像

像素处理算法:

语法格式:

//像素自动变紫
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body,
      html {
        /* background: black; */
        height: 2000px;
      }
    </style>
  </head>
  <body>
    <canvas
      class="canvas"
      style="border:1px solid blue;float:left"
      width="500"
      height="500"
    ></canvas>
    <canvas
      class="canvas"
      style="border:1px solid blue;float:right"
      width="500"
      height="500"
    ></canvas>
    <div class="btnbox" style="display: block;">
      <a href="javascript:void(0)" onclick="fillter()">fillter</a>
    </div>
    <script>
      var ctx1 = document.querySelectorAll(".canvas")[0].getContext("2d");
      var ctx2 = document.querySelectorAll(".canvas")[1].getContext("2d");
var n =0;
      var imageData;
      var data;
      var img = new Image();
      img.src = "./img.jpg";
      img.onload = function() {
        ctx1.drawImage(img, 0, 0, 500, 500);
      };
      function fillter() {
        imageData = ctx1.getImageData(0, 0, 500, 500);
        data = imageData.data;

        for (let i = 0; i < ctx2.canvas.width * ctx2.canvas.height; i++) {
          data[4 * i + 0] += n;
          // data[4 * i + 1] += n;
          data[4 * i + 2] += n;
          // data[4 * i + 3] = Math.ceil(Math.random()*255);
        }
        ctx2.putImageData(imageData, 0, 0, 0, 0, 500, 500);
      }
      setInterval(function(){
        n++;
        if(n===255)
        {
          n=Math.ceil(Math.random()*255)
        }
        fillter(n);
      },1);
    </script>
  </body>
</html>

案例:像素滤镜

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body,
      html {
        /* background: black; */
        height: 2000px;
      }
      .btnbox > a {
        float: left;
        margin: 10px 20px;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas" style="float:left" width="500" height="500"></canvas>
    <canvas
      class="canvas"
      style="float:right"
      width="500"
      height="500"
    ></canvas>
    <div class="btnbox" style="display: block;">
      <a href="javascript:void(0)" onclick="grey()">灰度 Grey Effect</a>
      <a href="javascript:void(0)" onclick="black()">黑白 black&white Effect</a>
      <a href="javascript:void(0)" onclick="revers()">反色 revers Effect</a>
      <a href="javascript:void(0)" onclick="Blur()">模糊 blur Effect</a>
      <a href="javascript:void(0)" onclick="mosaice()">马赛克 mosaice Effect</a>
    </div>
    <script>
      var ctx1 = document.querySelectorAll(".canvas")[0].getContext("2d");
      var ctx2 = document.querySelectorAll(".canvas")[1].getContext("2d");
      var imageData;
      var data;
      var img = new Image();
      img.src = "../1.jpg";
      img.onload = function() {
        ctx1.drawImage(img, 0, 0, 500, 500);
      };
      function mosaice() {
        var imgData = ctx1.getImageData(0, 0, 500, 500);
        var data = imgData.data;
        var tmpimgData = ctx1.getImageData(0, 0, 500, 500);
        var tmpData = tmpimgData.data;
        var size = 16,
          totalnum = Math.pow(size, 2);
        for (var i = 0; i < ctx2.canvas.height; i += size) {
          for (var j = 0; j < ctx2.canvas.width; j += size) {
            var totalr = 0,
              totalg = 0,
              totalb = 0;
            for (var dx = 0; dx < size; dx++) {
              for (var dy = 0; dy < size; dy++) {
                let x = i + dx,
                  y = j + dy;
                let p = x * ctx2.canvas.width + y;
                totalr += tmpData[p * 4 + 0];
                totalg += tmpData[p * 4 + 1];
                totalb += tmpData[p * 4 + 2];
              }
            }
            var p = i * ctx2.canvas.width + j;
            var resr = totalr / totalnum;
            var resg = totalg / totalnum;
            var resb = totalb / totalnum;
            for (var dx = 0; dx < size; dx++) {
              for (var dy = 0; dy < size; dy++) {
                var x = dx + i,
                  y = dy + j;
                var p = x * ctx2.canvas.width + y;
                data[4 * p + 0] = resr;
                data[4 * p + 1] = resg;
                data[4 * p + 2] = resb;
              }
            }
          }
        }
        ctx2.putImageData(imgData, 0, 0, 0, 0, 500, 500);
      }
      function revers() {
        imageData = ctx1.getImageData(0, 0, 500, 500);
        data = imageData.data;

        for (let i = 0; i < ctx2.canvas.width * ctx2.canvas.height; i++) {
          let r = 255 - data[4 * i + 0],
            g = 255 - data[4 * i + 1],
            b = 255 - data[4 * i + 2];
          data[4 * i + 0] = r;
          data[4 * i + 1] = g;
          data[4 * i + 2] = b;
        }
        ctx2.putImageData(imageData, 0, 0, 0, 0, 500, 500);
      }
      function Blur() {
        var imgData = ctx1.getImageData(0, 0, 500, 500);
        var data = imgData.data;
        var tmpimgData = ctx1.getImageData(0, 0, 500, 500);
        var tmpData = tmpimgData.data;
        var blurR = 2,
          totalnum = Math.pow(2 * blurR + 1, 2);
        for (var i = blurR; i < ctx2.canvas.height - blurR; i++) {
          for (var j = blurR; j < ctx2.canvas.width - blurR; j++) {
            var totalr = 0,
              totalg = 0,
              totalb = 0;
            for (var dx = -blurR; dx <= blurR; dx++) {
              for (var dy = -blurR; dy <= blurR; dy++) {
                let x = i + dx,
                  y = j + dy;
                let p = x * ctx2.canvas.width + y;
                totalr += tmpData[p * 4 + 0];
                totalg += tmpData[p * 4 + 1];
                totalb += tmpData[p * 4 + 2];
              }
            }
            var p = i * ctx2.canvas.width + j;
            data[p * 4 + 0] = totalr / totalnum;
            data[p * 4 + 1] = totalg / totalnum;
            data[p * 4 + 2] = totalb / totalnum;
          }
        }
        ctx2.putImageData(imgData, 0, 0, 0, 0, 500, 500);
      }
      function grey() {
        imageData = ctx1.getImageData(0, 0, 500, 500);
        data = imageData.data;

        for (let i = 0; i < ctx2.canvas.width * ctx2.canvas.height; i++) {
          let r = data[4 * i + 0],
            g = data[4 * i + 1],
            b = data[4 * i + 2];
          let grey = r * 0.3 + g * 0.59 + b * 0.11;
          data[4 * i + 0] = grey;
          data[4 * i + 1] = grey;
          data[4 * i + 2] = grey;
        }
        ctx2.putImageData(imageData, 0, 0, 0, 0, 500, 500);
      }
      function black() {
        imageData = ctx1.getImageData(0, 0, 500, 500);
        data = imageData.data;

        for (let i = 0; i < ctx2.canvas.width * ctx2.canvas.height; i++) {
          let r = data[4 * i + 0],
            g = data[4 * i + 1],
            b = data[4 * i + 2];
          let grey = r * 0.3 + g * 0.59 + b * 0.11;
          if (grey > 255 / 2) {
            v = 255;
          } else {
            v = 0;
          }
          data[4 * i + 0] = v;
          data[4 * i + 1] = v;
          data[4 * i + 2] = v;
        }
        ctx2.putImageData(imageData, 0, 0, 0, 0, 500, 500);
      }
    </script>
  </body>
</html>

案例:颜色板

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body,
      html {
        /* background: black; */
        height: 2000px;
      }
      .btnbox > a {
        float: left;
        margin: 10px 20px;
      }
    </style>
  </head>
  <body>
    <canvas class="canvas" style="float:left" width="800" height="800"></canvas>

    <div class="btnbox" style="display: block;"></div>
    <script>
      var ctx = document.querySelectorAll(".canvas")[0].getContext("2d");
      var img = ctx.createImageData(800, 800);
      var data = img.data;
      for (var i = 0; i < ctx.canvas.height; i++) {
        for (var j = 0; j < ctx.canvas.width; j++) {
          var p = i * ctx.canvas.width + j;
          data[4 * p + 0] = parseInt(
            Math.pow(Math.cos(Math.atan2(j - 400, i - 400) / 2), 2) * 255
          );
          data[4 * p + 1] = parseInt(
            Math.pow(
              Math.cos(
                Math.atan2(j - 400, i - 400) / 2 - (2 * Math.acos(-1)) / 3
              ),
              2
            ) * 255
          );
          data[4 * p + 2] = parseInt(
            Math.pow(
              Math.cos(
                Math.atan2(j - 400, i - 400) / 2 + (2 * Math.acos(-1)) / 3
              ),
              2
            ) * 255
          );
          data[4 * p + 3] = 255;
        }
      }
      ctx.putImageData(img, 0, 0, 0, 0, 800, 800);
    </script>
  </body>
</html>

什么是Ajax?

Ajax是一种可以与服务器交换数据并更新部分页面内容,同时可以在不让整个网页重新加载的情况下更新网页的一种技术

Ajax请求过程:

1:创建一个异步对象
var xmlHttp = new XMLHttpRequest();
2:设置请求方式和请求地址
/*
    参数顺序,描述
    (1)method:请求的类型;GET 或 POST
    (2)url:文件在服务器上的位置
    (3)async:true(异步)或 false(同步) (一般为true,因ajax的精髓就是异步)
*/
    xmlHttp.open("GET||POST","url 如(./ajax.php)",true)
//注意点:url中不能出现中文,只能数字、字母、ASCII码、下划线
//  GET方式的 url格式:./ajax.php?t=123&321......
//如果出现中文也可以用encodeURIComponent方法转换


//  POST方式的url不能在后面接字符串传递参数
xmlHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//setRequestHeader 必须放在设置请求与发送请求之间
//下一步在发送请求send中传递参数即可
xmlHttp.send("name=xuyuxin&age=18")
3:发送请求
 xmlHttp.send();
4.监听状态变化
//监听事件: onreadystatechange 每当请求状态发生变化,就会触发此函数

xmlHttp.onreadystatechange = function (ev2){
    /*
    readyState 状态变化有以下5种
    0:请求未初始化
    1:服务器连接已建立
    2:请求已接收
    3:请求处理中
    4:请求已完成,且响应已就绪
*/
    if(xmlHttp.readyState === 4){

        //请求已完成,并不代表请求成功,因此还需判断是否请求成功
        //status是专门判断请求是否成功的状态码
        // 状态码大于或等于200并且不能超过300以上,300以上除了304以外全都是请求失败
                if(xmlHttp.status >= 200 && xmlHttp.status < 300 || xmlHttp.status === 304){
                       console.log('请求成功')
                }else{
                    console.log('请求失败')
                }
    }



}

http请求成功或失败状态码资料查询

IE低版本浏览器兼容问题

由于在IE6-IE5以下不支持XMLHttpRequest这个属性,因此会产生错误,在低级浏览器中可以使用ActiveXObject来实现同样的效果

var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");

在IE低版本中ajax还有缓存的问题,解决这个问题,要url地址不断改变,不能为常量,即可解决

xmlhttp.open("GET","ajax.php?"+(new Date().getTime()),true)

解决兼容性通用方法

由于在Ajax中浏览器支持的属性不同,单一方案不能支持全部浏览器,有两种解决方案,因此可以把这两种方案合成一种,以便使用

  if(window.XMLHttpRuquest){
            var xmlHttp = new XMLHttpRequest();
        }else{
           var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
        }

=====================================================================================

    var xmlHttp = window.XMLHttpRequest ? new XMLHttpRequest() : new                     ActiveXOject("Microsoft.XMLHTTP");

封装Ajax

/**
 * @param {请求的类型}option.type
 * @param {发送请求的地址} option.url
 * @param {发送请求的数据}option.data
 * @param {超时时间} option.timeout
 * @param {请求成功后执行的函数*} option.sucess
 * @param {请求失败后执行的函数*} option.error
 */
function createAjax(option) {
  //0.把传入对象处理成字符串,服务器才可接收
  var toStringObj = objToString(option.data);
  //1.创建ajax对象,并判断游览器支持那个属性
  var xmlHttp = window.XMLHttpRequest
    ? new XMLHttpRequest()
    : new ActiveXObject("Microsoft.XMLHTTP");
  var timer;
  if (option.type.toloworCase === "get") {
    //2.设置请求方式和地址
    xmlHttp.open("GET", option.url + "?" + toStringObj, true);
    //3.发送请求
    xmlHttp.send();
    //4.监听请求状态
  } else {
    // POST请求方式
    xmlHttp.open(option.type, option.url, true);
    xmlHttp.setRequestHeader(
      "Content-type",
      "application/x-www-form-urlencoded"
    );
    xmlHttp.send(toStringObj);
  }

  xmlHttp.onreadystatechange = function() {
    //请求完成,并不代表请求成功
    if (xmlHttp.readyState === 4) {
      //判断请求是否成功
      if (
        (xmlHttp.status >= 200 && xmlHttp.status < 300) ||
        xmlHttp.status === 304
      ) {
        clearInterval(timer);
        option.sucess(xmlHttp);
      } else {
        option.error(xmlHttp);
      }
    }
  };

  //判断外界是否传入超时时间
  if (option.timeout) {
    timer = setInterval(function() {
      //超时时间到后执行停止此次发送请求,默认为失败
      xmlHttp.abort();
      clearInterval(timer);
    }, option.timeout);
  }
}
//把obj转为字符串
function objToString(data) {
  var res = [];
  data.time = new Date().getTime();
  for (var key in data) {
      //encodeURLComponent函数对,对象名和属性进行转换,以防出现url中不能出现的字符而出错
    res.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
  }
  return res.join("&");
}


//调用方式
    createAjax({
                    data:{
                       name: that.getAttribute('name')
                    },
                    type:"POST",
                    timeout:3000,
                    url:"./ajaxLesson2.php",
                    sucess:function(xml){
                         console.log('请求成功');
                    },
                    error:function(xml){
                        console.log("请求失败");
                    },

                })

获取服务器响应数据方式

  1. responseText 可以获取服务器以字符串形式返回的数据
  2. responseXML 可以获取服务器以XML形式返回的数据
//调用方式
// ajax对象.要获取的方式
xmlHttp.responseText

通过XML传输数据

XML数据基本格式

​ 1.开头前缀指定版本和编码(必要)

<?xml version="1.0" encoding="UTF-8"?>

​ 2.根目录(必要 和html标签一样要闭合)

<root>

</root>

​ 3.之后标签名不受限制,完整版

<?xml version="1.0" encoding="UTF-8"?>
<root>  

    <nz>
        <title>甜美女装</title>
        <des>人见人爱,花间花开,甜美系列</des>
        <image>images/1.jpg</image>
    </nz>
    <bb>
        <title>奢华驴包</title>
        <des>送女友,送情人,送学妹,一送一个准系列</des>
        <image>images/2.jpg</image>
    </bb>
    <tx>
        <title>键盘拖鞋</title>
        <des>程序员专属拖鞋, 屌丝气息浓郁, 你值得拥有</des>
        <image>images/3.jpg</image>
    </tx>

</root>

PHP基本XML配置格式

<?php
    //向客户端发送原始的 HTTP 报头。
    header("content-type:text/xml;charset=utf-8");
    //file_get_contents() 函数是用于将文件的内容读入到一个字符串中的首选方法。如果操作系统支持,还会使用    内存映射技术来增强性能。
    echo file_get_contents("xml文件地址如(./ajax.xml)")
    ?>

Ajax获取XML数据格式

//获取XML传输而来的数据要使用 responseXML方式获取
var Data = xml.responseXML; //返回的是一个document文档对象
//接着使用javascript获取遍历DOM元素
 var titleinfo = Data.querySelector(name+'>title'),
 des = Data.querySelector(name+'>des'),
 image = Data.querySelector(name+'>image');
//最后就可以对DOM里面存储的数据进行操作了
console.log(des.innerHTML);

通过JSON传输数据

JSON资料

JSON数据基本格式

{
    "nz":{
        "title":"甜美女装",
        "des":"人见人爱,花间花开,甜美系列",
        "image":"./images/1.jpg"
    },
        "bb":{
        "title":"奢华驴包",
        "des":"送女友,送情人,送学妹,一送一个准系列",
        "image":"./images/2.jpg"
    },
        "tx":{
        "title":"键盘拖鞋",
        "des":"程序员专属拖鞋, 屌丝气息浓郁, 你值得拥有",
        "image":"./images/3.jpg"
    }
}

//在 JS 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。但是对象和数组是比较特殊且常用的两种类型:
//JSON 键/值对
//JSON 键值对是用来保存 JS 对象的一种方式,和 JS 对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值:
"{"firstName": "Json"}"
这很容易理解,等价于这条 JavaScript 语句:
{firstName : "Json"}

//JSON 与 JS 对象的关系
//很多人搞不清楚 JSON 和 Js 对象的关系,甚至连谁是谁都不清楚。其实,可以这么理解:
//JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
//如:
var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的

var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串

JSON和JS对象互转

//要实现从JSON对象转换为JS字符串,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'}
//要实现从JS对象转换为JSON字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'
//当从服务器返回的数据不是标准json字符串时是无法使用parse的,那么可以试试用eval()强制转化和为js对象

非标准json转js对象

//当从服务器返回的数据不是标准json字符串时是无法使用parse的,那么可以试试用eval()强制转化和为js对象
//注意点: 转js对象必须加 "("+data+")"
var Data = eval("("+data+")")

JSON兼容性问题

在低版本的IE中, 不可以使用原生的JSON.parse方法, 但是可以使用json2.js这个框架来兼容

json2.js下载地址:

PHP基本JSON格式

echo file_get_contents(" JSON文件地址 如(./json.txt)");

跨域

ajax的请求过程:ajax发送请求–浏览器–服务器

响应过程则是请求过程的颠倒

当ajax发送请求到浏览器,浏览器发送到服务器,处理并响应后,原路返回到浏览器,此时会验证其请求来源的域名跟发送请求时是否一样,是则过,否则会被浏览器截止并提示错误,这正是跨域所造成的,想要解决此问题,并不能从前端入手,应该从后端,只有在后端响应并返回后告诉浏览器是自己人即可。

那怎么告诉浏览器是自己人呢? 只要设置其响应头部信息+(Access-Control-Allow-Origin:域名)告诉浏览器即可,允许多个、单个、全部 (*)。

PHP 方式

/*
    1、允许单个域名访问
*/
    header("Access-Control-Allow-Origin:(域名)");
/*
    2、允许多个域名访问
*/
    $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
    $option = array(
        ('域名1'),
        ('域名2'),
        ....
    );
     if(in_array($origin,$option)) header("Access-Control-Allow-Origin:$origin");


/*
    3、允许全部域名访问
*/
    header("Access-Control-Allow-Origin:*");

node方式

/*
    1、允许单个域名访问
*/
    http.createServer(req,res)
    {
        res.setHeader("Access-Control-Allow-Origin","(域名)");
    }
/*
    2、允许多个域名访问
*/    
    let option = [
        (域名1),
        (域名2),
        ...
    ];

    http.createServer(req,res)
    {

      let {origin} =  req.headers;
      let ori = option["origin"] ? option["origin"] : null;
      res.setHeader("Access-Control-Allow-Origin",ori);

    }

/*
    3、允许全部域名访问
*/
    http.createServer(req,res)
    {
        res.setHeader("Access-Control-Allow-Origin","*");
    }

FormData

FormData是ajax2.0新添加的功能,其作用是让表单也能异步发送

语法格式:

//必须要new 一个FormData对象 参数是要应用的表单元素 
//禁止表单默认行为
//其请求方式、请求地址跟随表单元素
//最后发送formdata对象即可

//原生方式
         let form = document.querySelector("form");
        document.querySelector("form").onsubmit = ()=>{
        let xhr = new XMLHttpRequest();
        let formdata = new FormData(form);
        xhr.open(form.method,form.action,true);
        xhr.send(formdata);
        xhr.onreadystatechange =()=>{
          if(xhr.readyState === 4)
        {
          if(xhr.status === 200)
          {
            console.log("成功");
          }else
          {
            console.log("失败");
          }
        }
        }
          return false;
        }
  //jQuery方式 
        $(function(){
          $("form").on("submit",function(){
            console.log(1);
            let formdata = new FormData(this);
            $.ajax({
              url:this.action,
              type:this.method,
              data:formdata,
                //由于jq在发送请求时,会把请求数据自动处理为适合发送的数据格式,但是formdata对象本事就不用处理,系统识别会自动处理数据,如果被jq格式化后,数据就会出错,所以要关闭其数据格式化,以及发送的头部信息。
              processData:false,    
              contentType:false
            }).then((req)=>{
              console.log("成功");
            },(res)=>{
             console.log("失败");
            })

            return false;
          })
        })
//如果不使用表单提交,可以使用以下另门方式
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <div id="div1">
      用户:<input type="text" id="user" /><br>
      密码:<input type="password" id="pass" /><br>
      文件:<input type="file" id="f1" /><br>
      <input id="btn1" type="button" value="提交">
    </div>
  </body>
  <script>
  let oBtn=document.querySelector('#btn1');
  oBtn.onclick=function (){
    let formdata=new FormData();

    formdata.append('username', document.querySelector('#user').value);
    formdata.append('password', document.querySelector('#pass').value);
    formdata.append('f1', document.querySelector('#f1').files[0]);

    //
    let xhr=new XMLHttpRequest();

    xhr.open('post', 'http://localhost:8080/', true);
    xhr.send(formdata);

    xhr.onreadystatechange=function (){
      if(xhr.readyState==4){
        if(xhr.status==200){
          alert('成功');
        }else{
          alert('失败');
        }
      }
    };
  };
  </script>
</html>

fetch

fetch是官方用来解决原生js的ajax的繁杂步骤问题的一门新语法,大大简化了ajax操作,原理基于ajax

// get txt
 window.onload=function (){
      let oBtn=document.getElementById('btn1');
      oBtn.onclick=async function (){
        //1.请求
        let res=await fetch('data/1.txt');
        //2.解析
        let str=await res.text();

        alert(str);
      };
    };
//get json
 window.onload=function (){
      let oBtn=document.getElementById('btn1');
      oBtn.onclick=async function (){
        //1.请求
        let res=await fetch('data/1.json');
        //2.解析
        let json=await res.json();

        console.log(json);
      };
    };
//get blod
window.onload=function (){
      let oImg=document.getElementById('img1');
      let oBtn=document.getElementById('btn1');
      oBtn.onclick=async function (){
        //1.请求
        let res=await fetch('data/1.png');
        //2.解析
        let data=await res.blob();
        let url=URL.createObjectURL(data);

        oImg.src=url;
      };
    };

文章作者:Jing Hong
版权声明:本博客所有文章除特別声明外,均采用CC BY 4.0许可协议。转载请注明来源Jing Hong!
评论
 上一篇
NodeNode
Nodejs编程 第0章 Node介绍0.0 回顾 JavaScript *历史及发展 * 1995年 网景公司的布兰登开发; 1997年7月,ECMA组织发布ECMAScript 1.0版; 2007年10月发布3.1版本后不久,ECMA
2019-10-11
下一篇 
HTML5HTML5
一、H5 拖拽JS 里拖拽三事件, onmousedown onmousemove onmouseup 是实现交互性效果,根据鼠标的移动位置让标签元素联动 而 H5 拖拽也可以实现但更简单,实际例子: 百度图片识别,qq 邮箱文件提交,百度
2019-09-16
  目录