HTML5 Canvas 和 SVG 是 IE9 中引入的两项令人激动的图形功能。上周在拉斯维加斯举办的 MIX11 大会对这两个功能进行了介绍(请参阅深入剖析 HTML5 <canvas> 和现代化您的网站:SVG 遇到 HTML5)。

这些技术可用于解决现代网络上众多的图形场景。由于 Canvas 带来了众多激动人心的功能,人们往往会忽略 SVG,而在许多情况下 SVG 可能是更好的选择。这里我提供一些关于在何时选择 Canvas、SVG 或二者的组合的想法。

Canvas 和 SVG 总体概述

以下是对 Canvas 和 SVG 的一个总体概述,为关于何时使用一种特定的矢量图形技术的讨论提供了框架。

Canvas  SVG 的对比

Canvas

SVG

基于像素(Canvas 在本质上是一个具有绘图 API 的图像元素)

基于对象模型(SVG 元素与 HTML 元素类似)

单个 HTML 元素,在行为上类似于 <img>

多个图形元素,是文档对象模型 (DOM) 的一部分

视觉呈现通过脚本以编程方式创建和修改

视觉呈现使用标记创建并通过 CSS 或通过脚本以编程方式修改

事件模型/用户交互是粗粒度的 – 仅在画布元素上,交互必须根据鼠标坐标手动编程

事件模型/用户交互基于对象,是在原语图形元素上的 – 线条、矩形、路径

API 不支持可访问性,除了画布,还必须使用基于标记的技术

SVG 标记和对象模型直接支持可访问性

SVG 作为一种在内存模型中持久保存的保留模式图形模型而著称。类似于 HTML,SVG 构建一个包含元素、特性和样式的对象模型。当 <svg> 元素出现在 HTML5 文档中时,它的行为类似于一个内联块,是 HTML 文档树的一部分。

Canvas 是一个位图,包含一个即时模式图形应用程序编程接口 (API) 来在它之上进行绘制。Canvas 是一种“即发即弃”模型,直接向它的位图呈现它的图形,然后对所绘制的形状没有任何认知,只会得到最终的位图。

可以这么认为,Canvas 类似于 Windows GDI API,其中您以编程方式向窗口绘制图形,而 SVG 类似于包含元素、样式、事件和基于 DOM 的可编程性的 HTML 标记。Canvas 是过程性的,而 SVG 是声明性的。

场景

以下几节介绍两种技术的技术优势和不足,包括确定一种技术何时适用于给定任务的一种常用方法。从下图中可以看出,每个场景落在从 Canvas 到 SVG 之间的一个范围中,两种技术中间有一个明确的交叉点。

clip_image002 
矢量图领域

高保真度复杂矢量文档

高保真度复杂矢量文档已是并将继续是 SVG 的最佳点。它非常详细,适用于查看和打印,可以是独立的,也可以嵌入到网页中。SVG 的声明性特征提供了从数据库到形状的工具、客户端或服务器端生成。

Internet Explorer Test Drive 上提供了一个真实图形演示:

clip_image004

第一幅图显示了图,而第二幅图显示了放大到 1000% 的图

clip_image006

当考虑到观察大型示意图的适用性,但需要下钻到详细信息或出于工程用途打印整个文档时,可缩放的矢量图形中的“可缩放”特征会变得非常清晰。出于这些原因,我们将高保真度复杂矢量文档放在范围的 SVG 端。

混合交叉

用于查看和打印的高保真度文档

clip_image008
SVG 作为一种图像格式

SVG 的另一个常见用途是用于网页内的静态图像。使用目前的高 DPI 显示器,开发人员必须考虑图形的质量。下面的图像表示通过 CSS 设置了样式的可能的 <li> 项目符号图像。下面的图像在外观和文件大小上几乎完全相同。

clip_image010 
左侧是 SVG 图形,右侧是它的 PNG 呈现

如果开发人员希望大规模重用该图像,或者如果最终用户使用了高 DPI 屏幕,光栅图像将变得失真,或者需要更大的文件才能保持保真度。

clip_image012 
左侧是放大的 SVG 图形,右侧是放大的 4K PNG

