canvas 基础 和 动图案例

2 篇文章 0 订阅

 <canvas> 是 HTML5 新增的一种标签,一个可以使用脚本(通常为JavaScript)在其中绘制图的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。

 

<canvas> 目前只支持JS,不支持其他如 jQuery 等等; 需要设置宽高,单位默认是px,如果不设置宽高,默认为 300*150;

<canvas>在低版本浏览器不兼容时,可以在标签内,设置不兼容时显示的内容

 一、canvas 获取画布和画笔 画线条

注意:canvas 标签是行内块元素,如果需要设置他的布局,可以设置为块级元素

1.设置一个canvas 画布画直线,需要固定执行以下步骤:

1.1.获取canvas 标签(通过标签选择器来选择)

1.2.拿到canvas 的上下文对象(通过ctx.getContext('2d')来获取)

1.3.开启一条路径 beginPath();

1.4.确定起始点 moveTo(x,y)

1.5.确定终点  lineTo(x,y) ,这里可以连写lineTo(x,y) 终点位置,实现连笔画,及以上一个结束坐标作为下一个点的开始坐标;

1.6.进行上色  

1.7.关闭路劲

    /* 1.获取canvas 标签 */
    let canvas = document.querySelector('canvas')
    /* 2.获取上下文对象,使用getContext('2d')方法 */
    let ctx = canvas.getContext('2d');
    /* 3.开启一条路径 */
    ctx.beginPath();
    /* 4.确定起点坐标 坐标是相对于 canvas 的左上角  */
    ctx.moveTo(100, 100)
    /* 5.确实终点坐标 */
    ctx.lineTo(400, 400)

    /* 设置颜色和线宽,必须要在上色之前设置颜色和线宽属性 */
    ctx.strokeStyle = 'pink'
    ctx.lineWidth = 5

    /* 6.上色 */
    ctx.stroke()
    /* 7.关闭路径 */
    ctx.closePath();

2.画虚线(通常可以把重复的代码封装为一个函数来调用)

2.1 可以使用循环方式连续画出不同的直线,实现虚线效果;原理是,不断的变化每条小的直线的坐标,来实现;

let canvas = document.querySelector('canvas')
let ctx = canvas.getContext('2d')

drawLine(100, 100, 105, 100, 'pink', 5)

// 画虚线
for (let i = 0; i < 20; i++) {
  drawLine(100 + 10 * i, 100, 105 + 10 * i, 100, 'pink', 5)
}

// 斜角度画虚线
for (let i = 0; i < 20; i++) {
  drawLine(100 + 10 * i, 100 + 10 * i, 105 + 10 * i, 105 + 10 * i, 'pink', 5)
}
// 画直线函数
function drawLine(x1, y1, x2, y2, color, width) {
  ctx.beginPath();
  ctx.moveTo(x1, y1)
  ctx.lineTo(x2, y2)
  ctx.strokeStyle = color
  ctx.lineWidth = width
  ctx.stroke()
  ctx.closePath();
}

2.2 通过 ctx.setLineDash([num1,num2]) 方法和   ctx.lineDashOffset = -5; 来定制虚线样式

(1)setLineDash([num1,num2]) 方法接受一个数组作为参数,num1为 线条长度,num2 为间隔距离

(2)lineDashOffset = num, 方法设置起始点偏移量;

function draw() {
  let canvas = document.querySelector('canvas')
  let ctx = canvas.getContext('2d')

  ctx.setLineDash([10, 10]);  // [实线长度, 间隙长度]
  ctx.lineDashOffset = -5;
  // ctx.strokeRect(50, 50, 210, 210);
  ctx.beginPath()
  ctx.moveTo(0, 0)
  ctx.lineTo(100, 100)
  ctx.stroke()
  ctx.closePath()
}
draw();

3.线条连画(多次调用lineTo(x,y))

4.画矩形

4.1 画有填充矩形 方法:fillRect(x,y,width,height)

        x,y表示矩形左上角起始坐标;

        width,height 表示 矩形的长宽;

