简介
2010 年 8 月 7 日,deviantart 迎来了其 10 周岁生日。 为了庆祝我们的生日,我们发布了一款名为 deviantART muro 的 HTML5 绘图工具。 该工具既可用作独立的 Web 应用,也可用作轻量绘图工具,用于向论坛评论添加图片。
deviantART 社区对这款全新绘图工具的热情非常高涨,该工具本身现在的流量与一些规模不小的网站资源相当。自该功能推出以来,大约每 5 秒就有人使用 deviantART muro 提交一幅新画作。这些只是已完成的绘图数量;还有许多绘图已开始,但未保存。
下文介绍了我们如何使用 HTML5、为何选择使用我们所用的技术,以及我在为大型网站编写首批完整的 HTML5 应用时发现的一些情况。
我的背景
2005 年底,我负责开发此处绘制功能所用的绘图工具。该工具是一种通过书签栏启动的“网络涂鸦”工具。它用于在任何网页上绘制图片。Draw Here 最初是使用 SVG 创建的(Firefox 1.5 Beta 版刚刚发布;它是首批支持 SVG 的浏览器之一)。
在 Internet Explorer 中,我们在后台创建 SVG,但使用 VML 渲染绘制内容。WebKit 当时不支持 SVG,因此我移植了我们的代码,以使用画布(当时仅在 WebKit 中出现的新技术)渲染 SVG。我甚至一度开发了一个端口,以便使用粘在一起的一组 div 元素在旧版浏览器上渲染 SVG 代码。(当然,这只是一个玩笑,目的是说明这种方法可行,但速度非常慢。)
在其鼎盛时期,用户每天使用“此处绘图”功能创作的绘图大约有 100 幅。虽然它还没有达到大型 Web 应用的最终润色程度,但已经足够完善,可以被称为不仅仅是实验。该项目在 2006 年年中被废弃,但该网站至今仍在苟延残喘,不过大部分都是为了娱乐。
deviantART muro 使用的技术
由于我曾在 HTML5 技术的早期使用这些技术,因此被邀请成为 deviantART muro 的首席开发者。阅读本文的任何人可能都能理解,我们为何决定采用 HTML5,而不是 Silverlight 或 Flash 等基于插件的技术。我们希望它既要强大,又要采用开放标准。
在 Canvas 和 SVG 之间做出选择
我们决定使用画布来实现绘制层。有些人可能会疑惑何时应使用画布,何时应使用 SVG。这两种技术可以执行的操作有很多重叠之处,正如“Draw Here”所证明的那样,这两种技术都可以用于创建绘图应用。
我发现,如果您想保留对所绘制对象的控制点,SVG 非常适合。例如,如果您希望用户能够绘制一条线,然后能够拖动线条的某些部分来更改其形状,那么使用 SVG 就可以轻松实现。但使用画布时,同样的操作会非常不方便。
使用画布时,您将内容发送到画布上,然后就忘了它。在代码中,空白画布和已经绘制了一小时的画布完全一样,并且它们的内存占用量大致相同。虽然光栅绘制程序通常非常适合与“触发即忘”技术搭配使用,但它确实会使某些事情变得困难。例如,在画布中创建快速撤消功能比在 SVG 中要困难得多。在 SVG 中,您只需保留对最近几条线条的控制柄,然后只需移除这些对象即可撤消操作。对于画布,绘制一条线条后,您不知道其下方是什么,因此若要移除该线条,则需要重新绘制其所在区域。
在决定使用 HTML5 来绘制画布后,我们决定在其他地方也使用一些其他 HTML5 功能。例如,我们如何使用 localStorage 跟踪用户的画笔设置。这样一来,用户只需按照自己的喜好设置各种画笔,下次使用我们的工具时就可以恢复这些设置。由于使用了 localStorage,我们无需耗尽 Cookie 或进行任何服务器往返即可获取这些偏好设置。
使用画布
在过去五年中,Canvas 取得了长足的进步。对于“Draw Here”,我们实际上并未发布我的画布端口,因为性能不佳。现在,我可以肯定地说,它的效果可能比您想象的要好。 清除大片画布并重新绘制复杂形状的速度通常比人类感知的速度要快。我发现唯一偶尔运行缓慢的操作是使用 getImageData() 对像素进行采样。操作速度显然取决于画布大小,但在大型画布上,如果在错误的时间执行 getImageData(),则可能需要很长时间,以致于用户会认为应用响应缓慢。
在阅读各种 Canvas 教程后,我最初的印象是,Canvas 是一个重量级功能,应尽量少用,在一个网页上可能只用一两次。我不知道其他人是否有这种感觉,但我有,因此在刚开始编写 deviantART muro 时,我会尽量少用它。不过,一段时间后,我发现在很多小细节上,画布可以为您省去很多精力。例如,应用的模拟图样指定了应有一个颜色选择器,其中包含两个重叠的三角形,分别显示主色和辅色:

我的第一直觉是开始思考如何使用传统的 HTML 和 CSS 创建这个小小的界面小工具。 精通 CSS 黑客攻击的用户可能会指出如何通过 CSS 实现所有这些,但由于这两个会变色的部分是三角形,因此这并不明显。
当我想到只使用画布时,便使用一个 DOM 元素和几行 JavaScript 代码制作了该 widget。deviantartART muro 在各处都使用了画布节点。每个图层都是一个画布,更改图层顺序只需切换 z 轴顺序即可。用于显示绘制区域缩小视图的缩放“导航器”调色板只是另一个画布,它会不时调用 drawImage(),并使用图层画布作为源图片。即使是绘制区域光标(一个双色圆圈,大小会根据画笔大小和缩放级别而调整),也是一个悬停在鼠标下方的画布。
我们对画布技术的使用比对其他 HTML5 技术更为宽松,这是因为 Google 的 ExplorerCanvas 库支持在 Internet Explorer 中模拟画布。这让我想到了下一部分。
Internet Explorer (IE)
更多大型网站尚未使用 HTML5 的主要原因是,它们不想失去 Internet Explorer 用户。我敢肯定,当大多数开发者听说 deviantART 开发了一款 HTML5 绘图应用时,他们首先想到的问题就是:“IE 怎么办?”
我们在开始就决定,在让内容在 Internet Explorer 中正常运行方面会尽最大努力,但不会再采用最常见的 Web 开发方式。由于网络社区采用了一种方法,即网站必须在所有已知浏览器中看起来都一样,才能发布,因此用户无法判断自己的浏览器是否缺失了某些功能。对于普通用户来说,速度问题归咎于其互联网连接,并且每个网页的呈现效果大致相同。因此,他们会根据任意细小的界面元素(例如返回按钮的颜色)来决定自己喜欢的浏览器。
我们决定使用 HTML5 规范创建任何想到的酷炫功能,并尝试让其在 Internet Explorer 中正常运行。如果无法正常运行,我们只会弹出一个模态窗口,说明该功能无法使用,因为用户的浏览器尚未实现 Web 标准。
我们最初尝试使用 Google 的 ExplorerCanvas (exCanvas) 来实现此功能。它在模仿大多数内容的画布方面表现出令人惊讶的出色效果。不过,这也有一个缺点。 在画布上绘制的每条笔触都是底层 VML 转换中的 DOM 对象。对于您可能尝试在画布上完成的大多数操作,这样做没问题,但 deviantART muro 的一些画笔会通过将大量笔触叠加在一起来创建形状。当 Internet Explorer 遇到包含数千个节点的 VML 时(即使是在快速机器上),也会崩溃。因此,对于许多绘制调用,我们实际上不得不进入实际的 VML 中编写代码,并使用一些技巧将节点串联在一起,并使用 move 命令指定应在何处留出空白。界面中的许多小控件和其他内容都使用 canvas 标记,因为这些小用例通常可以使用 exCanvas 正常运行。
除了让某些功能可与 exCanvas 搭配使用外,我们还建议用户安装 Google Chrome Frame 插件,以便继续使用其版本的 Internet Explorer。Google Chrome Frame 是一个插件,可将 Google Chrome 的渲染引擎嵌入到 Internet Explorer 中。从用户的角度来看,他们仍然在使用自己熟悉的浏览器;但在后台,我们的网页是使用 Chrome 的 HTML5 功能和更快的 JavaScript 进行呈现的。
我知道应该很容易将内容移植到与 Chrome Frame 搭配使用,但我没想到它竟然如此简单。只需添加一个额外的元标记,就大功告成了,IE 中的内容就会开始正常显示。
摘要
很高兴能够使用 HTML5 规范中的新技术,我可以说,我使用的所有技术都已准备就绪,可以大显身手了。即使您需要在 IE 上实现完美无缺的效果,也可以通过结合使用 canvas 和 exCanvas 来实现大量令人惊讶的效果。令人惊讶的是,在 SVG 和 VML 之间编写转换层也是可行的。开始使用这项技术后,就像进入了一个全新的世界。
参考
- deviantART muro
- deviantART 论坛,您可以在其中绘图(需要登录)
- ExplorerCanvas
- Google Chrome Frame