canvas学习

124 阅读1分钟

1:绘制基本图形

1.矩形绘制-填充模式

<body>
    <canvas width="400" height="600" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx =canvas.getContext('2d');
        // 填充颜色
        ctx.fillStyle = 'red'
         // 绘制一个填充的矩形
        ctx.fillRect(10,10,100,100)
    </script>
</body>
</html>

image.png

2.矩形绘制-路径模式

<body>
    <canvas width="400" height="600" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx =canvas.getContext('2d');
         // 绘制一个矩形的边框。
        ctx.strokeStyle = 'red'
        ctx.strokeRect(130,130,200,200) 

    </script>
</body>

image.png

3.矩形绘制填充-拆分-分段路径-模式

<body>
    <canvas width="400" height="600" id="main"></canvas>
    <script>
        const canvas = document.getElementById('main')
        const ctx = canvas.getContext('2d');

        // beginPath  closePath  可以完成路径的分段
        ctx.beginPath()
        // 拆开的写法
        ctx.rect(10, 10, 100, 100)
        //显示路径
        ctx.stroke()
        ctx.closePath()

        ctx.beginPath()
        ctx.rect(110, 110, 100, 100)
        //填充, 这里会覆盖上面的stroke
        ctx.fill()
        ctx.closePath()

    </script>
</body>

image.png

4.矩形绘制-清除模式

<body>
    <canvas width="400" height="600" id="main"></canvas>
    <script>
       const canvas =  document.getElementById('main')
        const ctx =canvas.getContext('2d');
        // 绘制一个填充的矩形
        ctx.fillStyle = 'red'
        ctx.fillRect(10,10,100,100)

         // 绘制一个矩形的边框。
        ctx.strokeStyle = 'red'
        ctx.strokeRect(130,130,200,200) 

        // 清除指定的矩形区域,然后这块区域会变的完全透明。
        // ctx.clearRect(0,0,canvas.width,canvas.height)
        let height = 0
        const canvasWidth = canvas.width
        const canvasHeight = canvas.height
        const timer = setInterval(() => {
            height++
            //清除指定的矩形区域,然后这块区域会变的完全透明。
            ctx.clearRect(0,0,canvasWidth,height)
            if(height === canvasHeight){
                clearInterval(timer)
            }
        }, 10);
    </script>
</body>

2:绘制圆形

1.圆弧

<body>
    <canvas width="400" height="600" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');
        
        // arc是绘制圆弧的方法
        // ctx.arc(圆心x, 圆心y,半径,开始的角度,结束的角度,是否顺时针【默认逆时针false】)
        ctx.beginPath()
        ctx.arc(300,200,50,0,Math.PI*2)
        ctx.fill()
        ctx.closePath()

        ctx.beginPath()
        ctx.arc(120,120,120,0,Math.PI/3,true)

        ctx.stroke()
        ctx.closePath()
    </script>
</body>

image.png

2.圆弧绘制笑脸

<body>
    <canvas width="400" height="600" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');
        
        // 绘制脸
        ctx.beginPath()
        ctx.arc(75,75,50,0,Math.PI*2)
        ctx.stroke()
        ctx.closePath()

        // 绘制嘴巴
        ctx.beginPath()
        ctx.arc(75,75,35,0,Math.PI) 
        ctx.stroke()
        ctx.closePath()

        // 绘制左眼
        ctx.beginPath()
        ctx.arc(60,65,5,0,Math.PI * 2)
        ctx.stroke()
        ctx.closePath()

        // 绘制右眼
        ctx.beginPath()
        ctx.arc(90,65,5,0,Math.PI * 2)
        ctx.stroke()
        ctx.closePath()
    </script>
</body>

image.png 3.圆弧绘制笑脸-moveTo

