Shinkai005

V1

2022/04/21阅读:25主题:红绯

我要开始学canvas了

我要开始学canvas了

  • 模板就是这玩意
function draw(){
      /** @type {HTMLCanvasElement} */ 
      const canvas = document.getElementById("canvas");
      if(canvas.getContext){
        const ctx = canvas.getContext("2d");
        ctx.fillStyle="rgb(200,0,0)";
        ctx.fillRect(10,10,350,300);

        ctx.fillStyle = "rgba(0,0,200,0.5)";
        ctx.fillRect(30,30,350,300);
      }
    }
<canvas id="canvas" width="800" height="600">
    你这浏览器不支持这玩意啊
 </canvas>

css

body{
  text-align: center;
  padding-top20px;
}
canvas{
  box-shadow0 0 10px #333;
  margin0 auto;
  border-radius25px;
}
image-20220419220254148
image-20220419220254148

描边矩形

// 描边矩形
        ctx.strokeStyle = "rgb(200,0,0)";
        ctx.strokeRect(10,10,350,300);

        ctx.strokeStyle = "rgba(0,0,200,0.5)";
        ctx.strokeRect(30,30,350,300);

填充矩形

// 填充矩形
        ctx.fillStyle="rgb(200,0,0)";
        ctx.fillRect(100,100,350,300);

        ctx.fillStyle = "rgba(0,0,200,0.5)";
        ctx.fillRect(250,250,350,300);

图形 (三角形)

function draw(){
      /** @type {HTMLCanvasElement} */ 
      const canvas = document.getElementById("canvas");
      if(canvas.getContext){
        const ctx = canvas.getContext("2d");
        /** 图形的基本元素就是路径
         * 
         * 1.首先,创建起始路径 beginPath();
         * 2. 使用画图命令画出路径 moveTo()
         * 3. 把路径闭合
         * 4. 通过描边或者填充来绘制.
        */

       
        /*绘制一个三角形*/
        ctx.beginPath();
        // 起始点
        ctx.moveTo(50,500);
        // 第一条线 连接两点
        ctx.lineTo(400,250);
        // 第二条线 
        ctx.lineTo(50,0);
        // 第三条线
        // ctx.lineTo(50,500);
        // 可以不使用第三条线,用closePath()闭合图形
        ctx.closePath();
        
        // 填充三角形
        // ctx.fill();
        // 描线三角形
        ctx.stroke();
      }
    }

作业一个骚骚的图形

