Lark新特性解读(二)

Posted on July 21, 2015
更新: Lark项目的新特性现已全面合并到Egret Engine 2.5+中,不再独立维护,Lark GUI(Swan)扩展库,对应Egret Engine 2.5+中的EUI扩展库。

这篇文章接着上一篇《Lark新特性解读(一)》,继续解读Lark核心库新特性的后半部分。

屏幕适配模式

Lark里精简了初始化过程,设置屏幕适配模式,不再需要去修改加载器代码,只需在Lark Div节点上直接设置data-scale-mode属性即可,如下图:

ScaleMode2

若需要运行时动态修改屏幕适配模式,开发者仍然可以通过修改Stage.scaleMode属性来实现。data-scale-mode属性相当于设置Stage.scaleMode初始值的一个简便方法。

Lark一共提供了6种屏幕适配模式,在讲解各种适配模式前,我们首先必须明确一点,屏幕适配模式解决且仅解决一个问题:舞台尺寸(stageWidth,stageHeight)与设备屏幕尺寸的对应关系。也就是修改屏幕适配模式,只会改变最外层的舞台尺寸。但这仅仅只是第一步,若要实现内容的完美适配,还需要在舞台尺寸发生改变时,自行调整内部UI界面的布局。当然,开发者可以借助Swan扩展库的自适应流式布局轻松实现内容的适配。

另外需要关注一下其它两个属性:data-content-width和data-content-height,它们表示应用程序期望的初始内容尺寸。屏幕适配就是根据设备屏幕尺寸与初始内容尺寸,以及指定的适配规则计算出此时的舞台尺寸的过程。下图演示了六种适配模式的效果图:

ScaleMode3

具体每种适配模式的详细定义,可以参考Lark的在线API文档,这里不再赘述。这里可以看到,Lark里除了支持Flash里标准的四种适配模式外,还增加了对fixedHeight,fixedWidth这两种移动设备上比较常用的适配模式的支持。

锁定屏幕方向

与屏幕适配相关的,开发者还有一个比较强烈的需求,就是锁定屏幕方向。之前在遇到这块需求时,开发者通常都是采用一个变通的方式:自行旋转舞台90°,然后在编程的时候,将x和y坐标调转过来写。虽然可以实现,但是方式较为繁琐,并且容易出现各种兼容问题。 现在Lark里已经对此功能原生提供了支持,可以轻松实现锁定屏幕为横屏或竖屏模式。Lark在底层旋转了画布,编程上直接当成锁定后的舞台尺寸即可,无需任何特殊处理。配置方式与屏幕适配模式也类似,只需简单地在Lark Div上设置data-orientation即可。一共有四个有效值:自动,锁定竖屏,以及正负90°的两种锁定横屏方式,效果如下图:

orientation

Event对象池

JS里创建对象的瞬间开销相对于其他强类型语言来说比较大,因此对象池作为一种减少对象创建次数的优化方式,在JS开发里被广泛使用。Lark的事件机制内置了Event的标准对象池操作方法,来避免不必要的重复创建新对象。之前在Egret里同样对事件机制做过了对象池优化,现在Lark里跟进一步,将事件对象池的方法重构为更加简洁的标准API,Event类上一共提供了三个方法:create()和release()两个静态方法,分别负责创建和销毁事件对象,另外一个实例方法clean(),事件子类覆盖clean()方法能够在回收时自动清理外部引用。具体调用方法如下图:

EventPool

可以看到基本上分为三步,第一步:使用Event.create()方法从对象池获取指定的事件实例,第二步:若有额外数据,附加事件实例上的额外数据,并抛出事件,第三步:使用Event.release()方法回收事件。这样就是一个标准的事件对象池调用过程,但大部分时候,我们并不需要重复写这部分代码,通常都是把这部分代码封装一个静态工具方法调用即可。具体可以参考TouchEvent.emitTouchEvent()静态方法。

另外,由于框架内部抛出的事件对象,均使用了对象池优化方式。因此开发者应该避免缓存事件实例,从而影响事件实例的回收。应当将事件对象当做携带数据源的临时载体,因为稍后对象被回收,携带的数据源也会清空。因此若有延迟访问事件对象携带的数据的需求,应该立即缓存下事件上对应的数据,而不是缓存事件实例本身。

运行时接口判断

TypeScript中提供了接口(interface)的定义功能,能够很大程度上方便面向对象的编程开发。但是由于TS最终是要编译为JS运行的,而JS中并没有接口的概念,因此TS的接口仅在编译阶段提供类型检查,无法在运行时判断一个实例是否实现了某个接口。为了解决这个问题,之前变通的方案是:通过in关键字来检查一个实例是否含有目标接口定义的属性名,从而判断该实例是否实现了某个接口,大部分情况下都是能够适用的,但是遇到正好含有同名属性,却未实现目标接口时,就无效了。因此这是个并不安全且可靠的方案。现在,Lark里提供了lark.is()的全局函数,能够在运行时准确判断一个实例是否实现了某个接口,或者是否为一个类或它的子类的实例。调用方式也非常简单:

