如何为您的网站选择 Canvas 和 SVG






4.87/5 (11投票s)
Canvas 和 SVG 是 Internet Explorer 9 中引入的两个令人兴奋的图形功能,它们都经过硬件加速。这些技术可用于解决现代 Web 上的一系列图形场景。尽管人们对 Canvas 充满热情,但人们往往忽略了 SVG,而在许多情况下,SVG...
Canvas 和 SVG 的高级概述
以下是 Canvas 和 SVG 的高级概述,旨在为讨论何时使用一种矢量图形技术而不是另一种提供框架。
Canvas 和 SVG 的比较 | |
画布 | SVG |
基于像素(Canvas 本质上是一个具有绘图 API 的图像元素) | 基于对象模型(SVG 元素类似于 HTML 元素) |
单个 HTML 元素,行为类似于 <img> | 多个图形元素,成为文档对象模型 (DOM) 的一部分 |
通过脚本以编程方式创建和修改的视觉呈现 | 通过标记创建,并通过 CSS 或脚本以编程方式修改的视觉呈现 |
事件模型/用户交互粗糙——仅在 Canvas 元素上;必须从鼠标坐标手动编程交互 | 事件模型/用户交互在原始图形元素(线条、矩形、路径)级别上是面向对象的 |
API 不支持可访问性;除了 Canvas 之外,还必须使用基于标记的技术 | SVG 标记和对象模型直接支持可访问性 |
SVG 被称为 **保留模式** 图形模型,保存在内存模型中。类似于 HTML,SVG 构建了元素、属性和样式的对象模型。当 <svg> 元素出现在 HTML5 文档中时,它的行为类似于内联块,并且是 HTML 文档树的一部分。
Canvas 是一个位图,带有一个用于在其上绘图的 **即时模式** 图形应用程序编程接口 (API)。Canvas 是一个“即时生成、即时消失”的模型,它直接将图形渲染到其位图,然后就没有对所绘制形状的感知了;只有生成的位图会保留下来。
一种思考方式是,Canvas 类似于 Windows GDI API,您通过编程将图形绘制到窗口;而 SVG 类似于带有元素、样式、事件和基于 DOM 的可编程性的 HTML 标记。Canvas 是过程式的,而 SVG 是声明式的。
场景
以下各节描述了这两种技术的技术优势和局限性,包括确定何时一种技术适用于特定任务的常识性方法。下图说明了每个场景在 Canvas 到 SVG 的光谱上的位置,中间有一个明确的交叉点。
高保真复杂矢量文档
高保真复杂矢量文档一直是 SVG 的优势领域,并且将继续如此。它们非常详细,适合查看和打印,可以独立使用,也可以嵌入到网页中。SVG 的声明式特性为从数据库生成形状提供了工具或客户端/服务器端生成。
来自 Internet Explorer Test Drive 上的 “真实世界数据和图表” 演示
第一张图显示了图表,而第二张图显示了这些图表放大到 1000% 的效果
当您考虑到查看大型示意图的实用性,但又需要深入细节,或者出于工程目的打印整个文档时,“可伸缩”(Scalable)在可伸缩矢量图形中的含义就显而易见了。出于这些原因,我们将高保真复杂矢量文档放在光谱的 SVG 端。
SVG 作为图像格式
SVG 的另一个常见用途是作为网页中的静态图像。在当今高 DPI 显示器上,开发人员必须考虑图形的质量。下图代表了通过 CSS 设置样式的潜在的 <li> 项目符号图像。以下图像在显示和文件大小方面几乎相同。
如果开发人员希望以更大的规模重用该图像,或者最终用户使用的是高 DPI 屏幕,那么栅格图像会变得模糊,或者需要更大版本的该文件以保留保真度。
因此,SVG 可以成为网页上最简单图像的绝佳图像替换格式。Canvas 无法提供合适的替代方案。
在光谱的另一端,Canvas 为不需要保留绘制内容的场景带来了速度。当 Canvas 最初推出时,开发了许多有趣的实验。我将这些实验分为三种不同的场景。
像素处理
由于 Canvas 完全围绕着绘图和操作基于像素的绘图表面,因此许多 Canvas 的实验和展示包括复杂的算法来实现令人印象深刻的图形效果,例如光线追踪或滤镜。
下面的 示例 由 Adam Burmister 编写。该实验通过追踪光线穿过图像平面像素的路径并模拟其与虚拟对象相遇的效果来创建图像。
作者本人包含以下警告:“这非常消耗 CPU。您的浏览器可能会显得无响应。” 因此,尽管 Canvas API 能够生成此类图片,但这可能不是一个好主意。正如网站作者 Adam Burmister 总结 的那样,“光线追踪是 [是] 有史以来最糟糕的 JavaScript 应用。”
同样可以说其他的场景级像素处理。以下函数用另一个相同大小的 Canvas 中的像素替换一个 Canvas 中的绿色像素。这样的函数可以用来创建视频“绿屏”效果。
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 中根本不可能实现。这是两种技术之间 **的差异化因素**。一种处理像素,另一种处理模型。
无论是从简单的矢量图形创建逼真的图像,还是使用视频创建绿屏效果,这些图形场景在大多数情况下尚未准备好在当今的 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();
}
事实上,它需要 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 的特性,性能会更好。
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 开发的信息,请观看这些视频:
Web 的持续发展在其核心不断包含更丰富的图形。我们提供了一种关于将这些技术应用于特定场景的观点。最终,Canvas 和 SVG 都是 HTML5 图形丰富 Web 的重要组成部分。
我们很乐意听到您如何将这些新的 HTML5 技术应用到您的网站上。请提供 URL,并且*请*确保您的页面在 IE9 中正常工作,包括 HTML5 文档类型 `<!DOCTYPE html>`,并使用功能检测而不是浏览器检测来了解 SVG 或 Canvas 是否受支持。