image-20220419231210798
function draw(){
      /** @type {HTMLCanvasElement} */ 
      const canvas = document.getElementById("canvas");
      if(canvas.getContext){
        const ctx = canvas.getContext("2d");
        /** 图形的基本元素就是路径
         * 
         * 1.首先,创建起始路径 beginPath();
         * 2. 使用画图命令画出路径 moveTo()
         * 3. 把路径闭合
         * 4. 通过描边或者填充来绘制.
        */

       
        /* 绘制一个矩形 */
        ctx.strokeRect(50,50,500,500)
        /* 绘制左上方三角形 */
        // 开始绘制路径
        ctx.beginPath();
        // 起始点
        ctx.moveTo(150,150);
        // 第一条线
        ctx.lineTo(400,150);
        // 第二条线
        ctx.lineTo(150,400);
        // 闭合
        ctx.closePath();
        // 绘制
        ctx.fill();
        
        /* 绘制第二个三角形 */
        ctx.beginPath();
        ctx.moveTo(450,450);
        ctx.lineTo(200,450);
        ctx.lineTo(450,200);
        ctx.closePath();
        ctx.stroke();

        /* 绘制中间十字 */
        // 第一条线
        ctx.beginPath();
        ctx.moveTo(300,275);
        ctx.lineTo(300,325);
        ctx.stroke();
        // 第二条线
        ctx.beginPath();
        ctx.moveTo(275,300);
        ctx.lineTo(325,300);
        ctx.stroke();

        
      }

画圆

function draw(){
      /** @type {HTMLCanvasElement} */ 
      const canvas = document.getElementById("canvas");
      if(canvas.getContext){
        const ctx = canvas.getContext("2d");
        // 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
        ctx.strokeStyle ='orange';
        ctx.lineWidth = 10;
        
        // 画圆
        //ctx.arc(x,y,radius,startAngle,endAngle,anticlockwise);
        //ctx.arc(400,300,150,0,Math.PI/2,false);
        ctx.arc(400,300,150,0,Math.PI*3/2,true);
        ctx.stroke();
      
      }
    }

循环画圆

image-20220420005146512
image-20220420005146512
function draw(){
      /** @type {HTMLCanvasElement} */ 
      const canvas = document.getElementById("canvas");
      if(canvas.getContext){
        const ctx = canvas.getContext("2d");
        // 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
        /*循环生成圆弧*/
        let x=150;
        let y=60;
        const r=50;
        let end = Math.PI;
        let anticlockWise=false;   
        ctx.lineWidth="2"
        const rule1 = [0,1];  
        const rule2 = [2,3];  
        for (let i = 0; i < 4; i++) {
          
          i!==0&&(x=150,y+=150,end=Math.PI,anticlockWise =!anticlockWise);
          for (let j = 0; j < 3; j++) {
            ctx.beginPath();
            ctx.arc(x,y,r,0,end,anticlockWise);
            rule1.includes(i)&&ctx.stroke();
            rule2.includes(i)&&ctx.fill();
            x+=250;
            end+=1/2*Math.PI;
            
          }
          
        }
      }
    }

优化一下~

includes实在是为了炫技而炫技..

function draw(){
      /** @type {HTMLCanvasElement} */ 
      const canvas = document.getElementById("canvas");
      if(canvas.getContext){
        const ctx = canvas.getContext("2d");
        // 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
        /*循环生成圆弧*/
        let x=150;
        let y=60;
        const r=50;
        let end = Math.PI;
        let anticlockWise=false;   
        ctx.lineWidth="2"
        const rule1 = [0,1];  
        const rule2 = [2,3];  
        for (let i = 0; i < 4; i++) {
          
          i!==0&&(x=150,y+=150,end=Math.PI,anticlockWise =!anticlockWise);
          for (let j = 0; j < 3; j++) {
            ctx.beginPath();
            ctx.arc(x,y,r,0,end,anticlockWise);
            (i<2)&&ctx.stroke();
            (i>=2)&&ctx.fill();
            x+=250;
            end+=1/2*Math.PI;
            
          }
          
        }
      }
    }

和老师写的不一样,也没啥谁更优的,当然是我了!人要自信

老师在循环里声明变量,其实理论上会消耗性能一些,但是如今的V8太先进了可以忽略不计.

画笑脸

image-20220420092632178
image-20220420092632178
function draw(){
      /** @type {HTMLCanvasElement} */ 
      const canvas = document.getElementById("canvas");
      if(canvas.getContext){
        const ctx = canvas.getContext("2d");
        // 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
        /* 外部圆 */
        ctx.beginPath();
        ctx.arc(400,300,300,0,Math.PI*2,false);
        ctx.stroke();
        /* 两个眼睛 */
        ctx.beginPath();
        ctx.arc(250,200,25,0,Math.PI*2,false);
        ctx.stroke();
        ctx.beginPath();
        ctx.arc(550,200,25,0,Math.PI*2,false);
        ctx.stroke();
        /* 嘴巴 */
        ctx.beginPath();
        ctx.arc(400,300,200,Math.PI/4,Math.PI*3/4,false);
        ctx.stroke();
      }
    }

优化:

一个图,分成几个部分画不太好~可以采用重绘起点的方式来让他们之间断开连接.

function draw(){
      /** @type {HTMLCanvasElement} */ 
      const canvas = document.getElementById("canvas");
      if(canvas.getContext){
        const ctx = canvas.getContext("2d");
        // 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
        /* 外部圆 */
        ctx.beginPath();
        ctx.arc(400,300,300,0,Math.PI*2,false);
        /* 两个眼睛 */
        ctx.moveTo(275,200);
        ctx.arc(250,200,25,0,Math.PI*2,false);
        ctx.moveTo(575,200);
        ctx.arc(550,200,25,0,Math.PI*2,false);
        ctx.stroke();
        /* 嘴巴 */
        ctx.beginPath()
        ctx.arc(400,300,200,Math.PI/4,Math.PI*3/4,false);
        ctx.stroke();
      }
    }

嘴巴位置起点位置不好找我就重建路径了.

线性渐变linear gredient

用的不多~

function draw({
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')
          /**
           * 线性渐变
           * 参数1: 起点x1
           * 参数2: 起点y1
           * 参数3: 起点x2
           * 参数4: 起点y2
           *
           */

          const linearGradient = ctx.createLinearGradient(000150)
          /**
           * linearGradient.addColorStop(0,'#cc6677');
           * 参数1: 必须是0.0~1.0之间的一个数值,数值表示颜色所在的相对位置
           * 参数2: 颜色,
           */

          linearGradient.addColorStop(0'#cc6677')
          linearGradient.addColorStop(0.5'#fff')
          linearGradient.addColorStop(0.5'#c6c776')
          const linearGradient2 = ctx.createLinearGradient(050090)
          linearGradient2.addColorStop(0.5'#000')
          linearGradient2.addColorStop(1'rgba(0,0,0,0)')

          ctx.fillStyle = linearGradient
          ctx.fillRect(1010150150)
          ctx.strokeStyle = linearGradient2
          ctx.strokeRect(60,50,50,50)
        }
      }

Radial gradient 辐射渐变

function draw({
        // 让vscode可以显示canvas智能提示
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')
          /**
           * 径向渐变 radial gradient
           * 参数1: x1
           * 参数2: y1
           * 参数3: r1
           * 参数4: x2
           * 参数5: y2
           * 参数6: r2
           */

          const radialGradient = ctx.createRadialGradient(170,170,10,180,180,30);
          radialGradient.addColorStop(0,'#a7d30c');
          radialGradient.addColorStop(0.9,'#019f62');
          radialGradient.addColorStop(1,'rgba(1,159,98,0)');
          const radialGradient2 = ctx.createRadialGradient(280,290,10,280,280,80
          radialGradient2.addColorStop(0,'#ff5f98');
          radialGradient2.addColorStop(0.9,'#ff0188');
          radialGradient2.addColorStop(1,'rgba(255,1,136,0)');
          //画图
          ctx.fillStyle = radialGradient;
          ctx.fillRect(0,0,800,800);
          ctx.fillStyle = radialGradient2;
          ctx.fillRect(0,0,325,325);
          
        }
      }

绘制图片

绘制图片有个问题,一定要先让图片加载出来再渲染.

我这块用了promise.

function draw({
        // 让vscode可以显示canvas智能提示
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')

          // 用promise对象判断是否加载完毕
          const loadImage = function (src{
            
            return new Promise((resolve, reject) => {
              const img = new Image()
              img.src = src
              aim = img
              aim.onload = () => {
                resolve(aim)
              }
              aim.onerror = (e) => {
                reject(e)
              }
            })
          }
     // loadResult是promise对象
          const loadResult = loadImage('hello.png')
          // .then是成功情况,.catch是失败会报错在控制台.
          loadResult
            .then((res) => {
              const pattern = ctx.createPattern(res,"no-repeat")
              ctx.fillStyle = pattern
              ctx.fillRect(0010001000)
            })
            .catch((e) => {
              console.log('error', e)
            })
        }
      }

绘制文字阴影

function draw({
        // 让vscode可以显示canvas智能提示
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')
          //设置线性渐变linear gradient
          const linerGradient = ctx.createLinearGradient(100,200,500,200); 
          linerGradient.addColorStop(0.5,'#cc6677');
          linerGradient.addColorStop(1,'#000');

          //设置阴影
          ctx.shadowColor = 'orange'
          ctx.shadowBlur = 10
          ctx.shadowOffsetX = 20
          ctx.shadowOffsetY = 20
          /**
           * font-weight
           * font-style
           * font-size
           * font-family
           */

          ctx.font = 'bold italic 160px arial'
          ctx.fillStyle = linerGradient;
          ctx.fillText('Shinkai'100200)

        }
      }

drawImage

不想写了

function draw({
        // 让vscode可以显示canvas智能提示
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')
          const img = new Image()
          img.src = 'hello.png'
          img.onload = function ({
            /**
             * 1.图像
             * 2.x轴
             * 3.y轴
             * 4.宽度
             * 5.高度
             * 6.绘制x轴
             * 7.绘制y轴
             * 8.绘制的宽度
             * 9.绘制的高度
             */

            // ctx.drawImage(this,0,0); //正常
            // ctx.drawImage(this,0,0,300,200); //缩放
            ctx.drawImage(this001201001505010050//裁剪
          }
        }
      }

制作房产证

function draw({
        // 让vscode可以显示canvas智能提示
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')
          const loadImage = function (src{
            return new Promise((resolve, reject) => {
              const img = new Image()
              img.src = src
              img.onload = () => {
                resolve(img)
              }
              img.onerror = (e) => {
                reject(e)
              }
            })
          }
          const loadResult = loadImage('fangchanzheng.jpg')
          loadResult
            .then((res) => {
              // const pattern = ctx.createPattern(res, 'no-repeat')
              // ctx.fillStyle = pattern
              // ctx.fillRect(0, 0, 800, 600)
              ctx.drawImage(res,0,0);
              ctx.font = '10 Arial';
              ctx.strokeStyle='rgb(131,121,118)'
              ctx.strokeText('Shinkai',178,120);
              ctx.stroke();
            })
            .catch((e) => {
              console.log(e)
            })
        }
      }

clip

在画布中呈现想要的形状带图案

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Vue.js</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="stylesheet" href="style.css" />
    <script src="vue.js"></script>
  </head>
  <body onload="draw()">
    <div id="vue-app"></div>
    <canvas id="canvas" width="800" height="600">
      你这浏览器不支持这玩意啊
    </canvas>
    <img src="fitness.jpg" alt="">
    <script src="app.js"></script>
    <script>
      function draw({
        // 让vscode可以显示canvas智能提示
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')
          
          const img = new Image();
          img.src = "fitness.jpg";
          
          img.onload = function(){
            ctx.arc(400,300,150,0,Math.PI*2)
            ctx.fill();//这句话图像不显示的时候显示的黑色
            ctx.clip();
            context.drawImage(this,0,0);
          }
        }
      }
    
</script>
  </body>
</html>

第二天

涂鸦

function draw({
        // 让vscode可以显示canvas智能提示
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')

          /* 涂鸦 */
          // 添加鼠标的按下event
          canvas.onmousedown = function (e{
            console.log('执行')
            const ev = e || window.event //兼容
            // xy 是 在画布上的坐标. 就是屏幕上当前的坐标-画布离左上角的坐标.
            const x = ev.clientX - canvas.offsetLeft

            const y = ev.clientY - canvas.offsetTop
            console.log(x, y)
            // 配置画笔
            ctx.strokeStyle = 'green'
            ctx.lineWidth = 10

            //
            ctx.beginPath()
            ctx.moveTo(x, y)
            // 因为不是只需要线,而是涂鸦所以还需要检测移动
            canvas.onmousemove = function (e{
              console.log('移动')
              const ev = e || window.event //兼容
              // xy 是 在画布上的坐标. 就是屏幕上当前的坐标-画布离左上角的坐标.
              const x = ev.clientX - canvas.offsetLeft
              const y = ev.clientY - canvas.offsetTop

              ctx.lineTo(x, y)
              ctx.stroke()
            }

            // 设置结束事件也就是 mouseup
            canvas.onmouseup = function (e{
              canvas.onmousemove = ''
            }
          }
        }
      }

优化要成为习惯~

励志刷完b站所有课的男人+打死不用老师源码的人

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Vue.js</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <!-- <link rel="stylesheet" href="style.css" /> -->
    <style>
      body {
        text-align: center;
        padding-top20px;
        /* margin: 0; */
      }
      canvas {
        box-shadow0 0 10px #333;
        margin0 auto;
        border-radius25px;
        /* position: absolute; */
        /* left: 0; */
      }
    
</style>
    <script src="vue.js"></script>
  </head>
  <body>
    <div id="vue-app">
      <canvas id="canvas" width="800" height="600">
        你这浏览器不支持这玩意啊
      </canvas>
      <button @click="mountedMethod()" :value="control">
        {{control?'正在涂鸦':'已停用涂鸦'}}
      </button>
    </div>

    <!-- <script src="app.js"></script> -->
    <script>
      new Vue({
        el'#vue-app',
        data: {
          controlfalse,
        },
        created() {},
        methods: {
          draw() {
            // 让vscode可以显示canvas智能提示
            /** @type {HTMLCanvasElement} */
            const canvas = document.getElementById('canvas')
            if (canvas.getContext) {
              const ctx = canvas.getContext('2d')

              /* 涂鸦 */
              // 添加鼠标的按下event
              canvas.onmousedown = function (e{
                console.log('执行')
                const ev = e || window.event //兼容
                // xy 是 在画布上的坐标. 就是屏幕上当前的坐标-画布离左上角的坐标.
                const x = ev.clientX - canvas.offsetLeft

                const y = ev.clientY - canvas.offsetTop
                console.log(x, y)
                // 配置画笔
                ctx.strokeStyle = 'green'
                ctx.lineWidth = 10

                //
                ctx.beginPath()
                ctx.moveTo(x, y)
                // 因为不是只需要线,而是涂鸦所以还需要检测移动
                canvas.onmousemove = function (e{
                  console.log('移动')
                  const ev = e || window.event //兼容
                  // xy 是 在画布上的坐标. 就是屏幕上当前的坐标-画布离左上角的坐标.
                  const x = ev.clientX - canvas.offsetLeft
                  const y = ev.clientY - canvas.offsetTop

                  ctx.lineTo(x, y)
                  ctx.stroke()
                }

                // 设置结束事件也就是 mouseup
                canvas.onmouseup = function (e{
                  canvas.onmousemove = ''
                }
              }
            }
          },
          mountedMethod() {
            this.control = !this.control
            this.$nextTick(() => {
              this.draw()
            })
          },
        },
      })
    
</script>
  </body>
</html>



上面是蛇精病写法....希望大家不会有一天遇到这样的需求....

主要的点就在于 dom渲染后在执行需要用this.$nextTick方法.

然后还有个bug是 开了控制台 绘画位置和鼠标不在一起了. 需要检测一下是否开了控制台,然后再重新写一个计算xy方式.懒得改了

蒙版实现刮刮乐

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Vue.js</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link rel="stylesheet" href="style.css" />
    <script src="vue.js"></script>
  </head>
  <body onload="draw()">
    <div id="vue-app"></div>
    <img src="beautiful.jpg" alt="" />
    <canvas id="canvas" width="800" height="600">
      你这浏览器不支持这玩意啊
    </canvas>
    <script src="app.js"></script>
    <script>
      function draw({
        // 让vscode可以显示canvas智能提示
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        if (canvas.getContext) {
          const ctx = canvas.getContext('2d')
          //蒙版
          ctx.beginPath()
          ctx.fillStyle = 'rgba(100,100,100,0.99)'
          ctx.fillRect(00240360)

          ctx.globalCompositeOperation = 'destination-out'
          ctx.lineWidth = 20
          ctx.lineCap = 'round'

          canvas.onmousedown = function (e{
            const ev = e || window.event
            const x = ev.clientX
            const y = ev.clientY
            ctx.moveTo(x, y)
            canvas.onmousemove = function (e{
              const ev = e || window.event
              const x = ev.clientX
              const y = ev.clientY
              ctx.lineTo(x, y)
              ctx.stroke()
            }
            canvas.onmouseup = function (e{
              canvas.onmousemove = ''
            }
          }
        }
      }
    
</script>
  </body>
</html>


分类:

前端

标签:

JavaScript

作者介绍

Shinkai005
V1

公众号:深海笔记Shinkai