ctx.fillRect(200, 200, 100, 100)
ctx.fillStyle = 'red'

4.2 画无填充矩形 方法:ctx.strokeRect(x,y,width,height)

ctx.strokeStyle = '#ccc'
ctx.lineWidth = 3
ctx.strokeRect(300, 300, 100, 100)

4.3 画有边框的 填充矩形 

let canvas = document.querySelector('canvas')
let ctx = canvas.getContext('2d')

ctx.beginPath()
// 创建矩形,rect(x,y,width,height)
ctx.rect(100, 100, 100, 100)
// 填充色
ctx.fillStyle = 'pink'
// 填充
ctx.fill()
// 描边
ctx.strokeStyle = 'green'
// 描边的宽度
ctx.lineWidth = 5
// 着色
ctx.stroke()

 简单实例如下:

5.清除画布  方法:ctx.clearRect(x,y,w,h) 

清除画布在画图过程中很常用,比如实现动态图,就是不断清除画布和重绘画布;

/* 画矩形 */
let canvas = document.querySelector('canvas')
let ctx = canvas.getContext('2d')

ctx.beginPath()
// 创建矩形,rect(x,y,width,height)
ctx.rect(100, 100, 300, 200)
// 填充色
ctx.fillStyle = 'pink'
// 填充
ctx.fill()
// 描边
ctx.strokeStyle = 'green'
// 描边的宽度
ctx.lineWidth = 5
// 着色
ctx.stroke()
// 清除画布 clearRect(x,y,width,height) 坐标x,y依然是矩形的左上角;
ctx.clearRect(120, 120, 150, 100)

6.画圆 

方法:arc(x,y,radius,startAngle,endAngle,counterclockwise)

      (1)x,y 表示圆心坐标

      (2)radius 表示圆弧半径

      (3)startAngle 开始角度

      (4)endAngle 结束角度

      (5)counterclockwise 表示画圆弧的顺逆时针方向,false 表示顺时针,true表示逆时针

ctx.beginPath()
ctx.arc(400, 400, 250, 0, Math.PI * 2, false)
ctx.strokeStyle = 'red'
ctx.lineWidth = 3
// 内部填充
ctx.fillStyle = 'gold'
ctx.fill()
// 着色,不可缺,没有着色是不能看到的
ctx.stroke()

// 图像不是封闭的时候,需要设置开始路径和结束路径的,要不然会连笔
ctx.beginPath()
ctx.arc(400, 400, 100, 0, Math.PI, false)
ctx.strokeStyle = 'red'
ctx.lineWidth = 3
// 着色,不可缺,没有着色是不能看到的
ctx.stroke()

 7.画文字

