代码改变世界

《Bad Apple》JavaScript版-创作全解析+浏览器横测

2010-01-16 10:57 by JimLiu, ... 阅读, ... 评论, 收藏, 编辑

上一篇日志中,小弟向大家展示了一个用JavaScript制作的《Bad Apple》,这次我将对这个程序创作过程中的一些有意思的细节做解释。

视频处理

这个并不在JavaScript范畴内,我们用KMPlayer导出视频的每一帧,然后插值缩小到指定分辨率,没有用.NET库里的插值,因为那个会造成边缘效果不佳(用三次立方的话),还有杂乱色斑(这个也有可能是jpg的原因),这个插值非常简单,就是根据分辨率取周围几个点的平均值而已……然后再生成js里用的格式(下文会说到),这个过程没什么好说的了……

画布

所谓画布,就是一个用来显示画面的HTML元素,里面有1px元素若干,用来表示视频中的像素点。在demo程序中,点create canvas以后会生成一个用来做画布的div,一开始我是用表格做的,但是发现不管是创建速度还是修改速度都很一般。后来改成全用span,float:left,然后同时设置上外层div的宽度高度,这样就可以内外吻合了。然后再每一帧中改变每个像素的背景色。

后来发现当颜色不变的时候,虽然设置了颜色,浏览器本身也会有一定优化,比改变的时候要快一些,但是那一句xxx.style.backgroundColor=...;还是会白白占用一些时间。所以和上一帧对比一下如果颜色没改变就不赋值了。但是这样一来,因为每一帧的变化程度是不一样的,有时候一个点也没变,有时候全变了,所以帧率变得非常不稳定,下面我会继续说到如何稳定帧率。

颜色编码

我们使用的颜色编码比较特殊,是64色的,一个字符串表示一行,一个字符表示一个像素。64个不同的字符,映射到256级灰度,这样对单个字符的利用率就很高了,64色也足以应付视频中的灰度平滑过度。在实际中,需要把字符转换成RGB颜色表示,这里我们深刻地体会到js字符串连接是有多么多么的慢。所以只好预处理一个拼接好的颜色表,用的时候直接查表,就不用费时去每次都连接字符串了。

稳定帧率

在经过前面两层优化之后,程序运行的速度已经比较快了,由于上文说到帧率不稳定,所以需要在画面过快的时候限制速度,我们采取了如下方式:

  1. 根据当前时间,计算期望帧数
  2. 如果实际帧数>期望帧数,则延迟(当前帧数*每帧时间(固定值) - 当前实际时间)
  3. 如果实际帧数<期望帧数,则跳帧至期望帧(按每帧时间逐一累加,直到时间补偿到正数,既跳至这一帧)

这样一来在速度快的电脑、浏览器和低分辨率下,可以以比较稳定的速度播放,即使遇到画面大幅变化的时候,也就丢几帧,不会造成fps急剧下降。而对于低速电脑和比较慢的浏览器,就只能狂跳帧了(这种情况下已经没有观赏价值了)。

动态加载JS

这个就比较简单了,参考老赵《王道!动态添加script元素》。我做了一个简单的ScriptLoader,这样就不用让无辜的用户一访问网页就承担那么大的数据量。这里要注意一下浏览器兼容性。

JavaScript代码压缩

这里说的并不是以前我们常用的那种js/css压缩的方法,比如去掉whitespace,长变量名换短变量名等。因为这里的js代码中,程序代码很少,也就几K。但是用来表示视频的那些代码却非常巨大。160*120分辨率15fps的数据就有64MB之多,而就算是最小的80*60(即前文中DEMO用的分辨率),完整版也有17MB,截取其中150帧,也就是10秒的动画,还是有700+KB,这对于在网络上演示和传播极为不利。而用传统的js压缩方法就毫无办法了。

所以这里我们用LZW算法对数据进行了压缩,是以前用C++写的,先压缩成二进制,再用可见字符表示二进制,然后我把解压的C++代码翻译成了js……在一个不极限的算法之下从700多KB压缩到了111KB,效果还算满意,极限算法应该可以压缩到100K以内。然后再作为字符串变量加载,在动态加载JS之后,eval这个字符串,就可以把它正常执行成程序用的内存数据了。

浏览器横测

完事后兴起,我们对Chrome3.0, Firefox3.5, IE8, Safari4,进行了对比测试。

测试的时候还没有JS代码压缩,只有动态加载,所以测试对比了4个方面:加载速度,画布生成速度,丢帧率/fps,内存占用。

测试用的电脑:AMD 9550/4GB RAM/Windows 7

测试在160*120(19200个点,约288000像素操作/s), 80*60(4800个点,约72000像素操作/s)两个分辨率下进行,总帧数3286,fps15,最终结果如下:

分辨率:160*120,数据64.7MB

 

Chrome

Firefox

IE

Safari

加载数据

1m36s

3s

31s

5s

创建画布

12s

1s

23s

5s

丢帧率和FPS

丢447帧

丢2332帧

总共才放了个位数帧

丢了大部分,基本2fps

内存占用

1038MB

178MB

318MB

611MB

补充:在Chromium4.0 nightly build中,这四个指标分别提升到了6s/11s/丢395帧/280MB,可谓提升惊人。

分辨率:80*60,数据17MB

 

Chrome

Firefox

IE

Safari

加载数据

16s

1s

7s

2s

创建画布

1s

1s

1s

1s

丢帧率和FPS

丢2帧

丢93帧

丢3090帧

丢1111帧

内存占用

275MB

80MB

113MB

205MB

最终结果并不出人意料,Chrome凭借超凡的V8引擎鹤立鸡群,虽然它的速度快很大程度上基于对内存的贪婪,但是也不得不佩服它运行的确很快。而号称最快浏览器的Safari却下场惨淡。firefox很奇怪,在我的电脑上非常慢,跟IE差不多下场,但是在测试电脑上还不错,最后我想可能是因为我装了firebug,据说它有bug,会造成TraceMonkey的JIT罢工,大幅影响速度。而IE就啥也不说了……

结语

一个自娱自乐的游戏,没想到最后可以说的东西还是蛮多的,最后在这里提供三种分辨率(160*120, 120*90, 80*60)的单机演示程序下载(无JS压缩版本)。

希望大家玩的高兴……有问题?请留言……

番外篇

后来用HTML5中的canvas重写了,LZW也改成一帧一帧解压,速度很安逸,在现代浏览器里,结合audio,可以音画同步(480*360*15FPS),再无怨念。