一文学会 canvas

长久以来,web上的动画都是Flash。比如动画广告、游戏等等,基本上都是Flash实现的。Flash是有缺点的,比如需要安装Adobe Flash Player,漏洞多,重量比较大。卡顿和不流畅等等。

HTML5提出了一个新的canvas标签,彻底颠覆了Flas 的主导地位。无论是广告、游戏都可以使用canvas实现了,Canvas是一个轻量级的画布,我们使用Canvas进行JavaScript的编程,不需要增加额外的插件,性能也很好,不卡顿,在手机中也很流畅

1.canvas 基本概念

1.1 canvas 的定义

< canvas > 是h5 新增的标签,它只是图形的容器,也称为画布。
在画布上进行绘制,那么你就需要使用脚本语言(通常是JavaScript)来进行绘制图形。

1.2 canvas 能做什么

  • 基础图形的绘制
  • 文字的绘制
  • 图形的变形和图片的合成
  • 图片和视频的处理
  • 动画的实现
  • 小游戏的制作

1.3 支持canvas 的浏览器

IE 9、Firefox、Opera、Chrome 和 Safari 支持 标签。

注释:IE 8 或更早版本的 IE 浏览器不支持 标签。在这里插入图片描述

1.4 如何判断浏览器是否支持

<canvas id="myCanvas">浏览器不支持canvas</canvas>

如果浏览器不支持canvas标签,里面的文字就会显示出来

2.canvas 基本使用

https://www.runoob.com/html/html5-canvas.html 这是各种规则

3.canvas 总结

3.1 使用总结

  • 先创建画布
  • 使用你需要图形模板语法
  • 要管理好你的图形,否则不小心会出现覆盖现象

3.2 关于beginPath

beginPath方法定义:开始一条路径,或重置当前的路径。
  • canvas中的绘制方法(如stroke, fill),遇到stroke 方法, 都会以“上一次beginPath”之后的所有路径为基础进行绘制。
  • 不管你用moveTo把画笔移动到哪里,只要不重新beginPath,那你一直都是在画一条路径(注:此处『一条路径』并非指连在一起)。所以每次使用stroke方法,都是把**“上一次beginPath”**之前的路径当前颜色的画笔再画一下。
    例如以下代码块,先开始画出第一条红色线。没有遇见beginPath, 但是再次遇见stroke,造成用设置为绿色的笔再次描绘一次第一条红色线。每次都是如此,直到遇见下个beginPath 结束。
    context.beginPath();

    context.moveTo(100,100);
    context.lineTo(200,100);
    context.strokeStyle = "red";
    context.stroke();


    context.moveTo(100,200);
    context.lineTo(200,200);
    context.strokeStyle = "green";
    context.stroke();

    context.beginPath();
    context.moveTo(100,300);
    context.lineTo(200,300);
    context.strokeStyle = "yellow";
    context.stroke();

3.3 关于save 方法和 restore 方法 的定义和总结

  • save()保存画布的所有状态。每次保存都相当于压栈,将当前保存的状态为画布状态
  • restore()恢复 canvas状态的。将栈里面的状态释放,选取你压入的状态,或者(多次调用)一步步返回到初始化状态。

可能保存的状态有:

当前应用的变形(即移动,旋转和缩放,见下) 以及下面这些属性:strokeStyle, fillStyle, globalAlpha,
lineWidth, lineCap, lineJoin, miterLimit, lineDashOffset,
shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor,
globalCompositeOperation, font, textAlign, textBaseline, direction,
imageSmoothingEnabled