7.1 绘制文字

      (1).实心字体 方法:fillText('文字内容',x,y,maxWidth)

      (2) 空心字体 方法:ctx.strokeText('文字内容', x, y)

          - x,y 表示文字的开始位置坐标,maxWidth表示文字内容最大宽度,超出会压缩

      (3)线性渐变

         方法:let grd = ctx.createLinearGradient(x1, y, x2, y2);

          grd.addColorStop('0','颜色')

          grd.addColorStop('1','颜色')

          - x1,y1,x2,y2表示渐变开始和结束的坐标

          - addColorStop() 表示,颜色的添加

    // 字体的样式需要放在 前面
    ctx.font = '50px 微软雅黑'
    ctx.fillStyle = 'gold'
    // 实心字体
    ctx.fillText('hello canvas', 100, 100)

    ctx.font = '50px 宋体'
    // 空心字体
    ctx.strokeStyle = 'pink'
    ctx.strokeText('你好', 100, 200)

    // 颜色线性渐变
    let grd = ctx.createLinearGradient(0, 0, canvas.width, 0);
    // 增加渐变颜色以及范围
    grd.addColorStop('0', 'yellow')
    grd.addColorStop('0.5', 'red')
    grd.addColorStop('1.0', 'skyBlue')

    // 应用渐变,把渐变的变量 赋值给strokeStyle 属性
    ctx.strokeStyle = grd;
    ctx.font = '40px 楷体'
    ctx.strokeText('今天很冷,要多穿衣服哦!!!', 0, 300, 400)

 

 7.2 文字位置设置

 文字位置属性:

      文字水平方向:start,end, left,right,center

      文字垂直方向:top,bottom,middle

    // 画田字格
    ctx.beginPath();
    ctx.moveTo(250, 0)
    ctx.lineTo(250, 500)
    ctx.stroke()
    ctx.closePath()

    ctx.beginPath();
    ctx.moveTo(0, 250)
    ctx.lineTo(500, 250)
    ctx.stroke()
    ctx.closePath()

    ctx.font = '30px 宋体'
    ctx.fillStyle = 'red'
    // 水平居中 (参考点都是坐标点)
    ctx.textAlign = 'center'
    // 垂直居中 (参考点都是坐标点)
    ctx.textBaseline = 'middle'
    ctx.fillText('风和日丽', 250, 250)

 8. 绘制图片

     全图显示方法:ctx.drawImage(img,x,y)

         - img 为图片对象,采用new Image()方式

            - x,y 为图片左上角的坐标;

    裁剪显示方法:ctx.drawImage(img,xs,ys,sWidth,sHeight,x,y,width,heigth)

         - xs,ys,表示开始裁剪的位置坐标

         - sWidth,sHeight 表示裁剪的宽高

              - x,y 表示图片在画布上显示的位置坐标

              - width,height 表示要使用的图像的宽高(sheng)

    // 创建图片
    let img = new Image()
    img.src = './imgs/fengjing.jpeg'
    img.onload = function () {
      /**
       * 获取当前图片的属性,宽高
      */
      console.log(img.width, img.height);
      // ctx.drawImage(img, 100, 100)
      ctx.drawImage(img, 0, 0, 100, 100, 100, 100, 50, 50)
    }

9. 像素操作和像素数据

9.1 获取图片像素对象  getImageData(x,y,width,height)

        - x,y 表获取起始点坐标

        - width,height 表示宽高

      ctx.getImageData(x,y,w,h)返回一个对象,包含了所获取的像素块的像素内容,包含了 该块图像的每一个像素,每一个像素点信息 = rgba(0-255,0-255,0-255,0-255),前三个数据表示红绿蓝,第四个数据表示 透明度(0-255)

9.2 重新绘制获取的图片对象 putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight)

        - imgData 表示要放回画布的 ImageData 对象

        - x,y 表示 ImageData 对象左上角的x,y坐标,以像素点计算

        - dirtyX,dirtyY 可选。表示在画布上放置图像的位置

        - dirtyWidth,dirtyHeight 可选。表示在画布上绘制图像所使用的宽高

    // 1.创建图片
    let img = new Image();
    img.src = './imgs/fengjing.jpeg';
    img.onload = function () {
      // 绘制图片
      ctx.drawImage(img, 0, 0);

      let copy = ctx.getImageData(0, 0, 100, 100)
      console.log(copy);
      /*
      putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight)
        - imgData 表示要放回画布的 ImageData 对象
        - x,y 表示 ImageData 对象左上角的x,y坐标,以像素点计算
        - dirtyX,dirtyY 可选。表示在画布上放置图像的位置
        - dirtyWidth,dirtyHeight 可选。表示在画布上绘制图像所使用的宽高
    */
      ctx.putImageData(copy, 0, 650)// 可以在原图片上扣取一部分图片重新绘制到指定地方;
    }

二、动图案例

1. 小球的碰撞检测

重要点为:小球碰到边缘如何反弹,本质是小球的速度(也就是坐标的变化距离),当小球的坐标+半径和 到了边缘时,设置小球往方向跑

