随着HTML5标准的不断发展,看到了众多令人兴奋的新特性。其中之一就是<canvas>
元素,它为网页开发者提供了极大的自由度。本文将通过一个简单的实验,探讨如何在不使用<canvas>
元素的情况下,在客户端生成图像动画。
建议读者访问以下资源,了解客户端生成的图像动画是如何使用数据URI实现的。这些资源主要关注基于旧式调色板的静态GIF图像动画,以及如何基于数据URI特性构建图像绘制工具。
为了更好地理解数据URI的用途,建议访问维基百科上的讨论。数据URI允许将图像数据嵌入到图像的src
属性中。不需要从服务器获取它,也不需要使用<canvas>
来绘制它。可以在客户端预先加载它,嵌入到网页中,或者可以使用JavaScript客户端代码来创建它。第一种方法会增加网页的大小,但对于小型动画图像很有用。第二种方法为提供了更多的自由度来从头开始创建图像。本文采用了第二种方法。
一般的<img>
元素如下所示:
<img id="myImage" src="http://image-url">
要加载来自Web服务器的图像,将图像URL作为图像src
属性的值。要加载嵌入在网页中的图像,将以下内容作为图像src
属性的值:
<img id="myImage" src="data:image/bmp;base64,image-data">
浏览器知道,在第二种情况下,图像数据不会通过向Web服务器发送HTTP请求获得,而是在图像标签的src
属性中。浏览器解析这个并在页面上正确显示图像。
那么,在第二种情况下,src
属性中包含的是什么?首先,通过指定data:image/bmp
值告诉浏览器有图像数据。这包含了数据URI的定义和随后的图像数据的MIME类型。如果图像数据是base64编码的,也必须提供这个信息。所以,一般的数据URI头部是data:MIME-type[;base64],
。MIME类型可以是任何支持的图像MIME类型,但必须以请求的格式提供图像数据,这在JavaScript世界中可能是一个真正的挑战,但并非不可能。
接下来是原始图像数据。它来自哪里?正如之前所说,它可以在其他地方获得并嵌入到网页中,或者直接嵌入到图像标签的src
属性中。如果需要一个生成器,请访问这个网站。这个网页会生成需要放入网页中的代码,使用提供的图像。它还提供了生成的图像的预览。不错的工作,不是吗?
但是,如果想扩展到自己的需求呢?如果想每次页面加载时都从头生成一个新的图像呢?让现在深入JavaScript世界,因为这是在客户端唯一可以做到的,不使用任何可用的技术:Flash、ActiveX、HTML5、Java,或任何服务器端脚本语言。
在开始时,会保持简单,也就是说,只会使用BMP图像,当找到一些额外的时间时,可以扩展到现代浏览器支持的任何其他图像格式。
在维基百科上,可以找到关于BMP图像格式需要知道的一切。现在,看下面的每个位图图像的一般结构:
文件头 [正好14字节长]
位图头 [正好40字节长]
调色板 [0或n*3字节长,其中n是调色板中的颜色数量]
位图数据 [正好高度*宽度*位/像素字节长,四舍五入到4字节边界]
为了更清楚,从维基百科上借用了以下图片:
首先要做的就是填充这个头部信息,因为它不会改变(网页上的图像大小很少变化)。请参见以下JavaScript代码:
var imageHeader = (
'BM'+
// "Magic Number" 或签名
num_file_bytes +
// 文件大小(字节)*
'\x00\x00'+
// 保留
'\x00\x00'+
// 保留
'\x36\x00\x00\x00'+
// BMP数据所在位置的偏移量(54字节)
'\x28\x00\x00\x00'+
// 头部剩余字节数
// 从这里(40字节)
width +
// 位图的宽度(像素)*
height +
// 位图的高度(像素)*
'\x01\x00'+
// 颜色平面的数量(1)
'\x20\x00'+
// 32位/像素
'\x00\x00\x00\x00'+
// 无压缩(0)
'\x00\x00\x00\x00'+
// BMP数据大小(字节)*
'\x13\x0B\x00\x00'+
// 2835像素/米 - 水平分辨率
'\x13\x0B\x00\x00'+
// 2835像素/米 - 垂直分辨率
'\x00\x00\x00\x00'+
// 调色板中的颜色数量
// (保持0为32位)
'\x00\x00\x00\x00'+
// 0重要颜色
// (意味着所有颜色都重要)
);
这样,就为32bpp位图图像创建了头部,其大小为width*height。这种位图类型没有颜色表,所以它在头部中缺失。
由于有了正确的头部,现在可以分配图像数据。请参见以下:
var imageData = new Array();
它基本上是一个Array对象,将使用它来存储图像像素。说到像素,创建了以下类来存储颜色信息:
function Color(r, g, b) {
this.red = parseInt(r) % 256;
this.green = parseInt(g) % 256;
this.blue = parseInt(b) % 256;
return this;
}
现在,要创建单个像素的值,考虑到32bpp颜色深度,使用了以下方法:
var color = new Color(red, green, blue);
var newColor = String.fromCharCode(color.blue, color.green, color.red, 0);
当想为图像中的某个像素放置值(颜色)时,使用了以下方法:
imageData[y*width+x] = newColor;
这很难比这更简单了,对吧?
基于之前的故事,创建了一个非常小的图形库,用JavaScript编写,将展示在客户端可以做什么。请参见以下示例:
var graphics = createGraphics(320, 240);
graphics.clear(new Color(0, 0, 0));
graphics.updateImage("myImage");
注意:这将只在Firefox、Chrome、Opera和Safari中正确工作。
在完整的HTML5标准实现的这个时代,这不是构建大型动画系统的可接受解决方案。但它仍然可以作为一种替代方案,尽管图形库提供的功能非常基本。这里缺少很多东西,如果有时间,会尝试实现它们。这是一个实验,是过去的技术,不是未来的。试图测试JavaScript语言在即时生成全彩色图像方面的速度,认为得到了有趣的结果。
看到了一些基于DOM的JavaScript图形库。在渲染过程中创建了大量的DIV元素,并且在客户端创建图像。这是另一种选择,也是可行的。
要指出的第一个问题是渲染速度。尽管有些人会说它并不慢(它确实不是),但说它无法与HTML5的<canvas>
元素相匹配,也不应该。这个速度在很大程度上取决于图像的大小:图像越小,显示得越快。接下来,JavaScript渲染算法并不是世界上最快的,知道的。
第二件事是位图的类型(或颜色的数量)。使用的是32bpp位图,它们提供了质量,但它们是今天在市场上能找到的最大的。如果使用调色板化的位图,将处理更少的数据,它肯定会更快,但最多有256种不同的颜色可用,所以在质量上做出了妥协以获得更多的速度。
最后一件事是从头开始不断加载图像。每次设置图像src
属性时,浏览器都会从头开始解析其信息,这是耗时的,考虑到前两个问题。压缩的图像数据是更好的解决方案(特别是对于调色板化的位图),但最好找到一个非常快的JavaScript GIF编码器。可以看这个作为例子,但要注意,因为它是实现生成单色GIF文件的。也可以寻找一个JavaScript LZW编码器。