<body>
    <canvas width="400" height="600" id="main"></canvas>
    <script>
        const canvas = document.getElementById('main')
        const ctx = canvas.getContext('2d');

        // 绘制脸
        ctx.beginPath()
        ctx.arc(75, 75, 50, 0, Math.PI * 2)
        ctx.moveTo(110, 75)

        // 绘制嘴巴
        ctx.arc(75, 75, 35, 0, Math.PI)
        ctx.moveTo(65, 65)

        // 绘制左眼 
        ctx.arc(60, 65, 5, 0, Math.PI * 2)
        ctx.moveTo(95, 65)

        // 绘制右眼 
        ctx.arc(90, 65, 5, 0, Math.PI * 2)
        ctx.stroke()

        ctx.closePath()
    </script>
</body>

image.png

4.arcTo绘制圆弧

void ctx.arcTo(x1, y1, x2, y2, radius);
<body>
    <canvas width="600" height="400" id="main"></canvas>
    <script>
        const canvas = document.getElementById('main')
        const ctx = canvas.getContext('2d');

        ctx.beginPath()

        ctx.moveTo(300, 200)
        ctx.arcTo(300, 300, 200, 300, 100)

        ctx.stroke()

        ctx.closePath()
    </script>
</body>

image.png

<body>
    <canvas width="600" height="400" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');

        ctx.strokeRect(100,100,100,100) 
        ctx.moveTo(200,100)
        ctx.arcTo(200,200,100,200,50)
        ctx.stroke()
    </script>
</body>

image.png

3:绘制线段

<body>
    <canvas width="600" height="400" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');
        
        // 绘制脸
        ctx.beginPath()

        ctx.moveTo(300,200)
        ctx.lineTo(400,200)
        ctx.lineTo(400,100)
        ctx.lineTo(300,200)
        ctx.stroke()
        
        ctx.closePath()
    </script>
</body>

image.png

4:二次贝塞尔曲线

<body>
    <canvas width="600" height="600" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');

        ctx.beginPath()
        ctx.moveTo(200,300)
        ctx.quadraticCurveTo(150,300,150,200)
        ctx.quadraticCurveTo(150,100,300,100)
        ctx.quadraticCurveTo(450,100,450,200)
        ctx.quadraticCurveTo(450,300,250,300)
        ctx.quadraticCurveTo(250,350,150,350)
        ctx.quadraticCurveTo(200,350,200,300)
        
        ctx.stroke()
    </script>
</body>

image.png

5:三次贝塞尔曲线

  <body>
    <canvas width="600" height="600" id="main"></canvas>
    <script>
      const canvas = document.getElementById("main");
      const ctx = canvas.getContext("2d");
      var heartPath = new Path2D();

      console.log(heartPath);
      // 起点
      heartPath.moveTo(300, 200); // 2个控制点、1个终点
      heartPath.bezierCurveTo(350, 150, 400, 200, 300, 250);
      heartPath.bezierCurveTo(200, 200, 250, 150, 300, 200);

      ctx.stroke(heartPath);
      var chatPath = new Path2D();
      chatPath.moveTo(200, 300);
      chatPath.quadraticCurveTo(150, 300, 150, 200);
      chatPath.quadraticCurveTo(150, 100, 300, 100);
      chatPath.quadraticCurveTo(450, 100, 450, 200);
      chatPath.quadraticCurveTo(450, 300, 250, 300);
      chatPath.quadraticCurveTo(250, 350, 150, 350);
      chatPath.quadraticCurveTo(200, 350, 200, 300);
      ctx.stroke(chatPath);
      ctx.fill(heartPath); // 创建一条折线
      var polyline = new Path2D("M10 10 h 80 v 80 h -80 z");
      ctx.stroke(polyline);
    </script>
  </body>

image.png

6:颜色设置

