返回顶部
首页 > 资讯 > 前端开发 > VUE >如何优化HTML5 Canvas程序的性能
  • 194
分享到

如何优化HTML5 Canvas程序的性能

2024-04-02 19:04:59 194人浏览 八月长安
摘要

这篇文章主要介绍了如何优化HTML5 canvas程序的性能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何优化html5 Canvas程序的性能文章都会有所收获,下面我们一

这篇文章主要介绍了如何优化HTML5 canvas程序的性能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何优化html5 Canvas程序的性能文章都会有所收获,下面我们一起来看看吧。

  【使用缓存

  使用缓存也就是用离屏canvas进行预渲染了,原理很简单,就是先绘制到一个离屏canvas中,然后再通过drawImage把离屏canvas画到主canvas中。可能看到这很多人就会误解,这不是写游戏里面用的很多的双缓冲机制么?

  其实不然,双缓冲机制是游戏编程中为了防止画面闪烁,因此会有一个显示在用户面前的画布以及一个后台画布,进行绘制时会先将画面内容绘制到后台画布中,再将后台画布里的数据绘制到前台画布中。这就是双缓冲,但是canvas中是没有双缓冲的,因为现代浏览器基本上都是内置了双缓冲机制。所以,使用离屏canvas并不是双缓冲,而是把离屏canvas当成一个缓存区。把需要重复绘制的画面数据进行缓存起来,减少调用canvas的api的消耗。

  众所周知,调用canvas的API很消耗性能,所以,当我们要绘制一些重复的画面数据时,妥善利用离屏canvas对性能方面有很大的提升,可以看下下面的DEMO

  1 、 没使用缓存   

  2、  使用了缓存但是没有设置离屏canvas的宽高  

      3 、 使用了缓存但是没有设置离屏canvas的宽高  

  4 、 使用了缓存且设置了离屏canvas的宽高

  可以看到上面的DEMO的性能不一样,下面分析一下原因:为了实现每个圈的样式,所以绘制圈圈时我用了循环绘制,如果没用启用缓存,当页面的圈圈数量达到一定时,动画每一帧就要大量调用canvas的API,要进行大量的计算,这样再好的浏览器也会被拖垮啦。

XML/HTML Code复制内容到剪贴板

ctx.save();   

                        var j=0;   

                        ctx.lineWidth = borderWidth;   

                        for(var i=1;i<this.r;i+=borderWidth){   

                            ctx.beginPath();   

                            ctx.strokeStyle = this.color[j];   

                            ctx.arc(this.x , this.y , i , 0 , 2*Math.PI);   

                            ctx.stroke();   

                            j++;   

                        }   

                        ctx.restore();  

  所以,我的方法很简单,每个圈圈对象里面给他一个离屏canvas作缓存区。

  除了创建离屏canvas作为缓存之外,下面的代码中有一点很关键,就是要设置离屏canvas的宽度和高度,canvas生成后的默认大小是300X150;对于我的代码中每个缓存起来圈圈对象半径最大也就不超过80,所以300X150的大小明显会造成很多空白区域,会造成资源浪费,所以就要设置一下离屏canvas的宽度和高度,让它跟缓存起来的元素大小一致,这样也有利于提高动画性能。上面的四个demo很明显的显示出了性能差距,如果没有设置宽高,当页面超过400个圈圈对象时就会卡的不行了,而设置了宽高1000个圈圈对象也不觉得卡。

XML/HTML Code复制内容到剪贴板

var ball = function(x , y , vx , vy , useCache){   

                this.x = x;   

                this.y = y;   

                this.vx = vx;   

                this.vy = vy;   

                this.r = getZ(getRandom(20,40));   

                this.color = [];   

                this.cacheCanvas = document.createElement("canvas");   

                thisthis.cacheCtx = this.cacheCanvas.getContext("2d");   

                this.cacheCanvas.width = 2*this.r;   

                this.cacheCanvas.height = 2*this.r;   

                var num = getZ(this.r/borderWidth);   

                for(var j=0;j<num;j++){   

                    this.color.push("rgba("+getZ(getRandom(0,255))+","+getZ(getRandom(0,255))+","+getZ(getRandom(0,255))+",1)");   

                }   

                this.useCache = useCache;   

                if(useCache){   

                    this.cache();   

                }   

            }  

