zoukankan      html  css  js  c++  java
  • canvas动画实战与性能优化

    插播一篇关于 canvas 动画及性能优化的文章,为我们可以更好的进入到 webgl 的世界奠定基础。

    本篇文章的内容可能会稍难理解,还希望大家有问题及时提出。闲话我们就不多说了,开始今天的正题吧。

    1. 动画实战

    首先介绍一下我们要实现的动画内容: 夜空中的流星源码

    今天就来跟大家详细分享一下如何进行编写 canvas 动画以及如何进行优化。

    1.1 搭建页面

    canvas的页面组成是非常简单的。如下所示:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>canvas动画和性能优化实战</title>
      <!-- 引入页面的样式 -->
      <link rel="stylesheet" href="./style/index.css">
    </head>
    <body>
      <canvas id="shooting-star">您的浏览器不支持canvas标签,请升级版本或选择其他浏览器</canvas>
    </body>
    </html>
    <!-- 引入页面的功能 -->
    <script src="./js/index.js"></script>
    

    创建一个 html 文件,并且引入 css 和 js 文件。

    这里有一个问题,之前我们跟大家分享的时候说,canvas标签的宽和高要在html中进行设置。但是为了适配我们的屏幕,就得用js来设置画布的宽和高。

    1.2 具体实现

    1. 首先创建一个流星的类。并添加一个启动的方法
    class ShootingStar{
      // 构造方法
      constructor() {}
      // 启动的方法
      start() {}
    }
    // 实例化一个对象,并启动
    new ShootingStar().start();
    
    2. 获取到 canvas 对象和绘图上下文,并设置 canvas 的宽和高

    注意:这里设置宽高是因为画布会出现模糊的现象。

    // 构造方法
    constructor() {
    	// 获取canvas对象
      this.ctx = document.getElementById('id名称');
      // 获取绘图上下文
      this.c = this.ctx.getContext('2d');
      // 获取当前页面的宽高
      this.maxW = document.body.offsetWidth;
      this.maxH = document.body.offsetHeight;
      
      // 定义一个数组,用来存放夜空中星,之后的开发中会用到
      this.skyList = [];
    }
    // 初始化画布的宽和高
    initCanvasSize() {
      this.ctx.width = this.maxW;
      this.ctx.height = this.maxH;
    }
    // 在起始方法中调用初始化函数
    start() {
      this.initCanvasSize();
    }
    
    3. 绘制背景

    这里我们没有引入图片,是模拟了一个星空的效果,略微简陋。

    // 绘制背景
    drawBackground() {
      // 夜空中星的数量
      const maxCount = 500;
      // 创建一个黑色的纯色背景
      this.c.fillRect(0, 0, this.ctx.width, this.ctx.height);
    
      // 创建随机坐标。在当前页面内随机
      for (let i = 0; i < maxCount; i++) {
        const r = ~~(Math.random() * 3);
        const x = ~~(Math.random() * this.maxW);
        const y = ~~(Math.random() * this.maxH);
    
        // 将随机坐标推入数组中
        this.skyList.push({
          x,y,r
        })
      }
    }
    
    4. 开启流星模式
    // 初始化背景,并重新开始绘制流星
    initBackground() {
      this.c.beginPath();
      this.c.fillStyle = 'rgba(0,0,0,0.2)';
      // 清空当前画布
      this.c.rect(0,0,this.maxW, this.maxH);
      this.c.fill();
      // 重新绘制背景
      this.drawStarList();
      // 重新开始绘制流星
      this.startShootingStar();
    }
    // 绘制流星
    drawStar(x, y) {
      this.drawStarList()
      this.c.beginPath();
      this.c.fillStyle = 'white';
      this.c.arc(x,y,2,0,Math.PI * 2);
      this.c.fill();
    }
    // 添加拖尾效果
    drawCover() {
      this.c.beginPath();
      this.c.fillStyle = 'rgba(0,0,0,0.06)';
      this.c.rect(0,0,this.ctx.width,this.ctx.height);
      this.c.fill();
    }
    // 开启流星模式
    startShootingStar() {
      // 设置x和y的运动速度
      let x = ~~(Math.random() * this.maxW);
      let y = 4;
      // 设置流星的颜色。
      this.c.fillStyle = 'darkorange';
      // 获取一个流星滑落的最大距离。到这个距离之后消失
      const clearY = Math.random() * this.maxH;
    
      // 绘制函数。
      const draw = () => {
        x -= 1;
        y += 4;
    
        // 如果当前滑落的距离大于最大距离,初始化当前背景。
        if (y >= clearY) {
          this.initBackground();
          return;
        }
    
    		// 绘制流星,传入当前的 x、y 坐标
        this.drawStar(x, y);
        // 此函数用来使流星有拖尾效果。
        this.drawCover();
        // 使用此函数实现动画效果
        requestAnimationFrame(draw);
      }
      draw()
    }
    

    到这里动画实战部分的内容就分享完了,有兴趣的同学可以查看下源码,也可以试着自己实现以下。

    2. 性能优化

    2.1 使用计算代替频繁的渲染

    绘制过程中,通常会使用计算来代替频繁的画布渲染。原理类似于 dom回流。因为 canvas 也是属于 dom 的部分,过多的操作会影响性能。当然,如果你使用一个特别消耗实现的算法的话就另当别论了。

    2.2 使用 requestAnimationFrame

    很多情况下,我们都习惯于使用 setInterval、setTimeout 来实现页面中的动画。也有很多小伙伴发现这种实现会出现丢帧的现象。原因有二:

    • setInterval、setTimeout 依赖于浏览器的异步循环,因而我们设定的动画执行时间可能并不是真正的动画执行时间,可能这个时候的 js引擎 正在执行其他代码,所以就会出现丢帧的情况。
    • 刷新频率收屏幕分辨率和屏幕尺寸影响。不同设备的屏幕刷新率可能不同。

    针对以上两点内容,我们使用 requestAnimationFrame 来优化动画实现。相对于前者,它有两点明显的优势

    • 由系统来决定回调的执行时机,在执行时浏览器会优化方法的调用。
    • 按帧进行重绘,通过 requestAnimationFrame 调用回调函数引起的页面重绘或回流的时间间 隔和显示器的刷新时间间隔相同。所以 requestAnimationFrame 不需要像 setTimeout 那样传递时间间隔,而是浏览器通过系统获取并使用显示器刷新频率

    2.3 离屏渲染

    离屏渲染可以理解为创建一个备用canvas但是不显示到页面上,执行预渲染操作。此项操作用到了drawImage 这个方法,此方法第一个参数可以接受一个图片对象或者是一个canvas 对象

    具体实现:

    将需要操作的内容在离屏的canvas上先处理好,之后再使用drawImage方法放入到显示层。

    2.4 分层画布

    也是使用多个canvas的方式,将静态的内容和需要频繁计算的内容分开渲染,越复杂的场景越适合使用此方法。具体实现如下

    示意图:

    通过这种方式可以将我们的需求拆分为多个模块。将需要频繁绘制的内容拆分出来,从而减少性能的消耗。

    性能优化方式主要是一些日常的注意点和拆分方式,并不是万能的。

    canvas的动画实现中,算法也占据了很大一部分,比如canvas中的粒子操作,动辄就是成千上万的像素点,算法的使用不当可能会带来很大的问题。

    好了,今天的分享就到这里了,临时插播的一条内容。接下来会分享关于 webgl 的内容,版兴趣的同学不要错过哟。Bye~

  • 相关阅读:
    atitit.ntfs ext 文件系统新特性对比
    Atitit.图片木马的原理与防范 attilax 总结
    Atitit.图片木马的原理与防范 attilax 总结
    Atitit.jdk java8的语法特性详解 attilax 总结
    Atitit.jdk java8的语法特性详解 attilax 总结
    Atitit.远程接口 监控与木马   常用的api 标准化v2 q216
    Atitit.远程接口 监控与木马   常用的api 标准化v2 q216
    Atitit..jdk java 各版本新特性 1.0 1.1 1.2 1.3 1.4 1.5(5.0) 1.6(6.0) 7.0 8.0 9.0 attilax 大总结
    Atitit..jdk java 各版本新特性 1.0 1.1 1.2 1.3 1.4 1.5(5.0) 1.6(6.0) 7.0 8.0 9.0 attilax 大总结
    Atitit.跨平台预定义函数 魔术方法 魔术函数 钩子函数 api兼容性草案 v2 q216  java c# php js.docx
  • 原文地址:https://www.cnblogs.com/yancyCathy/p/14128505.html
Copyright © 2011-2022 走看看