最近对 html5
小游戏有点兴趣,因为我感觉将来这个东西或许是前端一个重要的应用场景,例如现在每到某些节假日,像支付宝、淘宝或者其他的一些 APP
可能会给你推送通知,然后点进去就是一个小游戏,基本上点进去的人,只要不是太抵触,都会玩上一玩的,如果要是恰好 get
到用户的 G
点,还能进一步增强业务,无论是用户体验,还是对业务的发展,都是一种很不错的提升方式。
另外,我说的这个 html5
小游戏是包括 WebGL
、WebVR
等在内的东西,不仅限于游戏,也可以是其他用到相关技术的场景,例如商品图片 360°
在线查看这种,之所以从小游戏入手,是因为小游戏需要的技术包罗万象,能把游戏做好,再用相同的技术去做其他的事情,就比较信手拈来了
查找资料,发现门道还是蛮多的,看了一圈下来,决定从基础入手,先从较为简单的 canvas
游戏看起,看了一些相关文章和书籍,发现这个东西虽然用起来很简单,但是真想用好,发挥其该有的能力还是有点难度的,最好从实战入手
于是最近准备写个 canvas
小游戏练手,相关 UI
素材已经搜集好了,不过俗话说 工欲善其事必先利其器,由于对这方面没什么经验,所以为了避免过程中出现的各种坑点,特地又看了一些相关的踩坑文章,其中性能我感觉是必须要注意的地方,而且门道很多,所以整理了一下
使用 requestNextAnimationFrame
进行动画循环
setTimeout
和 setInterval
并非是专为连续循环产生的 API
,所以可能无法达到流畅的动画表现,故用 requestNextAnimationFrame
,可能需要 polyfill
:
const raf = window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function(callback) {
window.setTimeout(callback, 1000 / 60)
}
利用剪辑区域来处理动画背景或其他不变的图像
如果只是简单动画,那么每一帧动画擦除并重绘画布上所有内容是可取的操作,但如果背景比较复杂,那么可以使用 剪辑区域技术,通过每帧较少的绘制来获得更好的性能
利用剪辑区域技术来恢复上一帧动画所占背景图的执行步骤:
- 调用
context.save()
,保存屏幕canvas
的状态 - 通过调用
beginPath
来开始一段新的路径 - 在
context
对象上调用arc()
、rect()
等方法来设置路径 - 调用
context.clip()
方法,将当前路径设置为屏幕canvas
的剪辑区域 - 擦除屏幕
canvas
中的图像(实际上只会擦除剪辑区域所在的这一块范围) - 将背景图像绘制到屏幕
canvas
上(绘制操作实际上只会影响剪辑区域所在的范围,所以每帧绘制图像像素数更少) - 恢复屏幕
canvas
的状态参数,重置剪辑区域
离屏缓冲区(离屏canvas)
先绘制到一个离屏 canvas
中,然后再通过 drawImage
把离屏 canvas
画到主 canvas
中,就是把离屏 canvas
当成一个缓存区。把需要重复绘制的画面数据进行缓存起来,减少调用 canvas
的 API
的消耗
const cacheCanvas = document.createElement('canvas')
const cacheCtx = cacheCanvas.getContext('2d')
cacheCtx.width = 200
cacheCtx.height = 200
// 绘制到主canvas上
ctx.drawImage(0, 0)
虽然离屏 canvas
在绘制之前视野内看不到,但其宽高最好设置得跟缓存元素的尺寸一样,避免资源浪费,也避免绘制多余的不必要图像,同时在 drawImage
时缩放图像也将耗费资源
必要时,可以使用多个离屏 canvas
另外,离屏 canvas
不再使用时,最好把手动将引用重置为 null
,避免因为 js
和 dom
之间存在的关联,导致垃圾回收机制无法正常工作,占用资源
尽量利用 CSS
背景图
如果有大的静态背景图,直接绘制到 canvas
可能并不是一个很好的做法,如果可以,将这个大背景图作为 background-image
放在一个 DOM
元素上(例如,一个 div
),然后将这个元素放到 canvas
后面,这样就少了一个 canvas
的绘制渲染
transform变幻
CSS
的