当我实例化圈圈对象时,直接调用缓存方法,把复杂的圈圈直接画到圈圈对象的离屏canvas中保存起来。

XML/HTML Code复制内容到剪贴板

cache:function(){   

                    this.cacheCtx.save();   

                    var j=0;   

                    this.cacheCtx.lineWidth = borderWidth;   

                    for(var i=1;i<this.r;i+=borderWidth){   

                        this.cacheCtx.beginPath();   

                        thisthis.cacheCtx.strokeStyle = this.color[j];   

                        this.cacheCtx.arc(this.r , this.r , i , 0 , 2*Math.PI);   

                        this.cacheCtx.stroke();   

                        j++;   

                    }   

                    this.cacheCtx.restore();   

                }  

然后在接下来的动画中,我只需要把圈圈对象的离屏canvas画到主canvas中,这样,每一帧调用的canvasAPI就只有这么一句话:

XML/HTML Code复制内容到剪贴板

ctx.drawImage(this.cacheCanvas , this.x-this.r , this.y-this.r);  

跟之前的for循环绘制比起来,实在是快太多了。所以当需要重复绘制矢量图的时候或者绘制多个图片的时候,我们都可以合理利用离屏canvas来预先把画面数据缓存起来,在接下来的每一帧中就能减少很多没必要的消耗性能的操作。

下面贴出1000个圈圈对象流畅版代码:

XML/HTML Code复制内容到剪贴板

<!doctype html>  

<html lang="en">  

<head>  

    <meta charset="UTF-8">  

    <style>  

        body{   

            padding:0;   

            margin:0;   

            overflow: hidden;   

        }   

        #cas{   

            display: block;   

            background-color:rgba(0,0,0,0);   

            margin:auto;   

            border:1px solid;   

        }   

    </style>  

    <title>测试</title>  

</head>  