因此,SVG 是网页上最简单图像的一种好替换格式。Canvas 没有适用的替换格式可用。

静态 
图像

混合交叉

用于查看和打印的高保真度文档

clip_image014

在范围的另一端,Canvas 具有很高的呈现速度,不需要保留所绘制的内容。当首次引入 Canvas 时,开发了许多有趣的实验。我将这些实验划分为 3 种不同的场景。

像素操作

因为 Canvas 专门用于绘制和操作基于像素的绘图面,所以一些 Canvas 实验和展示包括复杂的算法,以实现令人印象深刻的图形效果,比如光线跟踪或滤镜。

下面的示例由 Adam Burmister 编写。该实验通过跟踪光线在一个图像平面上创建了一幅图像,模拟它遇到虚拟物体的效果。

clip_image016

作者本身提供了以下警告:“这会消耗大量 CPU 资源。您的浏览器可能看起来会停止响应。”因此,尽管 Canvas API 能够生成这样的图片,但这可能并不是一个好想法。正如网站作者 Adam Burmister 所总结的,“光线跟踪[是]有史以来最糟糕的 JavaScript 应用。”

其他场景范围的像素操作也可以这么说。下面的函数将一个画布中的绿色像素替换为另一个具有相同大小的画布中的像素。这样的函数可用于创建视频的“绿屏”效果。

function GreenScreenAtoB(a, b) {
var aImageData = a.getImageData(0, 0, a.canvas.width, a.canvas.height);
var bImageData = b.getImageData(0, 0, b.canvas.width, b.canvas.height);
var aPixels = aImageData.data;
var bPixels = bImageData.data;
 
if (aPixels.length != bPixels.length) {
window.alert("Canvases do not have the same number of pixels");
return bImageData;
    }
 
var pixelCount = bPixels.length;
for (var pixelIndex = 0; pixelIndex < pixelcount; pixelIndex += 4) {
// grab the RGBA components of each pixel in b
var r = bPixels[pixelIndex + 0];
var g = bPixels[pixelIndex + 1];
var b = bPixels[pixelIndex + 2];
var a = bPixels[pixelIndex + 3];
 
// if the b pixel is green, replace it with a pixel from a
if (r == 0 && g == 255 && b == 0 && a == 255) {
bPixels[pixelIndex + 0] = aPixels[pixelIndex + 0];
bPixels[pixelIndex + 1] = aPixels[pixelIndex + 1];
bPixels[pixelIndex + 2] = aPixels[pixelIndex + 2];
bPixels[pixelIndex + 3] = aPixels[pixelIndex + 3];
        }
    }
 
return bImageData;
}

这是一个有趣的实验,但是与上面的光线跟踪示例一样,目前的机器性能会拖后腿。我列举这些示例有一个主要原因:这种像素操作使用 SVG 无法实现。它是两种技术之间的区别性因素。一种技术处理像素,而另一种处理模型。

无论是从简单的矢量图形创建真实的图像还是为视频创建绿屏效果,在大多数情况下,这些图形场景都不适合在如今的网络上进行快速部署。但是,某些场景具有足够的响应能力(比如应用滤镜来删除照片中的红眼)。这些像素操作场景在范围中与画布场景一样位于最左端。

混合交叉

用于查看和打印的高保真度文档

静态图像

高性能(滤镜、光线跟踪器)

clip_image018
Hybrid and Crossover

最有趣的用例不会指明明确获胜者。这些用例可分为两种主要场景:图表/图形/映射和二维游戏。

图表和图形需要矢量图,Canvas 或 SVG 都可以使用。但是,由于 SVG 固有的功能,它常常是更好的选择。

SVG 图表/图形/映射场景

网络上一个流行的图表和图形子集包括:

  • 交互式组织图和流程图
  • 交互式地图 – 路径查找
  • 建筑楼层平面图
  • 工程示意图
  • 航线或活动场所座位布局
  • 一般数据或财务图表(柱状图、条形图、折线图、散点图、环形图等)

对于所有这些,SVG 是首选技术的原因在于:

  • 它们可以通过将 XML 转换为 SVG,从现有数据轻松生成
  • 静态版本可从工具(包括 Inkscape、Adobe Illustrator、Microsoft Visio 和各种 CAD 程序)导出
  • 它们需要准确的用户交互
  • 第三方内容提供商可使用 CSS 样式为 Web 作者进行自定义
  • 它们需要可访问性

