Skip to main content

如何为您的网站在Canvas和SVG之间做出选择

作者:Patrick Dengler,高级项目经理,Internet Explorer

Canvas 和 SVG 是 Internet Explorer 9 中推出的两个让人激动不已的图形特性,并且是可硬件加速的。这些技术可用于解决现代 web 上与图形有关的各种问题。由于 Canvas 更让人激动不已,人们开始遗忘 SVG,然而在很多情况下,SVG 反而是更好的选择。我将在本文中介绍何时选择 Canvas、SVG 或结合使用两者的一些想法。

Canvas和SVG概述

下面从较高的层面介绍 Canvas 和 SVG,旨在讨论如何在这两种矢量图形技术之间做出选择。

Canvas和SVG的比较
CanvasSVG
基于像素(canvas 实质上是一种带有绘图 API 的图像元素)基于对象模型(SVG 元素类似于 HTML 元素)
单个 HTML 元素,其行为类似于 <img>多个图形元素,是文档对象模型 (DOM) 的一部分
通过脚本以编程方式创建和修改视觉呈现使用标记创建视觉呈现,并通过 CSS 或通过脚本以编程方式修改视觉呈现
事件模型/用户交互是粗粒度的—仅在 canvas 元素级别;必须通过鼠标坐标对交互进行手动编程设置事件模型/用户交互是基于对象的,在最基本的图像元素级别进行——线条、矩形、路径
API 不支持可访问性;除了 canvas,还必须使用基于标记的技术SVG 标记和对象模型直接支持可访问性

SVG 是一种保留模式 (retained mode)的图形模型,是一种在内存中进行持久处理的模型。类似于 HTML,SVG 构建了包含图元、属性和样式的对象模型。在 HTML5 文档中出现 <svg> 元素时,它的作用类似于一个内联块,并且是 HTML 文档树的一部分。

Canvas 是一种位图,采用直接模式 (immediate mode)的图形应用程序编程接口 (API) 在其上进行绘图。 Canvas 是一种“发后不理”(Fire and Forget) 模型,在这种模式下,将直接向位图呈现其图形,之后对绘制的形状并不知晓;最后只呈现生成的位图。 

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

方案

以下各节介绍了这两种技术各自的优点和局限性,包括判断应该为给定任务选择哪种技术的一般方法。下图演示了从 Canvas 到 SVG 的范围内每种方案的位置,在两者之间有一个明显的交叉点。

Spectrum of Web vector graphics from canvas to SVG

矢量图形范围

高保真复杂矢量文档

高保真复杂矢量文档一直以来是 SVG 的最大亮点,并且以后将继续如此。它为查看和打印单独的内容或网页中嵌入的内容实现了极高的细节。SVG 的声明性本质提供了工具,在客户端或服务器端根据数据库生成图形。

来自 Internet Explorer测试驱动 真实图形

第一幅图显示了原图,而第二幅图显示了这些图形放大 1000% 后的效果。

需要查看很大的示意图,但是需要深入了解细节,或为了工程目的而打印整个文档时,那么可缩放矢量图形的“可缩放”特性就变得极其重要。考虑到这些原因,我们将高保真复杂矢量文档放到范围中的 SVG 端。

SVG作为一种图像格式

SVG 的另一个常见用途是 web 页面内的静态图像。针对当前的高 DPI 显示器,开发人员必须要考虑图形的质量。下图表现了经过 CSS 样式化的 <li> 项目符号图像。在显示和文件大小方面,下面两个图像几乎是相同的。


左侧为 SVG 图形,右侧为图像的 PNG 呈现

如果开发人员希望大量重用该图像,或者最终用户使用的是高 DPI 屏幕,那么光栅图像将出现像素化,或是需要更大的文件版本才能保持图像的保真度。


左侧为放大后的 SVG 图形,右侧为放大后的 4K PNG 图形

因此,SVG 可以作为一种出色的图像替换格式,即使是 web 页面上最简单的图像也是如此。 Canvas 无法提供合适的替换图像。