<body>
    <canvas width="400" height="600" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');
        
        // 绘制脸
        ctx.beginPath()
        ctx.strokeStyle = 'red'
        ctx.arc(75,75,50,0,Math.PI*2) 
        ctx.stroke()

        ctx.beginPath()
        // 绘制嘴巴
        ctx.strokeStyle = 'blue'
        ctx.arc(75,75,35,0,Math.PI)  
        ctx.stroke()

        ctx.beginPath()
        // 绘制左眼 
        ctx.fillStyle = 'yellow'
        ctx.arc(60,65,5,0,Math.PI * 2)  
        ctx.fill()

        ctx.beginPath()
        // 绘制右眼 
        ctx.fillStyle = '#398009'
        ctx.arc(90,65,5,0,Math.PI * 2)
        ctx.fill()

        ctx.closePath()
    </script>
</body>

image.png

7:渐变

  1. 线性渐变
  <body>
    <canvas width="600" height="600" id="main"></canvas>
    <script>
      const canvas = document.getElementById("main");
      const ctx = canvas.getContext("2d");
      // const linearGradient = ctx.createLinearGradient(0, 0, 600, 600);
      let inx = 0;
      function render() {
        ctx.clearRect(100, 100, 500, 300);
        if (inx >= 0.99) {
          inx = 0;
        }
        inx += 0.01;
        const linearGradient = ctx.createLinearGradient(100, 100, 600, 400);
        linearGradient.addColorStop(0, "red");
        linearGradient.addColorStop(inx, "yellow");
        linearGradient.addColorStop(1, "blue");
        ctx.fillStyle = linearGradient;
        ctx.fillRect(100, 100, 500, 300);
        window.requestAnimationFrame(render);
      }
      render();
    </script>
  </body>

image.png

2.径向渐变

  <body>
    <canvas width="600" height="600" id="main"></canvas>
    <script>
      const canvas = document.getElementById("main");
      const ctx = canvas.getContext("2d");
    
        const radialGradient = ctx.createRadialGradient(200, 200, 0, 200, 200, 100);
        radialGradient.addColorStop(0, "red");
        radialGradient.addColorStop(0.5, "yellow");
        radialGradient.addColorStop(1, "blue");
        ctx.fillStyle = radialGradient;
        ctx.fillRect(0, 0, 400, 400); 
      
    </script>
  </body>

image.png

  1. 径向渐变-3D球
  <body>
    <canvas width="600" height="600" id="main"></canvas>
    <script>
      const canvas = document.getElementById("main");
      const ctx = canvas.getContext("2d");
    
        const radialGradient = ctx.createRadialGradient(200, 200, 10, 250, 250, 100);
        radialGradient.addColorStop(0, "pink");
        radialGradient.addColorStop(1, "red");
        ctx.fillStyle = radialGradient;
        ctx.arc(300,300,200,0,Math.PI*2)
        ctx.fill()
      
    </script>
  </body>

image.png

8:绘制图片

<body>
    <canvas width="1200" height="900" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');

        const img = new Image()
        img.src = '../img/1.jpg'
        img.onload = function(){
            console.log(img.__proto__,img.width,img.height);
            // ctx.drawImage(img, 0, 0)
            // ctx.drawImage(img, 0, 0, 1200, 900);
            ctx.drawImage(img,img.width/4,0,img.width*3/4,img.height/2    ,0, 0,400, 300);
        }
    </script>
</body>

image.png

9. 绘制视频and添加水印

  <body>
    <canvas width="500" height="500" id="main"></canvas>
    <video src="../img/mov_bbb.mp4"></video>
    <button onclick="playPause()">播放/暂停</button>
    <script>
      const canvas = document.getElementById("main");
      const ctx = canvas.getContext("2d");

      const myVideo = document.querySelector('video')
      function playPause() {
        if (myVideo.paused){
            myVideo.play();
            render()
        }else{
            myVideo.pause();
        }
      }

      const img = new Image()
        img.src = '../img/1.jpg'

      function render(){
        if(!myVideo.paused){
            ctx.drawImage(myVideo, 0, 0, 300, 200);
            ctx.drawImage(img,250,150,50,50)
            requestAnimationFrame(render)
        }
      }


     
      

      // const img = new Image()
      // img.src = '../img/1.jpg'
      // img.onload = function(){
      //     console.log(img.__proto__,img.width,img.height);
      //     // ctx.drawImage(img, 0, 0)
      //     // ctx.drawImage(img, 0, 0, 1200, 900);
      //     ctx.drawImage(img,img.width/4,0,img.width*3/4,img.height/2    ,0, 0,400, 300);
      // }
    </script>
  </body>