为了更准确地演示,我们看一下在美国地图上选择一个州的场景。

clip_image020网页上此图显示不出来,请插入该图

上面显示的阿拉斯加州详细地图是公共领域,可在 Wikimedia Commons 上看到。

在 SVG 中,阿拉斯加州使用一个 <path> 元素表示,该元素的“d”特性中包含大约 162,500 字符的地理数据。

<path id="AK" fill="#cdc3cc" d="M 777.5514,1536.1543 C 776.4904,1535.0933 776.7795,1530.0041 777.9416,1529.2859 C 781.3258,1527.1943 787.2657,1532.4522 784.8317,1535.3849 …" />

对于 Canvas,此形状可使用一系列 JavaScript 调用创建:

function drawAlaska() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(777.5514, 1536.1543);
ctx.bezierCurveTo(776.4904, 1535.0933, 776.7795, 1530.0041, 777.9416, 1529.2859);
ctx.bezierCurveTo(781.3258, 1527.1943, 787.2657, 1532.4522, 784.8317,1535.3849);
    //
// 2,875 more path-drawing directives
    //
ctx.bezierCurveTo(1689.8261, 12.13753, 1689.1395, 12.17333, 1685.8848, 10.52683);
ctx.closePath();
ctx.fillStyle = "#cdc3cc";
ctx.fill();
}

实际上,它需要 2,878 条路径绘制指令(moveTo、lineTo 和 bezierCurveTo)来绘制复杂的阿拉斯加州地图。当然,可以实现此地图的低分辨率版本。怀俄明州和科罗拉多州所需的代码要少得多。 :-)

SVG 基于地图的应用程序通常包含一种涉及到悬停效果、选择、项目之间的缩进和缩放的交互式体验。在使用 SVG 时,比如用于处理鼠标事件,这些操作仅需要轻量型的 HTML 概念:

<path id="AK" fill="#cdc3cc" onmousedown="window.alert('Alaska');" d="M 777.5514,1536.1543 …" />

或者使用 CSS 创建一个悬停突出显示效果:

path#AK:hover { fill:yellow; }

此交互式地图类型的一个示例可以在 Test Drive 演示 Atlas zur Europawahl 2004 in Deutschland 中看到,2004 年德国的欧洲选举结果的可视化。

在 Canvas 中,创建这些效果需要使用事件对象的鼠标坐标对您自己的点击检测进行编码。可以使用任何形状。尽管有 isPointOnPath() API,但它仅适用于创建的最后一条路径。

代码能够并确实以图形库的形式存在,以在图形上启用具体的点击检测,能够正常地使用像素数据来检测点击和悬停。它们也可供 SVG 使用,如果在设计中利用了 SVG 功能,将具有更高的性能。

混合交叉

用于查看和打印的高保真度文档

静态图像

交互式图表和图形

高性能(滤镜、光线跟踪器)

clip_image022
Canvas 图表/图形场景

Canvas 为图表和图形场景提供了自己的空间。要设置此场景的上下文,我们需要了解 SVG 和 Canvas 的性能特征。

有时,一些外部影响因素要求选择一种(几乎)与功能独立的技术。对于 SVG 和 Canvas,有两个主要的区别。

开发人员知识、技能集和现有的资产将在技术选择中扮演着重要角色。如果在创建游戏期间,开发人员拥有深厚的低级图形 API 知识和有限的 Web 技术知识,那么可能的技术选择是 Canvas(稍后将详细介绍)。对于游戏移植,一些工具支持从第三方实现迁移到 Canvas。

如果性能至关重要,常常要求精确到毫秒级,那么有必要对比两种技术的性能特征。这并不意味着 Canvas(通常被视为具有很高的性能)是显而易见的选择。但是,对于具有必须在像素级别绘制的大量数据的应用程序,Canvas 显然是更好的选择。

下面的天气地图不需要较大的表面区域,屏幕上的对象数量非常多。使用 Canvas,无需更新 DOM 即可快速绘制这些对象。

