面向对象,春暖花开
——茂叔
文字渲染的优化
上一篇,我们实现了文字渲染。但是,细心的朋友应该发现,尽管我们可以渲染出文字,但整个过程非常的耗时。
首先,我们需要根据内码去获得文字的区位码,这一过程需要去解析一个非常长的字符串。想想都觉得累。
其次,我们获得了区位码后,需要根据一串字节去逐位画点,也就是说,我们渲染一个汉字实际上画了很多个点,系统消耗非常大。
据实际测试,在一台性能不错的笔记本上,渲染5000个汉字,居然耗时300毫秒以上。作为游戏,这是不可接受的,一秒3帧,还玩个P啊!
因此,我们需要对文字的渲染算法进行优化。
既然实事解析内码去获得区位码非常耗时,那我们可以把内码与区位码之间的关系放进一个Map对象,通过映射关系快速查找到内码所对应的区位码。
对于逐一描点这种累人的活,我们可以提前将所有汉字点都描出来,在运行时只需要根据区位码找到对应的图像,然后截取出来就可以了。
于是,我们对han.js做了一些“小”改动:
var fontImg = wx.createImage()
exports.install = (callback) => {
fontImg.src = "images/han.png";
fontImg.onload = callback
}
GameGlobal.drawText = (ctx, str, sx, sy) => {
var cx = 0
for (var i = 0; i < str.length; i++) {
var charCode = str.charCodeAt(i)
var quwei = CodeMap.get(charCode)
ctx.drawImage(fontImg, quwei.wei * 12, quwei.qu * 12, 12, 12, sx + cx, sy, 12, 12)
cx += 14
}
}
var CodeMap = new Map([[12288, {
"qu": 0, "wei": 0 }], [12289, {
"qu": 0, "wei": 1 }], [12290, {
"qu": 0, "wei": 2 }], [183, {
"qu": 0, "wei": 3 }], [713, {
"qu": 0, "wei": 4 }], [711, {
……
//代码不齐,太长了,两次让我的浏览器停止响应,所以请到GitHub去下载完整代码吧,我也不想这样
其中,CodeMap是根据那个很长很长的字符串解析出来的,具体解析过程略……重点就是每个内码对应一个区位对象。
而han.png是按区位码排列的8000多个汉字点阵图,居然比二进制字库文件还小……

每行94个字符,一共94行,正好对应区位码。
所以,使用
ctx.drawImage(fontImg, quwei.wei * 12, quwei.qu * 12, 12, 12, sx + cx, sy, 12, 12)
这一句就很方便的把图中对应区位码的图像画到我们需要的地方了。
经过这两个修改,我们渲染5000个汉字的时间只需要不到50毫秒,嗯20帧,凑活玩了。
实际使用中,以我们的界面大小,不会有一帧超过200个汉字的需求,而如果渲染200个汉字,只需要5毫秒,一秒200帧,愉快了……
按钮?窗体?
Windows系列系统中,窗体、按钮、控件什么的,都是从一个基类派生出来的,有完善的消息机制和渲染策略。
我们的小程序中也可以采用这样的策略来组织我们的界面。
具体的思路是,一切需要渲染到设备上的界面元素,从主界面到一个标签、一个按钮、都从一个基本渲染对象派生而来,封装好的对象可以在写码时方便的实例化,从而使界面代码更加有序和高效。
首选,我们在根路径新建一个UIOBJECT,js文件,在里面定义一个基本对象:
function baseUIObj(father, x = 0, y = 0, w = 0, h = 0){
this.father = father || undefined
this.x = x
this.y = y
this.width = w
this.height = h
}
这个对象作为我们所有界面元素的基类。
我们只需要使用:
var newUIObj=new baseUIObj()
console.log(newUIObj.width)//输出:0
var newUIObj2=new baseUIObj(newUIObj,0,0,100,30)
console.log(newUIObj2.width)//输出:100
就可以获得这样一个界面元素,创建时可以输入它的父元素,或者说容器,以及在父元素中位置和尺寸。
然后添加它自己的canvas对象:
this.canvas = wx.createCanvas()
this.canvas.width = w
this.canvas.height = h
this.gCtx = this.canvas.getContext('2d')
在定义一个渲染的方法:
baseUIObj.prototype.redraw = function(caller) {
if (caller.isTouch)
caller.gCtx.fillStyle = caller.colorOnTouch
else
caller.gCtx.fillStyle = caller.color
caller.gCtx.fillRect(0, 0, caller.width, caller.height)
if (caller.background)
caller.gCtx.drawImage(caller.background, 0, 0, caller.width, caller.height)
if (caller.backgroundTouch&&caller.isTouch)
caller.gCtx.drawImage(caller.backgroundTouch, 0, 0, caller.width, caller.height)
if (caller.borderWidth > 0) {
caller.gCtx.strokeStyle = '#fff'
caller.gCtx.lineWidth = caller.borderWidth
caller.gCtx.strokeRect(caller.borderWidth / 2, caller.borderWidth / 2, caller.width - caller.borderWidth, caller.height - caller.borderWidth)
}
}
以后直接调用这个方法就可以在它的canvas上渲染它所代表的界面了。
当然,还有很多其他的成员和方法,都是管理界面元素所需要的,详细自己读代码吧,最后的代码是这样的:
function baseUIObj(father, x = 0, y = 0, w = 0, h = 0) {
this.text = "baseUIObj"
this.name = "base"
this.me = this
this.children = []
this.father = father || undefined
if (this.father) {
this.father.children.push(this)
}
this.x = x
this

最低0.47元/天 解锁文章
1355

被折叠的 条评论
为什么被折叠?