image.png

10.文字绘制

  <body>
    <canvas width="600" height="400" id="main"></canvas>
    <script>
            const canvas = document.getElementById("main");
            const ctx = canvas.getContext("2d");

      // 文字,大小/字体
      ctx.strokeStyle ="#ff0000";
      ctx.font ="100px Microsoft YaHei";
      // 填充渲染文字
      // fillText(文本,文本的起点x,文本起点的y,绘制文字最大的宽度)
      // ctx.fillText("老陈",300,200,100);
      // 文本对齐选项textAlign,start(默认),end,left,right,center
      ctx.textAlign ="center";
      // 文本基线对齐,textBaseline,top,bottom,alphabetic
      ctx.textBaseline ="middle";
      // 文本的方向
      // ctx.direction ="rtl";
      // 预测量文本宽度
      let text = ctx.measureText("Hello!");
      console.log(text);
      ctx.strokeText("Hello!",300,200);
    </script>
  </body>

image.png

11.位移变换

  1. 平移坐标系 translate
  <body>
    <canvas width="600" height="400" id="main" style="border: 1px solid #000000"></canvas>
    <script>
      const canvas = document.getElementById("main");
      const context = canvas.getContext("2d");

      // 会将坐标系平移 100 100 的位置
      context.translate(100, 100);
      
      context.fillRect(0,0,100,100);

      context.fillStyle='red';
      
      context.fillRect(50,50,100,100);
      
    </script>
  </body>

image.png

2.旋转坐标系rotate

  <body>
    <canvas
      width="600"
      height="400"
      id="main"
      style="border: 1px solid #000000"
    ></canvas>
    <script>
      const canvas = document.getElementById("main");
      const ctx = canvas.getContext("2d");
      // 3.绘制图形
      // 3.1 绘制矩形fillRect(位置x,位置y,宽度,高度)
      // scale拉伸坐标系
      // ctx.rotate(Math.PI / 4);
      // ctx.translate(300, 200);
      // ctx.scale(2, 1);
      // ctx.fillRect(-250, -25, 500, 50);

      ctx.fillRect(200,150, 200, 20);

      // 整个坐标系会旋转
      ctx.rotate(Math.PI / 10);
      ctx.fillRect(200,150, 200, 100);
    </script>
  </body>

image.png

3.坐标系放大 scale

  <body>
    <canvas width="600" height="400" id="main" style="border: 1px solid #000000"></canvas>
    <script>
      const canvas = document.getElementById("main");
      const context = canvas.getContext("2d");

      // x轴放大5倍, Y轴放大2倍, 整个坐标系也是放大 x-->5倍  y-->2倍
      context.scale(5, 2);
      
      context.fillRect(0,0,100,100);

    </script>
  </body>

image.png

12:图像合成

美女刮刮卡 ctx.globalCompositeOperation = "destination-out"

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
    .content {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 1;
      width: 600px;
      height: 400px;
      display: flex;
    }

    h1 {
      margin: auto;
    }

    canvas {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 2;
    }
  </style>
</head>