clip_image024

尽管上图完全可以在 SVG 中创建,使用圆圈或椭圆元素表示点,但将数千个元素加载到 DOM 中可能要花费很多时间。无论您是看到大量像素还是图像,这都是表明 Canvas 是要使用的技术的好线索——无论这是天文学、生物细胞运动还是音频调制显示。这里对数据可视化速度的限制是 CPU 速度、Canvas 实现速度和 JavaScript 实现速度。

实时高容量数据表示

交互式图表和图形

用于查看和打印的高保真度文档

高性能(滤镜、光线跟踪器)

混合交叉

静态图像

clip_image026
二维游戏

休闲游戏是我们要探索的最复杂的场景。一些初始观察结果如下:

  • 游戏库利用了较低级的图形 API
  • 开发人员的游戏行业技能集针对这些较低级的 API 进行了调优
  • 许多游戏在很大程度上是基于图像或子画面的
  • Adobe 等供应商正在开始支持导出 Canvas
  • 休闲游戏通常不需要复杂的点击测试
  • 休闲游戏通常没有大量的“对象”

在游戏库中(例如,流行的力学引擎),图形模型是独立的,并且图形成为了一种实现细节。图形几何特征,比如边界、速度、大小和位置都提供给引擎,随后引擎使用速度、碰撞和位置进行响应。图形仅用于在屏幕上获取计算的场景。

图形与游戏逻辑独立的概念在两个游戏中得到了展示,这两个游戏由同一个人开发,旨在分别强调 SVG 和 <canvas>:SVG-oids 和 canvas-pinball

尽管游戏和演示逻辑是不同的,但二者都利用了相同的力学引擎,该引擎跟踪位置、碰撞、速度和游戏组件的其他力学方面。最后,一个人可以使用 SVG 绘制(或移动)游戏元素,其他人可以使用 Canvas 重新绘制它们。

如今为 HTML5 构建的大部分 2D 休闲游戏都使用了 Canvas,所以我们将此场景放在交叉点上靠近 Canvas 的一端。

2D 休闲游戏

交互式图表和图形

实时高容量数据表示

高性能(滤镜、光线跟踪器)

用于查看和打印的高保真度文档

混合交叉

静态图像

clip_image028
混合场景

休闲游戏属于混合场景,因为它能够利用两种技术的优势。对于轻松的点击检测和用户交互,SVG 几何结构的一个最不透明的层可用于定位元素,而底层 Canvas 可非常快地定位相关图像并提供实时动画。

休闲游戏领域外部越来越多的人发现使用混合场景很有吸引力。当一个场景同时包含生动的动态图形和动画的需要 (Canvas) 以及丰富的用户交互的需要 (SVG) 时,这两种技术都应该使用。一个代表场景就是 The Beauty of the Web网站上展示的来自我们的一家合作伙伴的 Brain Power 网站。这个 Brain Power 网站(以及 The Beauty of the Web 上的其他特色网站)已发现了此恰当的平衡。

对于 Brain Power 的用户交互和显示部分,该网站利用了更高级的 SVG 几何结构:

<polygon id="SensoryCortex" points="253,80,266,93,…" />

对于实时动画和特殊效果,可以使用 Canvas:

<canvas id="cnvDisplay" width="1920" height="1099" />

结束语

对最新的现代浏览器中可用的现有矢量图技术的分析表明,可以通过一种交互式方式使用标准 Web 技术创建新的场景。

包含丰富图形的 Web

实时高容量数据表示

交互式图表和图形

混合交叉

用于查看和打印的高保真度文档

静态图像

高性能(滤镜、光线跟踪器)

2D 休闲游戏

clip_image030

Web 的持续演化将不断继续,在其核心位置包含更加丰富的图形。我们提供了一种关于将这些技术应用到特定场景的观点。最后,Canvas 和 SVG 都是包含丰富图形的 HTML5 Web 的重要组件。

我们期待听到您如何将这些新 HTML5 技术应用到您的网站。包含 URL 并通过包含 HTML5 doctype、<!DOCTYPE html> 和使用功能检测(而不是浏览器检测)来了解支持 SVG 还是 Canvas;确保您的页面适用于 IE9。