<!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>
    * {
      margin: 0;
      padding: 0;
    }
    canvas {
      margin: 50px auto;
      display: block;
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <canvas width="600" height="600">默认参数</canvas>
  <script>
    let c = document.querySelector('canvas')
    let ctx = c.getContext('2d')
    /* 
      小球运动的本质就是定时器重新绘制小球
    */
    let w = h = 600
    let x = 100;
    let y = 100
    let r = 20
    let xSpeed = 3;
    let ySpeed = 4
    setInterval(function () {
      ctx.clearRect(0, 0, w, h)
      // 小球坐标的变化
      drawCircle(x, y, r, 'red')
      if (x + r >= w || x - r <= 0) {
        xSpeed = -xSpeed;
      }
      x += xSpeed

      if (y + r >= h || y - r <= 0) {
        ySpeed = -ySpeed;
      }
      y += ySpeed

    }, 10)

    function drawCircle(x, y, r, color = '#000') {
      ctx.beginPath();
      ctx.arc(x, y, r, 0, Math.PI * 2);
      ctx.fillStyle = color;
      // ctx.fill()方法是实心着色
      ctx.fill()
      // 着色,不可缺,没有着色是不能看到的
      // ctx.stroke() 是空心着色
      // ctx.stroke();
      ctx.closePath();
    }
  </script>
</body>
</html>

2.面向对象的小球碰撞

面向对象的本质是对小球对象的封装,小球移动和绘制方法的封装;

<body>
  <canvas width="600" height="600">默认参数</canvas>
  <script>
    let c = document.querySelector('canvas')
    let ctx = c.getContext('2d')
    /* 
      小球运动的本质就是定时器重新绘制小球
    */
    let w = h = 600

    // 创建随机数
    function ranD(num) {
      return Math.random() * num
    }
    // 创建一个小球对象
    class Ball {
      constructor() {
        this.r = ranD(30) + 20;
        this.x = ranD(500) + 60;
        this.y = ranD(500) + 60;
        this.xSpeed = ranD(3) + 2;
        this.ySpeed = ranD(3) + 1;
        this.color = "#" + parseInt(ranD(1) * 0xffffff).toString(16)
      }
      // 小球显示的方法
      show() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
        ctx.fillStyle = this.color;
        ctx.fill()
        ctx.closePath();
      }

      // 小球运动的方法
      run() {
        if (this.x + this.r >= w || this.x - this.r <= 0) {
          this.xSpeed = -this.xSpeed;
        }
        this.x += this.xSpeed

        if (this.y + this.r >= h || this.y - this.r <= 0) {
          this.ySpeed = -this.ySpeed;
        }
        this.y += this.ySpeed
      }
    }
    // 创建小球
    let ballArr = []
    for (let i = 0; i < 20; i++) {
      var ball = new Ball()
      ballArr.push(ball)
      ball.show()
    }
    console.log(ballArr);

    // 小球运动
    setInterval(function () {
      ctx.clearRect(0, 0, w, h)
      for (let i = 0; i < ballArr.length; i++) {
        ballArr[i].show()
        ballArr[i].run()
      }
    }, 10)
  </script>
</body>

</html>

3.小球连线

小球之间连线的本质是 选取小球之间的圆心,不断动态重新绘制直线

