css canvas
在这个由两部分组成的系列的第1部分中,学习有关创建结合canvas API和HTML / CSS模型优点的应用程序的信息。 对于需要高性能,低开销图形的Web应用程序,canvas API可能是一个不错的选择。 但是,在不考虑传统HTML / CSS模型有用性的情况下,围绕画布构建整个应用程序可能是短视的。 与其将其视为理所当然,还不如学习利用HTML / CSS的许多有用方面,这些方面在画布上很难甚至不可能实现。
本文研究了一种“混合”方法来设计使用传统HTML组件和画布元素的应用程序。 将讨论这种方法的动机,包括为什么纯基于画布的实现可能远非理想的原因,以及与画布API相比传统HTML模型的优缺点的考察。 几个示例还可以帮助您在设计应用程序时规划HTML和canvas元素之间的布局和交互。
您可以下载本文中使用的示例的源代码。
HTML和CSS模型的优势
使用HTML和CSS的传统网络模型在某些情况下表现出色。 本节回顾了在画布API中没有特别好(或根本没有)支持HTML模型的最有用的好处,尤其是它强大的文本支持以及与语义上有意义HTML标记相关的好处。
HTML强大的文本支持
HTML的主要优势在于它能够使用样式提示轻松注释文本,从格式标记(例如<b></b>
到CSS规则(例如font-weight:bold
。
另一方面,canvas API公开了fillText()
方法,该方法将文本字符串呈现为位图。 这是一个简单的,低级的调用,但有很多限制。 最大的限制之一是只能将一个统一样式规则应用于提供给fillText()
的文本字符串。 在这种情况下,“样式规则”可以看作是设置字体外观,大小,颜色等的单个规则(类似于CSS规则)。
具有统一样式的文字
考虑图1中的文本。
图1.示例文本

由于文本始终包含统一的样式规则(红色,斜体,Arial字体),因此您可以使用HTML / CSS或画布轻松轻松地同样好地呈现此文本。
清单1和清单2中的代码比较了如何使用HTML和CSS以及canvas渲染此文本。 请参阅相关主题的工作示例。
清单1显示了图1中以HTML和CSS呈现的文本。
清单1.用HTML和CSS用统一样式渲染文本
<style>
.uniform-style {
font: italic 12px Arial;
color: red;
}
</style>
<span class="uniform-style">
Variety is the spice of life!
</span>
清单2显示了使用画布渲染的相同文本。
清单2.在画布中渲染具有统一样式的文本
var canvas = document.getElementById('my_canvas');
var context = canvas.getContext('2d');
context.font = 'italic 12px Arial';
context.fillStyle = 'red';
context.fillText('Variety is the spice of life!', 0, 50);
具有动态样式的文本
考虑图2中的文本。
图2.具有动态样式的文本
清单3说明了呈现此文本所需CSS规则。
清单3.用动态样式在HTML和CSS中呈现文本
<style>
.dynamic-style {
font: 12px Arial;
color: blue;
}
.dynamic-style strong {
font-size : 18px;
color: green;
}
.dynamic-style em {
color: red;
font-weight: bold;
font-style: italic;
}
</style>
<span class="dynamic-style">
<strong>Variety</strong> is the <em>spice of life</em>!
</span>
由于canvas API中不存在HTML标记或CSS类的概念,该概念允许您注释使用不同的字体样式,因此在上一示例中呈现相同的文本块要复杂得多。
因为canvas API充当顺序状态机,所以要重现图2中的效果,就需要将其描述为“类似打字机”的方法。 您必须选择要使用的样式,键入需要使用该样式的文本部分,选择要使用的其他样式,键入文本的下一部分,依此类推。
通常,这可以使用以下算法来完成:
- 设置用于下一部分文本的样式(18px Arial,绿色)。
- 将文本的下一部分渲染到画布上(Variety)。
- 向右移动一定数量的像素(60)。
- 对文本的每个部分重复步骤1-3。
清单4显示了该算法的最基本,最简单的实现。
清单4.在画布中用动态样式渲染文本
context.font = '18px Arial';
context.fillStyle = 'green';
context.fillText('Variety', 0, 50);
context.translate(60, 0); //move 60 pixels to the right (a)
context.font = '12px Arial';
context.fillStyle = 'blue';
context.fillText('is the', 0, 50);
context.translate(35, 0); //move 35 pixels to the right (b)
context.font = 'italic bold 12px Arial';
context.fillStyle = 'red';
context.fillText('spice of life!', 0, 50); // (c)
图3说明了三个单独的文本块在按顺序渲染时将如何显示。
图3. Canvas中的文本呈现
在本文的后面,您将看到如何通过在本机画布fillText()
函数之上添加另一层抽象来对该方法进行一些改进。 无论采用哪种方法,相同的打字机算法仍将是核心。
应该注意的是,HTML API本身并不支持HTML / CSS中被视为理所当然的大多数文本样式,例如自动换行,字母间距和行高。 如果需要,有必要自行修改算法并实现功能。
到现在为止,显而易见的是,为什么文本渲染对于canvas API来说不是一个简单的问题,以及为什么在用最少的精力格式化文本时传统HTML模型就占了上风。
HTML的有意义的语义
好的网页设计的基础是有意义的,语义正确HTML结构。 HTML标记具有许多好处,这些标记在编写时就牢记了适当的结构和含义。
canvas的核心只是一个图形绘图仪,因此不存在有意义的标记的概念,并且对于纯粹为canvas编写的应用程序,有意义HTML标记的好处将基本消失。 例如,从搜索引擎和有视觉障碍的用户的角度来看,canvas元素实际上将显示为一无所有的黑盒子。
下面,我们将研究有意义HTML标记带来的一些好处。
搜索引擎意识
搜索引擎根据自动搜索机器人/蜘蛛读取的信息创建搜索索引-当搜索蜘蛛访问您的网页时,它会解析HTML以提取尽可能多的有意义的信息。
由于以编程方式呈现在画布上的文本最后只是一个位图,因此搜索蜘蛛将完全忽略该文本。 因此,重要的是要理解,如果您的应用程序是专门在画布中开发的,那么搜索蜘蛛将能够从您的页面中收集很少的上下文信息。
尽管如此,搜索引擎仍将能够读取用于非HTML5兼容浏览器的<canvas>
元素内的任何后备文本,如清单5所示。
清单5. canvas元素内的后备文本
<canvas>
We are sorry, your browser doesn't support HTML5!
</canvas>
对于画布而言,这并不是一个特别新颖的问题-缺乏搜索引擎意识一直是为Flash和Silverlight等浏览器插件编写的应用程序所面临的问题。
辅助功能
视力受损的用户依靠文本语音转换工具(屏幕阅读器)阅读网页上的内容。 结构化HTML标记使屏幕阅读器工具可以了解页眉,页脚,列表等之间的区别。 不产生HTML标记的应用程序(例如基于画布的应用程序)将不会留下任何内容的痕迹,这些内容是屏幕阅读器可以识别的。
结构良好HTML还使用户能够使用标准的键盘快捷键来浏览页面。 例如,Tab键会将焦点移至页面上的下一个链接,如图4所示。
图4.使用Tab键将焦点移至页面上的链接

甚至Flash和SIlverlight之类的浏览器插件也通过支持常用的键盘快捷键在提供可访问性方面取得了长足的进步。 不幸的是,canvas没有提供类似的内置可访问性合规性。
设备和用户代理
最终,由用户代理决定如何呈现您的页面。 某些设备对同一HTML标记的解释可能与其他设备不同。 例如,在大多数移动设备上,点击<input type="text">
元素会导致设备显示屏幕键盘,以便用户可以输入输入。 符合HTML5规范的浏览器提供了更广泛的可用输入类型,例如电子邮件,网站,电话号码,日期等。
图5显示了<input type="date">
元素在iOS设备上的显示方式。 浏览器会自动显示功能齐全的日期选择器; 不需要额外的代码需要实现比定义作为输入元件的类型之外的日期选择器date
。
图5. iPhone对<input type =“ date”>的处理
在编写语义正确HTML时,您实际上是在与用户代理签约,并相信用户代理能够以最佳方式为该特定设备的用户呈现内容。
相反,在canvas元素中呈现的所有内容在所有设备上看起来始终相同。 您正在与用户代理达成协议; 不再信任该设备最清楚如何呈现您的内容。
超链接意识
网络的核心是建立在超链接周围,很难想象没有它们的世界。 除了允许用户导航到链接之外,现代的用户代理还为用户提供了许多附加的上下文功能:在新选项卡中打开页面,复制链接地址,电子邮件链接等。
尽管可以将文本呈现到画布上,但从技术上讲,不存在超链接的概念。
即使您自己想通过渲染带下划线的蓝色文本来模拟超链接,用户代理也不会意识到这是一个真正的超链接,并且将无法为用户提供任何有用的上下文功能。
canvas API的优势
考虑到HTML的优势,对于为canvas编写的应用程序来说,情况似乎很严峻。 但是,围绕画布设计解决方案有几个好处。 主要优点在于画布的高度图形性能和跨设备一致性的潜力。
图形表现
HTML和canvas可以完成某些任务,例如渲染图像,文本和动画。
由于canvas不会承受与解析HTML和维护分层文档模型相关的开销,因此在canvas中完成这些任务时,它们的性能将始终更快。
在以HTML为中心的应用程序中,主要的性能杀手是节点的添加,删除或更新。 对文档模型的这些更新通常迫使浏览器重新绘制或重排整个页面,这可能是一个计算量非常大的过程。 在实时应用程序中每秒必须多次对文档模型进行更新,这可能会使浏览器的运行速度变慢。
当速度是最重要的时候,从以HTML为中心的体系结构转换为以画布为中心的体系结构可能是实现最佳性能的圣杯。 高级别的性能是以处理低级API而不是以健壮的标记语言(如HTML和CSS的灵活性)为代价的。
跨设备一致性
由于浏览器布局引擎的差异,一致的跨平台外观一直是传统HTML / CSS模型的难题。 尽管可以使网页在各个平台上看起来非常相似,但是很难实现一致设计的“像素完美”水平。
将图形绘制在画布上时,这些问题已消除。 Canvas可以对图形和文本的外观进行像素级的控制,并确保在所有平台上输出都是100%一致的。
canvas / HTML混合的原理
创建面向画布的应用程序时,可以利用传统HTML / CSS模型的功能来克服画布的缺点。
如前所述,canvas的最大局限之一是难以实现健壮的,可访问的UI组件。 在决定实现UI的最佳方法时,评估UI元素的需求可能是有用的练习。 要考虑的标准可能包括:
- UI元素的期望内容是什么?
- 此内容必须多久更新一次?
- 需要什么水平的交互性?
这些领域中的任何一个强烈要求都可以极大地建议一个实施方案或另一个实施方案。 例如,如果所需内容是应用程序某些方面的简单文本显示,则基本HTML元素可能是最有效的,并且不会导致任何性能损失。 但是,如果您希望该元素具有动画效果或包含频繁更新的内容,则使用画布可能是一种更好的方法。 如果要求易于交互,则HTML元素通常提供最自然的方法,尤其是与jQuery之类的库提供的功能配对时。
下面,我们将研究许多非常适合混合canvas / HTML实现的情况。
快速原型
使用HTML和CSS可以比使用画布更快地完成快速原型的构建。 例如,考虑具有菜单的应用程序。 因为菜单非常适合HTML / CSS结构,并且您可以利用大量现有的库(例如jQuery插件),所以与在画布中等效实现相比,在HTML中实现菜单的工作量微不足道。 与HTML元素相关联的性能消耗与我们的原型无关紧要,因为它旨在作为概念验证而不是完全优化的系统。
当应用程序完全充实后,可以在纯画布中实现利用HTML和CSS的原型方面。 以菜单示例为例,在最终产品中将菜单转换为画布将显着提高性能,因为您将删除对浏览器文档模型的依赖。
开发工具
鉴于易于使用HTML标记显示大量信息,因此开发工具非常适合HTML和CSS。 一个示例是在应用程序中添加控制台以进行调试。 调试控制台可能会显示统计信息,例如每秒帧数,应用程序中所有对象的列表及其坐标,等等。
同样,在这种情况下,引入HTML元素的性能影响不大,因为这些工具仅面向开发人员,永远不会显示给最终用户。
用户界面覆盖
如果应用程序需要将文本与表单元素(例如复选框和下拉菜单)结合在一起的丰富UI,那么HTML是自然的解决方案。 在canvas中复制这些类型的组件可能非常耗时,并且您的应用程序可能会遇到可访问性问题。
相反,HTML的优势可以提供丰富的用户体验。 例如,基于HTML的UI组件的集合可以包括浮动在画布区域上方的“抬头”显示。
为了最大程度地减少对应用程序实时性能的影响,请注意对文档模型进行尽可能少的更改(元素更新,插入和删除)。 如果文档更新频繁,则应用程序的性能可能会降低爬网速度。
伴侣用户界面
当UI元素打算出现在应用程序的交互式实时组件之外时,这可能是使用HTML和CSS的绝佳机会。 例如,UI元素可能仅在介绍性启动序列中或在应用程序暂停时出现。
加载/保存游戏界面是另一个很好的示例,其中可以使用基于HTML的伴随UI。 由于在用户保存游戏时游戏会暂停,因此不会影响应用程序的实时性能。 HTML / CSS将允许快速开发此界面,从而获得结构良好的标记的所有好处。
实现画布/ HTML混合
本节介绍了将画布元素与网页布局内的其他HTML元素组合在一起的几种基本方法。 尽管不详尽,但以下概述的方法提供了几个示例,这些示例说明了如何在最大程度地利用HTML和canvas优点的同时最大程度地减少它们的缺点。 在规划应用程序的体系结构时,请考虑哪种方法(或一组方法)最适合您的目标。
以下方案假定了两个主要组成部分:
- canvas元素,主要包含图形和动画组件
- HTML元素,其中包含UI的不同方面,例如菜单,导航元素,表单等
流动画布元素
<canvas>
标记被认为是一个块级元素,因此将HTML和canvas元素组合在一起的最简单方法是在文档正文中简单地插入一个<canvas>
元素。 由于默认情况下该元素将相对放置,因此画布元素可以与文档的其余部分一起流动而无需特殊的标记或CSS。
画布作为前景元素
<canvas>
元素可以通过z-index绝对定位在页面上,从而出现在前景中。
这种方法适用于不会与页面上找到的其他HTML元素进行交互的画布应用程序,类似于嵌入Flash应用程序。
因为画布是前景中最顶部的元素,所以canvas元素将能够捕获所有形式的用户输入(鼠标单击,触摸事件等)。
图6说明了将画布定位为绝对定位的前景元素的方法。
图6.在文档上方分层画布元素

清单6中的代码演示了使用大的z-index将元素放置为最顶层前景图层所需CSS。
清单6.将画布定位为前景元素CSS
canvas {
position: absolute;
z-index: 100;
bottom: 0px;
right: 0px;
}
画布作为背景元素
<canvas>
元素也可以用于在网页背景上显示内容。
此方法可用于在标准HTML内容下生成丰富的动画背景场景。 背景画布场景将通过文档中的所有透明区域显示为可见。
图7.在文档下面分层画布元素

清单7中的代码显示了如何通过设置负z-index将canvas元素定位为最底层。
清单7.将画布定位为背景元素CSS
canvas {
position: absolute;
z-index: -1;
top: 0px;
left: 0px;
}
在画布上分层
另一种方法涉及在画布元素上方分层一个或多个HTML元素。 如图8所示,对于需要大量UI组件的应用程序,此方法可能是强有力的竞争者。 在这种情况下,一组基于HTML的UI组件将浮在画布提供的图形层上方。
图8.在画布上方分层HTML元素

每个单独的UI组件都可以使用CSS进行排列,以便它们出现在画布上方前景中的某个位置。
清单8显示了一个示例,该示例如何通过设置高z-index来放置<div>
元素中包含的UI组件。
清单8.将HTML组件定位为前景元素CSS
div.user-interface {
position: absolute;
z-index: 100;
top: 50px;
left: 50px;
}
由于基于HTML的UI组件位于最顶层,因此默认情况下它们将响应用户输入。 例如,如果某个元素包含UI元素(例如复选框,滚动条或超链接),则这些元素将拦截用户的鼠标单击。 通常,单击这些元素之一永远不会落入下面的画布表面。 但是,在某些情况下,可能需要用户输入进入画布。
可以通过设置指针事件CSS属性将特定HTML元素配置为忽略用户输入,如清单9所示。将指针事件设置为none
会导致该元素忽略通常与用户输入关联的所有行为,即所有文本。 ,超链接和该<div>
表单元素将是不可选择的,并且基本上是无法使用的。
清单9.设置指针事件属性CSS
div.user-interface {
position: absolute;
z-index: 100;
top: 50px;
left: 50px;
pointer-events:none;
}
输入事件落在下面的层上,其中<canvas>
元素可以拦截该事件。 例如,可以在捕获鼠标移动事件的<canvas>
元素上放置一个事件侦听器。
所有浏览器都支持指针事件的属性,但Internet Explorer除外(存在一些替代方法来模仿Internet Explorer中的类似行为)。
使用输入表面在画布上分层
捕获用户输入的另一种方法与以前的方法类似,但是不需要使用pointer-events:none
CSS属性。 用这种方法,在canvas和HTML UI元素的顶部增加了一层。 添加附加层仅是为了捕获用户输入。
此方法适用于HTML元素仅用于显示目的的情况。 无法与HTML界面元素进行交互,因为输入表面会在所有事件到达基础元素之前捕获它们。 图9显示了一个示例。
图9.在画布上用额外的输入表面将HTML元素分层

您可以通过创建绝对定位的<div>
来实现输入表面,该宽度和高度与画布相同。 将<div>
留为空白实际上是为您提供了一个透明层,其唯一目的是捕获用户输入。
要捕获用户输入,只需将所有事件侦听器附加到此<div>
。 因为它是最顶层的元素,所以所有用户输入都将被此元素而不是基础元素捕获。
下一步
本系列的第2部分将扩展本文涵盖的概念,并逐步介绍混合canvas / HTML应用程序的示例实现。 您将逐步了解如何在画布游戏之上添加基本HTML UI,以及如何在画布中创建UI元素。 第2部分还将讨论动画和文本渲染的注意事项,并说明如何利用HTML和canvas的优势。
图10展示了第2部分中构建的示例游戏概念。
图10.结合了HTML和Canvas元素的示例应用程序
结论
在本文中,您了解了HTML模型与canvas API相比的优缺点,构建混合canvas / HTML应用程序的原因以及用于分层HTML和canvas元素的不同方法。
现在,您正在考虑如何选择一种架构,以最好地利用HTML和canvas的优势,请注意本系列的第2部分,在这里您将可以使用新技能来实现太空射击游戏。
翻译自: https://www.ibm.com/developerworks/opensource/library/wa-htmlmark/index.html
css canvas