<body>  

    <div >  

        <canvas id='cas' width="800" height="600">浏览器不支持canvas</canvas>  

        <div style="text-align:center">1000个圈圈对象也不卡</div>  

    </div>  

    <script>  

        var testBox = function(){   

            var canvas = document.getElementById("cas"),   

                ctx = canvas.getContext('2d'),   

                borderWidth = 2,   

                Balls = [];   

            var ball = function(x , y , vx , vy , useCache){   

                this.x = x;   

                this.y = y;   

                this.vx = vx;   

                this.vy = vy;   

                this.r = getZ(getRandom(20,40));   

                this.color = [];   

                this.cacheCanvas = document.createElement("canvas");   

                thisthis.cacheCtx = this.cacheCanvas.getContext("2d");   

                this.cacheCanvas.width = 2*this.r;   

                this.cacheCanvas.height = 2*this.r;   

                var num = getZ(this.r/borderWidth);   

                for(var j=0;j<num;j++){   

                    this.color.push("rgba("+getZ(getRandom(0,255))+","+getZ(getRandom(0,255))+","+getZ(getRandom(0,255))+",1)");   

                }   

                this.useCache = useCache;   

                if(useCache){   

                    this.cache();   

                }   

            }   

            function getZ(num){   

                var rounded;   

                rounded = (0.5 + num) | 0;   

                // A double bitwise not.   

                rounded = ~~ (0.5 + num);   

                // Finally, a left bitwise shift.   

                rounded = (0.5 + num) << 0;   

                return rounded;   

            }   

            ball.prototype = {   

                paint:function(ctx){   

                    if(!this.useCache){   

                        ctx.save();   

                        var j=0;   

                        ctx.lineWidth = borderWidth;   

                        for(var i=1;i<this.r;i+=borderWidth){   

                            ctx.beginPath();   

                            ctx.strokeStyle = this.color[j];   

                            ctx.arc(this.x , this.y , i , 0 , 2*Math.PI);   

                            ctx.stroke();   

                            j++;   

                        }   

                        ctx.restore();   

                    } else{   

                        ctx.drawImage(this.cacheCanvas , this.x-this.r , this.y-this.r);   

                    }   

                },   

                cache:function(){   

                    this.cacheCtx.save();   

                    var j=0;   

                    this.cacheCtx.lineWidth = borderWidth;   

                    for(var i=1;i<this.r;i+=borderWidth){   

                        this.cacheCtx.beginPath();   

                        thisthis.cacheCtx.strokeStyle = this.color[j];   

                        this.cacheCtx.arc(this.r , this.r , i , 0 , 2*Math.PI);   

                        this.cacheCtx.stroke();   

                        j++;   

                    }   

                    this.cacheCtx.restore();   

                },   

                move:function(){   

                    this.x += this.vx;   

                    this.y += this.vy;   

                    if(this.x>(canvas.width-this.r)||this.x<this.r){   

                        thisthis.x=this.x<this.r?this.r:(canvas.width-this.r);   

                        this.vx = -this.vx;   

                    }   

                    if(this.y>(canvas.height-this.r)||this.y<this.r){   

                        thisthis.y=this.y<this.r?this.r:(canvas.height-this.r);   

                        this.vy = -this.vy;   

                    }   

                    this.paint(ctx);   

                }   

            }   

            var Game = {   

                init:function(){   

                    for(var i=0;i<1000;i++){   

                        var b = new ball(getRandom(0,canvas.width) , getRandom(0,canvas.height) , getRandom(-10 , 10) ,  getRandom(-10 , 10) , true)   

                        Balls.push(b);   

                    }   

                },   

                update:function(){   

                    ctx.clearRect(0,0,canvas.width,canvas.height);   

                    for(var i=0;i<Balls.length;i++){   

                        Balls[i].move();   

                    }   

                },   

                loop:function(){   

                    var _this = this;   

                    this.update();   

                    RAF(function(){   

                        _this.loop();   

                    })   

                },   

                start:function(){   

                    this.init();   

                    this.loop();   

                }   

            }   

            window.RAF = (function(){   

                return window.requestAnimationFrame || window.WEBkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {window.setTimeout(callback, 1000 / 60); };   

            })();   

            return Game;   

        }();   

        function getRandom(a , b){   

            return Math.random()*(b-a)+a;   

        }   

        window.onload = function(){   

            testBox.start();   

        }   

    </script>  

</body>  

</html>  

  离屏canvas还有一个注意事项,如果你做的效果是会将对象不停地创建和销毁,请慎重使用离屏canvas,至少不要像我上面写的那样给每个对象的属性绑定离屏canvas。

  因为如果这样绑定,当对象被销毁时,离屏canvas也会被销毁,而大量的离屏canvas不停地被创建和销毁,会导致canvas buffer耗费大量GPU资源,容易造成浏览器崩溃或者严重卡帧现象。解决办法就是弄一个离屏canvas数组,预先装进足够数量的离屏canvas,仅将仍然存活的对象缓存起来,当对象被销毁时,再解除缓存。这样就不会导致离屏canvas被销毁了。

 【使用requestAnimationFrame】

  这个就不具体解释了,估计很多人都知道,这个才是做动画的最佳循环,而不是setTimeout或者setInterval。直接贴出兼容性写法:

XML/HTML Code复制内容到剪贴板

window.RAF = (function(){   

       return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {window.setTimeout(callback, 1000 / 60); };   

            })();   

  【避免浮点运算】

  虽然javascript提供了很方便的一些取整方法,像Math.floor,Math.ceil,parseInt,但是,国外友人做过测试,parseInt这个方法做了一些额外的工作(比如检测数据是不是有效的数值,parseInt 甚至先将参数转换成了字符串!),所以,直接用parseInt的话相对来说比较消耗性能,那怎样取整呢,可以直接用老外写的很巧妙的方法了:

