《Bad Apple》JavaScript版-创作全解析+浏览器横测
2010-01-16 10:57 JimLiu 阅读(3373) 评论(17) 编辑 收藏 举报在上一篇日志中,小弟向大家展示了一个用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字符串连接是有多么多么的慢。所以只好预处理一个拼接好的颜色表,用的时候直接查表,就不用费时去每次都连接字符串了。
稳定帧率
在经过前面两层优化之后,程序运行的速度已经比较快了,由于上文说到帧率不稳定,所以需要在画面过快的时候限制速度,我们采取了如下方式:
- 根据当前时间,计算期望帧数
- 如果实际帧数>期望帧数,则延迟(当前帧数*每帧时间(固定值) - 当前实际时间)
- 如果实际帧数<期望帧数,则跳帧至期望帧(按每帧时间逐一累加,直到时间补偿到正数,既跳至这一帧)
这样一来在速度快的电脑、浏览器和低分辨率下,可以以比较稳定的速度播放,即使遇到画面大幅变化的时候,也就丢几帧,不会造成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),再无怨念。