在范围的另一端,canvas 为不需要保留绘制内容的方案带来了更快的速度。首次引入 Canvas 时,曾经进行了很多有趣的试验。我将这些试验分为三种不同的方案。

像素处理

由于 Canvas 主要是绘制和处理基于像素的绘图图面,因此,有关 Canvas 的一些试验和演示包括用复杂的算法实现出色的图形效果,如光线追踪或滤光器。

下面的例子由 Adam Burmister 编写。该试验通过以下方式创建了一个图像:通过图像平面中的像素跟踪光线路径,并模拟光线打到虚拟对象上的效果。

作者提出了以下警告:“这个试验非常耗费 CUP 资源。您的浏览器可能看似停止响应了。”因此,虽然 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 无法实现这种像素处理。这是这两种技术之间的区别性因素。Canvas 处理的是像素,而 SVG 处理的是模型。

不管是用简单的矢量图形创建逼真的图像,还是在视频中营造绿屏效果,在大多数情况下,最初这些图形方案还没有准备好大量部署到当今的 Web 中。然而,某些方案具有足够快的响应速度(例如应用滤光镜来去除照片中的红眼)。这些像素处理方案位于范围的最左侧,属于 canvas 应用方案。

混合和交叉方案

最有趣的用例集并没有指出哪种技术是最终的胜利者。这些用例可以通过两种主要方案进行演示:制表/制图/绘制地图和二维游戏。

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

SVG制表/制图/绘制地图方案

Web 上常见的图表和图形集合包括:

·    交互式组织结构图和流程图

·    交互式地图 – 道路查找

·    建筑楼层平面图

·    工程示意图

·    航线或活动场地的座位图

·    通用数据或财务图表(列、栏、线、分散内容、圆环等)

对于所有这些内容,SVG 都是最佳的技术选择,因为:

·    通过将 XML 转换为 SVG,可以轻松地利用现有数据生成这些内容

·    可以通过工具导出静态版本(包括 Inkscape、Adobe Illustrator、Microsoft Visio 和各种 CAD 程序) 

·    它们需要精确的用户交互

·    第三方内容提供商可以为使用 CSS 样式的 Web 作者进行自定义

·    它们需要可访问性

为了更准确地进行说明,让我们看看下面的方案:从美国地图中选择某个州。

上面显示的 阿拉斯加州详细地图无版权限制,可以从 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();

}

实际上它需要 2878 个路径绘制指令(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; }

这类交互地图的一个例子可以从测试驱动演示 Atlas zur Europawahl 2004 in Deutschland 中找到,这是 2004 年德国的欧洲选举结果的图形化表示。

在 canvas 中,创建这些效果要求您使用事件对象的鼠标坐标编写自己的命中检测代码。没有什么形状的上下文了。虽然有一个isPointOnPath() API,但它只适用于最后创建的路径。

代码可以并且确实以图形库的方式存在,支持使用像素数据在图形上进行具体的命中检测,从而识别命中和悬停效果以及确定这些功能是否工作。也可以在 SVG 中使用代码,并且如果代码充分利用了 SVG 特性,那么可获得更好的性能。

Canvas制表/制图方案

Canvas 也可以用于制表和制图方案。为了对此设定上下文,我们需要查看 SVG 和 Canvas 的性能特征。

有时,在外部因素的影响下,需要选择一种独立于或很大程度上独立于功能的技术。对于 SVG 和 Canvas,有两个主要的区别因素。

开发人员的知识、技能集和现有资产在选择技术的过程中扮演了重要的角色。如果在开发游戏的过程中,开发人员非常精通低级图形 API,而对 Web 技术知之甚少,那么可能选择的技术就是 Canvas(稍后将详细介绍)。在移植游戏时,有很多工具支持将游戏从第三方实现迁移到 Canvas。

如果性能非常关键,经常需要实现毫秒级性能,那么需要比较这两种技术的性能特征。这并不意味着通常被认为具有较高性能的 Canvas 就是最明显的选择。然而,对于需要在像素级绘制大量数据的应用程序,目前为止 Canvas 是更好的选择。