<body>
  <div class="content">
    <h1>谢谢惠顾</h1>
  </div>
  <canvas width="600" height="400" id="main"></canvas>
  <script>
    const canvas = document.getElementById("main");
    const ctx = canvas.getContext("2d");
    let img = new Image();
    img.src = '../img/1.jpg'
    img.onload = function () {
      ctx.drawImage(img, 0, 0, 600, 400);
    }

    let isDraw = false

    canvas.onmousedown = function (){
      isDraw = true
    }

    canvas.onmouseup = function (){
      isDraw = false
    }

    canvas.onmousemove = function (e) {
      console.log(isDraw);
      if(isDraw){
        var x = e.pageX;
        var y = e.pageY;
        ctx.globalCompositeOperation = "destination-out"
        ctx.beginPath();
        ctx.arc(x, y, 10, 0, 2 * Math.PI);
        ctx.fill();
      }
    }

    // 手机端
    canvas.ontouchmove = function(e){
      const {clientX:x,clientY:y} = e.touches[0]
      ctx.globalCompositeOperation = "destination-out"
        ctx.beginPath();
        ctx.arc(x, y, 5, 0, 2 * Math.PI);
        ctx.fill();
    }
  </script>
</body>

</html>

image.png

13:剪裁 clip

<body>
    <canvas width="600" height="600" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');


        ctx.moveTo(200,300)
        ctx.quadraticCurveTo(150,300,150,200)
        ctx.quadraticCurveTo(150,100,300,100)
        ctx.quadraticCurveTo(450,100,450,200)
        ctx.quadraticCurveTo(450,300,250,300)
        ctx.quadraticCurveTo(250,350,150,350)
        ctx.quadraticCurveTo(200,350,200,300)
        ctx.stroke()
        ctx.clip()
        let img = new Image();
        img.src = '../img/1.jpg'
        img.onload = function () {
             ctx.drawImage(img, 0, 0, 600, 400);
        }
    </script>
</body>

image.png

14:状态的恢复与保存

<body>
    <canvas width="800" height="800" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');

        ctx.fillStyle='red';
        ctx.fillRect(0,0,100,100);
        ctx.save()

        ctx.fillStyle='yellow';
        ctx.fillRect(100,100,100,100);
        ctx.save()

        ctx.fillStyle='blue';
        ctx.fillRect(200,200,100,100);
        // ctx.save()

        ctx.restore()
        ctx.fillRect(300,300,100,100);

        ctx.restore()
        ctx.fillRect(400,400,100,100);

    </script>
</body>

image.png

15:像素操作

这个地方有个坑,要使用服务器发开页面,比如http-server,本地打开会报错 from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted

<body>
    <canvas width="400" height="300" id="main"></canvas>
    <script>
        const canvas =  document.getElementById('main')
        const ctx = canvas.getContext('2d');

        const img = new Image()
        img.crossOrigin = ''
        img.src = '../img/1.jpg'
        img.onload = function(){
            ctx.drawImage(img,0,0,400,300);
            // 使用 http-server 访问,不然会跨域😭
            const imgData = ctx.getImageData(0, 0,400, 300);
            console.log(imgData);
            
            for (let i = 0; i < imgData.data.length; i+=4) {
                const avg =(imgData.data[i]+imgData.data[i+1]+imgData.data[i+2])/3
                imgData.data[i] = avg
                imgData.data[i+1] = avg
                imgData.data[i+2] = avg
            }
            // ctx.putImageData(imgData, 0, 0);
            ctx.putImageData(imgData, 0, 0, 0,0, 200, 150);
         
            
        }
    </script>
</body>

image.png