JavaScript Code复制内容到剪贴板

1.rounded = (0.5 + somenum) | 0;      

2.rounded = ~~ (0.5 + somenum);   3.rounded = (0.5 + somenum) << 0;      

  【尽量减少canvasAPI的调用】

  作粒子效果时,尽量少使用圆,最好使用方形,因为粒子太小,所以方形看上去也跟圆差不多。至于原因,很容易理解,我们画一个圆需要三个步骤:先beginPath,然后用arc画弧,再用fill进行填充才能产生一个圆。但是画方形,只需要一个fillRect就可以了。虽然只是差了两个调用,当粒子对象数量达到一定时,这性能差距就会显示出来了。

  还有一些其他注意事项,我就不一一列举了,因为谷歌上一搜也挺多的。我这也算是一个给自己做下记录,主要是记录缓存的用法。想要提升canvas的性能最主要的还是得注意代码的结构,减少不必要的API调用,在每一帧中减少复杂的运算或者把复杂运算由每一帧算一次改成数帧算一次。同时,上面所述的缓存用法,我因为贪图方便,所以是每个对象一个离屏canvas,其实离屏canvas也不能用的太泛滥,如果用太多离屏canvas也会有性能问题,请尽量合理利用离屏canvas。

关于“如何优化HTML5 Canvas程序的性能”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“如何优化HTML5 Canvas程序的性能”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网VUE频道。

--结束END--

本文标题: 如何优化HTML5 Canvas程序的性能