3.4 属性的叠加(只有转换方法会出现

涉及以下转换方法

  • scale() 缩放当前绘图至更大或更小。
  • rotate() 旋转当前绘图。
  • translate() 重新映射画布上的 (0,0) 位置。
  • transform() 替换绘图的当前转换矩阵。
  • setTransform() 将当前转换重置为单位矩阵。然后运行 transform()。

setTransform() 将当前转换重置为单位矩阵。然后运行 transform()。
例如4.3 滚动车轮实战,如果你不恢复初始画布状态,那么translate属性会一直叠加,直到往下跑出画布。其他全部属性也是这样。例如下面的scale属性。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
您的浏览器不支持 HTML5 canvas 标签。
</canvas>
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.strokeRect(5,5,25,15);
ctx.scale(2,2);
ctx.strokeRect(5,5,25,15);
	ctx.scale(2,2);
	ctx.strokeRect(5,5,25,15);
</script> 

</body>
</html>

3.5 canvas中 translate rotate 执行顺序问题

执行顺序是谁在前面,谁后执行。像队列里面的出对入队

3.6 beginPath 和 save区别

beginPath 是从新开始一条新线,只是路径的方法。使用的环境状态还是当前画布设置的环境状态
save 方法是对各种环境变量的保存,保存当前画布环境状态

4.实战

4.1 碰壁反弹小球

功能需求: 随机出现小球,且碰到见墙壁要进行反弹
实现思路:

  1. 需要创建小球的实体类,这样每个小球都有自己专有属性和方法
  2. 初始化实体类方法:渲染小球出现
  3. 更新小球路径方法:渲染小球出现的位置,以及判断是否需要折返
  4. 每次更新都需要清空画布,更新小球位置,渲染小球出现

在这里插入图片描述

<body>
  <canvas id='can' style="border: 1px solid black;"></canvas>
  <style>
    * {
      margin: 0px;
      padding: 0px;
    }

    html,
    body {
      width: 100%;
      height: 100%;
    }
  </style>

  <script>
    let vas = document.getElementById('can')
    console.log('document.clientWidth', document.body.clientWidth)
    vas.width = document.body.clientWidth - 20
    vas.height = document.body.clientHeight - 10
    var ctx = vas.getContext("2d");

    let ballArr = []
    // 设置球
    class Ball {
      constructor(x, y, r, xstep, ystep) {
        this.x = x
        this.y = y
        this.r = r
        this.xstep = xstep
        this.ystep = ystep
      }
      // 显示球
      init() {
        ctx.beginPath()
        ctx.fillStyle = "#FF0000";
        ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
        ctx.fill()
      }
      // 运动
      update() {
        this.x += this.xstep
        this.y += this.ystep
        if (this.x < this.r || this.x > (vas.width - 5)) {
          this.xstep = - this.xstep
        }
        if (this.y < this.r || this.y > (vas.height - 5)) {
          this.ystep = - this.ystep
        }
      }
    }

    function init() {
      for (let index = 0; index < 20; index++) {
        let x = Number((Math.random()) * 500 + 5)
        let y = Number((Math.random()) * 500 + 5)
        let r = 5
        let xstep = Number((Math.random()) * 10 - 5)
        let ystep = Number((Math.random()) * 10 - 5)
        let ball = new Ball(x, y, r, xstep, ystep)
        ball.init()
        ballArr.push(ball)
      }

    }
    init()
    setInterval(() => {
      ctx.clearRect(0, 0, vas.width, vas.height)
      for (let index = 0; index < ballArr.length; index++) {
        ballArr[index].update()
        ballArr[index].init()
      }
    }, 10)

  </script>

</body>

4.2 小球连线

功能需求: 随机出现小球,碰到见墙壁要进行反弹,小球在一定的距离内连线
实现思路:

  1. 需要创建小球的实体类,这样每个小球都有自己专有属性和方法
  2. 初始化实体类方法:渲染小球出现
  3. 更新小球路径方法:渲染小球出现的位置,以及判断是否需要折返
  4. 判断小球连线方法:需要从当前位置进行循环来判断,全部小球循环,否则出现两个线
  5. 每次更新都需要清空画布,更新小球位置,进行判断是否需要连线,渲染小球出现
    在这里插入图片描述
<body>
  <canvas id='can' style="border: 1px solid black;"></canvas>
  <style>
    * {
      margin: 0px;
      padding: 0px;
    }

    html,
    body {
      width: 100%;
      height: 100%;
    }
  </style>

  <script>
    let vas = document.getElementById('can')
    console.log('document.clientWidth', document.body.clientWidth)
    vas.width = document.body.clientWidth - 20
    vas.height = document.body.clientHeight - 10
    var ctx = vas.getContext("2d");

    let ballArr = []
    // 设置球
    class Ball {
      constructor(x, y, r, xstep, ystep) {
        this.x = x
        this.y = y
        this.r = r
        this.xstep = xstep
        this.ystep = ystep
        // 获取当前
        this.index = ballArr.length
      }
      // 显示球
      init() {
        ctx.beginPath()
        ctx.fillStyle = "red";
        ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
        ctx.fill()
      }
      // 运动
      update() {
        this.x += this.xstep
        this.y += this.ystep
        if (this.x < this.r || this.x > (vas.width - 5)) {
          this.xstep = - this.xstep
        }
        if (this.y < this.r || this.y > (vas.height - 5)) {
          this.ystep = - this.ystep
        }
      }
      // 小球连线
      line() {

        for (let temp = this.index; temp < ballArr.length; temp++) {
          if (Math.abs(this.x - ballArr[temp].x) < 150 && Math.abs(this.y - ballArr[temp].y) < 150) {
            ctx.strokeStyle = `rgba(255,0,0,${10 / (Math.sqrt(Math.pow(this.x - ballArr[temp].x, 2) + Math.pow(this.y - ballArr[temp].y, 2)))})`
            ctx.beginPath()
            ctx.moveTo(this.x, this.y)
            ctx.lineTo(ballArr[temp].x, ballArr[temp].y)
            ctx.stroke()
          }
        }

      }
    }

    function init() {
      for (let index = 0; index < 30; index++) {
        let x = (Math.random()) * 500 + 5
        let y = (Math.random()) * 500 + 5
        let r = 5
        let xstep = (Math.random()) * 10 - 5
        let ystep = (Math.random()) * 10 - 5
        let ball = new Ball(x, y, r, xstep, ystep)
        ball.init()
        ballArr.push(ball)
      }

    }
    init()
    setInterval(() => {
      ctx.clearRect(0, 0, vas.width, vas.height)
      // 先全部位置确定,再进行连线显示
      for (let index = 0; index < ballArr.length; index++) {
        ballArr[index].update()
      }
      for (let index = 0; index < ballArr.length; index++) {
        ballArr[index].line()
        ballArr[index].init()
      }
    }, 10)

  </script>

</body>

4.3 滚动车轮

功能需求: 车轮往前行走
实现思路:

  1. 使用rotate,车轮以中心点滚动
  2. 谁用translate,滚动后平移
  3. 使用save,restore方法,回到初始化属性设置,消除旋转平移的影响

涉及到的问题:

  1. 变换属性会叠加问题: 一直设置变换属性,这个时候是一直叠加的,需要通过save,restore 恢复到未设置之前。
  2. translate, rotate 执行顺序问题:谁在前面反而后执行
  3. canvas 绘制完毕,图片还没加载成功问题:所以使用onload 当图片加载 成功再绘制
  4. rotate 旋转围绕的圆心:是以画布的左上角为圆心旋转
    在这里插入图片描述
<body>
    <style>
        canvas {
            border: 1px solid #000;
        }
    </style>
    <canvas width="1200" height="500"></canvas>
    <script>
        var canvas = document.querySelector("canvas");
        var ctx = canvas.getContext("2d");

        var image = new Image();
        image.src = "images/lunzi.png";
        image.onload = function() {
            // 定时器
            // 旋转的度数
            var deg = 0;
            // 位置
            var x = 200;
            setInterval(function() {
                ctx.clearRect(0, 0, canvas.width, canvas.height)
                deg += 0.01;
                x++;
                // 备份
                ctx.save();
                // 平移
                ctx.translate(x, 200);
                // 旋转
                ctx.rotate(deg);
                ctx.drawImage(image, -374 / 2, -374 / 2);
                // 恢复
                ctx.restore();
            }, 10)
        }
    </script>
</body>

4.4 刮刮乐实现

功能需求:挂出奖

实现思路:

  1. 将文字隐藏在canvas 下面
  2. beginPath 开始画,并消除绘画的影响
  3. arc进行圆形返回消除
  4. 按下移动就涂掉,松开就停止涂掉

在这里插入图片描述

<body>
    <style>
        div {
            border: 1px solid #000;
            width: 250px;
            height: 60px;
            font-size: 40px;
            line-height: 60px;
            text-align: center;
            position: relative;
            user-select: none;
        }
        
        canvas {
            position: absolute;
            left: 0;
            top: 0;
        }
    </style>
    <div>
        特等奖
        <canvas width="250" height="60"></canvas>
    </div>
    <script>
        var canvas = document.querySelector("canvas");
        var ctx = canvas.getContext("2d");

        ctx.fillStyle = "#333";
        ctx.fillRect(0, 0, 250, 60);
        // 设置新画上的元素,实际上就是擦除之前的元素
        ctx.globalCompositeOperation = "destination-out";

        // 鼠标按下
        canvas.onmousedown = function() {
            // 拖动
            canvas.onmousemove = function(event) {
                // 画圆
                ctx.beginPath();
                ctx.arc(event.offsetX, event.offsetY, 10, 0, 7, false);
                ctx.fill()
            }
        }
        canvas.onmouseup = function() {
            canvas.onmousemove = function() {}
        }
    </script>
</body>
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值