16:结合事件

  <body>
    <canvas width="600" height="600" id="main"></canvas>
    <script>
     
     
      class A{
        constructor(x,y){
          const canvas = document.getElementById("main");
          this.x = x
          this.y = y
          this.color = 'red'
          this.isIn = false;
          this.ctx = canvas.getContext("2d");

          canvas.onmousemove = (e) =>{
            const {offsetX:x,offsetY:y} = e 
            this.isIn = this.ctx.isPointInPath(x, y); 
            if(this.isIn){
              this.color = 'blue'
            }else{
              this.color = 'red'
            }
            this.draw()
          }

        }

        draw(){
          // isPointInPath()不支持canvas自带的两个方法fillRect(),strokeRect();

          // this.ctx.save()
          // this.ctx.clearRect(this.x, this.y, 200, 200);
          // this.ctx.fillStyle = this.color
          // this.ctx.fillRect(this.x, this.y,200,200);
          // this.ctx.restore(); 
 
          this.ctx.clearRect(this.x, this.y, 200, 200);
          this.ctx.fillStyle = this.color
          this.ctx.rect(this.x, this.y,200,200);
          this.ctx.fill() 
        }
      };

      const a = new A(0,0);
      a.draw()
    </script>
  </body>

鼠标移入移出的效果 image.png

2.签名面板

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .active{
        background: red;
      }
    </style>
  </head>
  <body>
    <canvas id="c1" width="2000" height="600"></canvas>
    <hr />
    <button id="boldBtn" type="button">粗线条</button>
    <button id="thinBtn" type="button">细线条</button>
    <button id="saveBtn" type="button">保存签名</button>
    <input  id="colorBtn" type="color" name=" value="" />
    <button id="clearBtn">橡皮擦</button>
    <button id="nullBtn">清空画布</button>
    <script>
      // 1获取canvas画布和绘制的上下文对象
      const canvas = document.querySelector("#c1");
      const ctx = canvas.getContext("2d");

      // 圆润的连接
      ctx.lineJoin='round';
      
  
      // 设置画笔的粗细
      const boldBtn = document.querySelector("#boldBtn");
      boldBtn.addEventListener('click',function(e){
        ctx.lineWidth = 20;
        this.classList.add('active')
        thinBtn.classList.remove('active')
        clearBtn.classList.remove('active')
        ctx.globalCompositeOperation='source-over';
      })

      const thinBtn = document.querySelector("#thinBtn");
      thinBtn.addEventListener('click',function(e){
        ctx.lineWidth = 1;
        this.classList.add('active')
        boldBtn.classList.remove('active')
        clearBtn.classList.remove('active')
        ctx.globalCompositeOperation='source-over';
      })

      // 保存签名
      const saveBtn = document.querySelector("#saveBtn");
      saveBtn.addEventListener('click',function(e){
        const urlData = canvas.toDataURL()
        // const img = new Image()
        // img.src = urlData
        // document.body.appendChild(img)

        const a = document.createElement('a')
        a.href = urlData
        a.download = 'fux签名'
        a.click()
        a.remove()
      })

      // 修改颜色
      const colorBtn = document.querySelector("#colorBtn");
      colorBtn.addEventListener('change',function(e){
        ctx.strokeStyle=this.value
        
      })

      // 橡皮擦
      const clearBtn = document.querySelector("#clearBtn");
      clearBtn.addEventListener('click',function(e){
        ctx.globalCompositeOperation='destination-out';
        ctx.lineWidth = 20
        this.classList.add('active')
        thinBtn.classList.remove('active')
        boldBtn.classList.remove('active')
      })

      // 清空画板
      const nullBtn = document.querySelector("#nullBtn");
      nullBtn.addEventListener('click',function(e){
        ctx.clearRect(0, 0, canvas.width, canvas.height);
      })

      let isDraw = false;

      canvas.onmousedown = function(e){
        isDraw = true
        ctx.beginPath();
        const x = e.pageX - canvas.offsetLeft
        const y = e.pageY - canvas.offsetTop
        ctx.moveTo(x, y);
      }

      canvas.onmouseleave = function(e){
        isDraw = false
        ctx.closePath();
      }
      canvas.onmouseup = function(e){
        isDraw = false
        ctx.closePath();
      }

      canvas.onmousemove = function(e){ 
        if(isDraw){
          const x = e.pageX - canvas.offsetLeft
          const y = e.pageY - canvas.offsetTop
          ctx.lineTo(x,y)
          ctx.stroke();
        }
      }

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

image.png