larkis

lark.is()方法的底层实现原理是扩展了TS编译器。相信大部分开发者都知道,原生的TS编译结果是无法在运行时读取完整类名的,而之前在Egret里却能够使用getQualifiedClassName()函数返回实例类名。其实现原理就是扩展了TS编译器,在编译阶段将类名信息输出到目标JS文件里。现在Lark更进了一步,将接口信息也输出到了目标JS文件中。并且这些信息直接记录在类定义上,并不会影响创建实例的性能。lark.is()方法就是根据类定义上的这些信息,实现的准确类型判断。通常情况下,TS编译器会帮你把自定义类也做好类信息的输出,开发者无需关心底层实现。而对于不使用TS编译器的纯JS开发者,Lark也同样考虑到了,提供了一个可选的lark.registerClass()方法,能够手动为需要运行时反射的自定义类注册类信息。

任意显示对象作为遮罩

不规则遮罩功能同样是开发者强烈反馈的一个需求,现在Lark里已经实现与Flash完全一致的遮罩功能,能够将任意显示对象作为遮罩,也就是说支持任意形状的遮罩类型。底层仍然是通过硬件加速实现的,不用担心不规则形状的性能问题。调用方式也比较简单,直接将作为遮罩的显示对象赋值给另一个显示对象的mask属性即可。不过要注意,作为遮罩的对象也要添加到显示列表中。

像素级精确碰撞

Lark里新增了另一个呼声很高的API,原生的像素级精确碰撞。简单说就是能够实现位图透明区域的点击穿透。通常情况下,位图都是作为一个矩形碰撞区域来处理的,即使透明的像素也能接受触摸事件。开发者在处理这个需求时,都需要自行额外封装碰撞检测方法,缓存并操作位图像素来实现。现在只需要简单地将Bitmap实例的pixelHitTest属性设置为true即可实现点击穿透。并且此功能在底层做了优化,并不会占用额外的内存用于缓存像素。性能也足够支撑极大数量的碰撞检测。唯一需要注意的一点是,对于跨域加载的图片,由于浏览器安全限制,无法访问图片像素数据,此功能可能无法生效。请确保需要精确碰撞的图片不是跨域加载获得的。

内置的日志输出面板

Lark里提供了统一的日志输出方法lark.log(),推荐用它全面替代console.log()。因为console.log()理论上是浏览器提供的API,原生打包方案里不一定会有。lark.log()除了能向浏览器的控制台标准输出外,还能够将日志直接输出到屏幕上的FPS区域,效果如下图:

LOG

这个功能在移动端调试时非常有用,因为移动端目前还比较缺乏通用的真机调试方案,也无法方便地直接获取手机上浏览器的控制台输出。此时就可以通过FPS面板直接看到的日志信息,来实现快速的调试。开启此功能只需在Lark Div节点设置data-show-log属性为true即可,反之设置为false将不再显示日志信息。另外还可以通过设置data-log-filter属性来附加一个正则表达式,用于过滤大量的日志信息,只显示指定的部分。

DEBUG编译参数

除了以上直接显示日志信息的功能,Lark内还有其他大量辅助调试的功能。例如为只读属性输出运行时警告,显示脏矩形重绘区域等。Lark框架内之所以可以实现更加丰富的调试功能,又不影响最终包体大小,主要是因为引入了DEBUG编译参数功能。熟悉其他语言的开发者可能对此功能并不陌生,简单说就是设置一个标志,让部分代码包含在标志中,这部分代码仅在调试版中存在,一旦到发行版时,编译器将自动移除这部分调试代码。由于TS编译器还未支持编译参数功能,严格意义上说Lark里提供的并不是真正的编译参数功能,因为无法自定义参数名称,但是已经能够很大程度上解决包体问题。Lark在框架内提供了一个全局变量DEBUG,只要将调试的代码包含在 if(DEBUG){…} 的括号中,当发行版时,编译器会自动将DEBUG识别为常量false,导致括号内的代码无法执行变为无效代码,接着在混淆代码的阶段将自动删除整个无效的if代码片段。另外,这个DEBUG全局变量不仅框架能可以使用,用户的项目中同样可以引用。这样我们可以放心地写丰富的辅助调试或输出日志的代码,在发行版中将自动移除,既方便调试,又不影响最终包体大小。

单文件增量编译

最后值得一提的是,Lark的命令行工具进行了大规模的重构改造,现在已经支持对TS的单文件增量编译方式。也就是说改变一个文件时,只会编译这一个文件,不会编译整个项目,从而能够极大减少编译等待时间,对于大型项目而言,能够明显感觉到编译时间的缩短,几乎在瞬间完成。

到此Lark核心库的新特性解读就告一段落了,下一篇文章将会重点介绍全新的GUI库Swan的新特性,感谢关注!