本文链接: https://lsjlt.com/news/90378.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何优化HTML5 Canvas程序的性能
    这篇文章主要介绍了如何优化HTML5 Canvas程序的性能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何优化HTML5 Canvas程序的性能文章都会有所收获,下面我们一...
    99+
    2024-04-02
  • 如何利用缓存来优化HTML5 Canvas程序的性能
    这篇文章主要介绍“如何利用缓存来优化HTML5 Canvas程序的性能”,在日常操作中,相信很多人在如何利用缓存来优化HTML5 Canvas程序的性能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操...
    99+
    2024-04-02
  • 如何优化JavaScript程序的性能
    如何优化JavaScript程序的性能,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。避免全局查找在一个函数中会用到全局对象存储为局部变量来减少...
    99+
    2024-04-02
  • wpf程序性能如何优化
    WPF程序的性能优化可以从以下几个方面进行:1. 减少UI元素的数量和复杂度:尽量减少窗口中的控件数量,避免使用大量嵌套的控件和复杂...
    99+
    2023-10-18
    wpf
  • 微信小程序性能如何优化
    这篇文章主要介绍了微信小程序性能如何优化的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇微信小程序性能如何优化文章都会有所收获,下面我们一起来看看吧。为什么要做性能优化?一切性能优化都是为了体验优化1. 使用小程...
    99+
    2023-06-26
  • 如何优化Vue js应用程序的性能
    这篇文章主要为大家展示了“如何优化Vue js应用程序的性能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何优化Vue js应用程序的性能”这篇文章吧。一、介绍VueJS 是开发网站最受欢迎、...
    99+
    2023-06-22
  • PHP 应用程序性能优化中 Composer 的性能优化技巧
    composer 优化技巧:禁用自动加载,避免在编译时加载不必要的类。使用 composer optimize 命令生成优化文件,减少加载类的时间。限制依赖项更新,避免频繁重新生成锁定文...
    99+
    2024-05-01
    php composer 内存占用
  • iOS程序性能优化的技巧
    1. 用ARC管理内存 ARC(Automatic ReferenceCounting, 自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内...
    99+
    2022-05-15
    ios 程序 性能 优化
  • 怎么优化void程序的性能
    这篇文章主要介绍“怎么优化void程序的性能”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么优化void程序的性能”文章能帮助大家解决问题。我们的编译器已经提供了很好的优化机制,但是还有很多细节编...
    99+
    2023-06-04
  • 如何利用 Python 中的索引优化程序性能?
    Python 是一种高级编程语言,虽然它非常简单易学,但在处理大规模数据时,程序的性能往往会成为一个瓶颈。因此,我们需要使用一些优化技巧来提高程序性能。其中之一就是利用 Python 中的索引。 索引是一个指向存储在内存中的数据结构的指针。...
    99+
    2023-10-20
    索引 日志 面试
  • 如何利用numpy优化ASP应用程序的性能?
    在ASP应用程序中,性能优化一直是一个非常关键的问题。numpy是一个强大的数学库,可以帮助优化ASP应用程序的性能。本文将介绍如何利用numpy来优化ASP应用程序的性能。 一、numpy简介 numpy是一个基于Python的科学计算库...
    99+
    2023-11-07
    异步编程 自然语言处理 numpy
  • 如何使用C++优化Web应用程序的性能?
    优化web应用程序性能的c++++技术:使用现代编译器和优化标志避免动态内存分配最小化函数调用利用多线程使用高效的数据结构实战案例显示:优化技术可显著提升性能:执行时间减少20%内存开销...
    99+
    2024-05-12
    c++ 性能优化 并发请求
  • 如何优化Go语言Websocket应用程序性能
    在现代实时 Web 应用程序开发中,WebSocket 已经成为了一种非常受欢迎的协议。 在使用 WebSocket 编写应用程序时,我们需要考虑它的性能优化,以确保我们的应用程序能够快速和准确地响应客户端请求。在本文中,我们将讨论如何优化...
    99+
    2023-12-18
    Go语言 websocket 优化性能
  • 如何进行跨平台C++程序性能优化?
    在进行跨平台 c++++ 程序性能优化时,可以采取以下策略:使用特定平台的编译器标记;优化数据结构和算法;利用并行化技术。 如何在进行跨平台 C++ 程序性能优化 在开发跨平台 C++...
    99+
    2024-05-08
    跨平台 c++优化 linux c++
  • HTML5 Canvas的性能提高技巧
    这篇文章主要讲解了“HTML5 Canvas的性能提高技巧”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“HTML5 Canvas的性能提高技巧”吧! ...
    99+
    2024-04-02
  • 如何优化 Golang 协程性能?
    优化协程性能的方法包括:1. 限制协程数量以防止资源消耗;2. 使用管道实现协程通信,避免数据竞争;3. 减少锁竞争,使用非阻塞锁或 waitgroup 机制同步协程。 如何优化 Go...
    99+
    2024-05-21
    golang 协程
  • 小程序redux性能优化的方法
    这篇文章主要介绍“小程序redux性能优化的方法”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“小程序redux性能优化的方法”文章能帮助大家解决问题。首先了解小程序的工作原理和性能关键点。1工作原理...
    99+
    2023-06-26
  • 如何使用PATH API优化PHP应用程序的性能?
    PHP是一种广泛使用的编程语言,它以其易于学习和使用的特性而受到开发人员的欢迎。然而,在处理大量请求时,PHP应用程序的性能可能会受到影响。在这种情况下,可以使用PATH API来优化PHP应用程序的性能。 PATH API是PHP的一个扩...
    99+
    2023-10-25
    开发技术 path api
  • Apache 服务器如何优化 Django 应用程序的性能?
    Django 是一个流行的 Python Web 框架,它提供了许多强大的功能,可以帮助开发者快速地构建高性能的 Web 应用程序。然而,为了让 Django 应用程序具有更好的性能,我们需要对其进行优化。在本文中,我们将介绍如何使用 A...
    99+
    2023-08-09
    并发 apache django
  • Apache服务器如何优化ASP应用程序的性能?
    Apache服务器是一款广泛使用的Web服务器软件,由于其高效、稳定和可靠,成为了Web服务器领域的领导者。然而,在处理ASP应用程序时,Apache服务器的性能可能会受到影响。本文将介绍如何优化ASP应用程序的性能,以提高Apache服...
    99+
    2023-11-11
    编程算法 对象 apache
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作