下面的气象图不需要较大的表面面积,屏幕上的对象数量非常多。使用 Canvas,可以快速地绘制出这些对象,不需要对 DOM 进行更新。

虽然也可以使用 SVG 完整地创建上图,使用圆形和椭圆形元素表示圆点,但是将数千个元素加载到 DOM 中会非常慢。只要出现大量的像素或图像,那么很明显应该使用 Canvas 技术——无论是用于天文、生物细胞移动显示还是语音调制显示。此处的限制是将数据转变为图形的速度,这取决于 CPU 的速度、Canvas 实现的速度以及 JavaScript 实现的速度。

二维游戏

休闲游戏是要介绍的最复杂的方案。一些初步观察结果包括:

·    游戏库利用较低级的图形 API

·    游戏行业开发人员的技能集开始转向这些较低级 API

·    很多游戏在很大程度上是基于图像或子画面的

·    诸如 Adobe 等供应商开始支持 Canvas 作为一种导出方法

·    休闲游戏通常不需要复杂的命中测试

·    休闲游戏通常没有大量的“对象”

在游戏库中,流行的物理引擎和图形模型是独立的,并且图形变成了实现细节。图形几何,如边界、速率、大小和位置被传递给引擎,后者随后通过速率、碰撞和位置进行响应。图形仅用于获得屏幕中的计算情景。

通过两个游戏演示图形独立于游戏逻辑这一概念,这两个游戏由同一个人开发,目的是凸显 SVG 和 <canvas> 的特点: SVG-oids canvas-pinball

虽然游戏和演示逻辑不同,但是都利用了相同的物理引擎,该引擎跟踪游戏组件的位置、碰撞、速率和其他物理属性。最后,一个游戏使用 SVG 绘制(或移动)游戏的元素,另一个则使用 Canvas 重绘这些元素。

目前针对 HTML5 开发的大部分 2D 休闲游戏都使用 Canvas,因此我们将这个方案放到范围中交叉点的 Canvas 一侧。

混合方案

休闲游戏也属于混合方案,因为结合利用这两种技术的优点也是种优势。要实现轻松的命中检测和用户交互,可以使用 SVG 几何图形的非透明层来定位元素,而底层的 Canvas 可以更快速地定位相关图像并提供实时动画效果。

在休闲游戏以外的领域,人们逐渐发现结合使用这两种技术可以取得很好的效果。方案中同时需要大量可视的动态图形和动画效果 (Canvas) 以及丰富的用户交互 (SVG) 时,可以并且应当结合使用这两种技术。来自我们的合作伙伴之一的 Brain Power 站点展示了这一点,该站点在 The Beauty of the Web 网站中展示。该 Brain Power 站点(以及 The Beauty of the Web 上展示的其他内容)将两者很好地结合在一起。

对于用户交互和大脑位置的显示,该网站利用了 SVG 更高级的几何特性:

<polygon id="SensoryCortex" points="253,80,266,93,…" style="fill:rgba(0,0,0,0)" />

对于实时动画和特殊效果,则采用 canvas 实现:

<canvas id="cnvDisplay" width="1920" height="1099" style="position:absolute;" />

结束语

对最新的现代浏览器中当前使用的矢量图形技术的分析表明,可以通过交互方式使用标准的 Web 技术实现新的方案。观看以下视频,了解有关如何针对 Canvas 和 SVG 进行开发的更多信息:

·         Deep Dive Into HTML5 <canvas>

·         Modernizing Your Website: SVG Meets HTML5

Spectrum from canvas to SVG showing "the graphically rich Web" at the center of spectrum

不断发展的 Web 将继续容纳更丰富的图形。我们介绍了有关将这些技术应用到特定方案的一种观点。最后,对于具有丰富图形的 HTML5 Web,Canvas 和 SVG 都是非常重要的组成部分。

我们希望倾听您讲述如何将这些新的 HTML5 技术应用到自己网站的故事。包括 URL,请包含HTML5 doctype <!DOCTYPE html> 以确保您的网页可以在 IE9 中正常工作,并且使用特性检测(而不是浏览器检测)来确定 SVG 或 Canvas 是否受支持。