<body>
  <canvas width="600" height="600">默认参数</canvas>
  <script>
    let c = document.querySelector('canvas')
    let ctx = c.getContext('2d')
    /* 
      小球运动的本质就是定时器重新绘制小球
    */
    let w = h = 600

    // 创建随机数
    function ranD(num) {
      return Math.random() * num
    }
    // 创建一个小球对象
    class Ball {
      constructor(text) {
        this.r = ranD(30) + 20;
        this.x = ranD(500) + 50;
        this.y = ranD(500) + 50;
        this.xSpeed = ranD(3) + 2;
        this.ySpeed = ranD(3) + 1;
        this.color = "#" + parseInt(ranD(1) * 0xffffff).toString(16);
        this.text = text;
      }
      // 画圆;小球显示的方法
      show() {
        drawCircle(this.x, this.y, this.r, this.color);// 画圆
        drawText(this.r, this.text, this.x + this.r, this.y, this.color);// 画文字
      }

      // 小球运动的方法
      run() {
        if (this.x + this.r >= w - 60 || this.x - this.r <= 0) {
          this.xSpeed = -this.xSpeed;
        }
        this.x += this.xSpeed

        if (this.y + this.r >= h || this.y - this.r <= 0) {
          this.ySpeed = -this.ySpeed;
        }
        this.y += this.ySpeed
      }
    }
    // 画直线
    function drawLine(x1, y1, x2, y2, color = "#000") {
      ctx.beginPath();
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.strokeStyle = color;
      ctx.stroke();
      ctx.closePath();
    }

    // 画字体
    function drawText(font, text, x, y, color = '#000') {
      ctx.font = `${font}px 宋体`;
      ctx.textAlign = 'left';
      ctx.textBaseline = 'middle';
      ctx.fillStyle = color;
      ctx.fillText(text, x, y, 60)
    }

    // 画圆
    function drawCircle(x, y, r, color) {
      ctx.beginPath();
      ctx.arc(x, y, r, 0, Math.PI * 2);
      ctx.fillStyle = color;
      ctx.fill()
      ctx.closePath();
    }

    // 文字数据
    let titleText = 'Java PHP JS GO Vue Angular jQuery CSS HTML Bootstrap webGL ThreeJs ElementUI UI AXIOS ajax'.split(' ')

    // 创建小球
    let ballArr = []
    for (let i = 0; i < 6; i++) {
      var ball = new Ball(titleText[i])
      ballArr.push(ball)
      ball.show()
    }
    console.log(ballArr);
    // 小球运动
    setInterval(function () {
      ctx.clearRect(0, 0, w, h)
      for (let i = 0; i < ballArr.length; i++) {
        ballArr[i].show()
        ballArr[i].run()

        /* 
          小球连线
        */
        for (let j = 0; j < i; j++) {
          drawLine(ballArr[i].x, ballArr[i].y, ballArr[j].x, ballArr[j].y, ballArr[i].color)
        }
      }
    }, 10)


  </script>
</body>

</html>

 4.跟随鼠标的炫彩炫彩小球

<body>
  <canvas>默认参数</canvas>
  <script>
    let c = document.querySelector('canvas')
    let ctx = c.getContext('2d')
    /* 
      小球运动的本质就是定时器重新绘制小球
    */
    let w = document.documentElement.clientWidth - 10
    let h = document.documentElement.clientHeight - 10
    c.width = w;
    c.height = h
    // 创建随机数
    function ranD(num) {
      return Math.random() * num
    }
    // 创建一个小球对象
    class Ball {
      constructor(x, y) {
        this.r = 60;
        this.x = x;
        this.y = y;
        this.color = "#" + parseInt(ranD(1) * 0xffffff).toString(16);
      }
      // 画圆;小球显示的方法
      show() {
        this.r--;// 小球直径减小
        if (this.r <= 0) return
        drawCircle(this.x, this.y, this.r, this.color);// 画圆
      }
    }

    // 画圆
    function drawCircle(x, y, r, color) {
      ctx.beginPath();
      ctx.arc(x, y, r, 0, Math.PI * 2);
      ctx.fillStyle = color;
      ctx.fill()
      ctx.closePath();
    }

    // 创建小球
    let ballArr = []
    // 小球的坐标跟着鼠标的位置而移动
    window.onmousemove = function (event) {
      console.log(event.x, event.y);
      var ball = new Ball(event.x, event.y)
      ballArr.push(ball)
      ball.show()
    }
    // 拿到小球对象集合
    console.log(ballArr);
    // 小球运动
    setInterval(function () {
      ctx.clearRect(0, 0, w, h)
      for (let i = 0; i < ballArr.length; i++) {
        ballArr[i].show()
      }
    }, 8)


  </script>
</body>

</html>

最后附上写的比较好的大佬的博客,感谢参考!!

http://t.csdn.cn/l1OK0

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值