使用 HTML5 和 SVG 应对僵尸启示录,第二部分:文本、路径和基本动画





5.00/5 (1投票)
在 HTML5 的所有功能中,SVG 得到的关注实在太少了。了解它如何创建具有视觉吸引力的页面,同时可能挽救您免于即将到来的僵尸启示录。
本系列的第一篇文章介绍了 SVG 的基础知识,SVG 是 HTML5 中一个被忽视、被低估的方面。虽然不同的浏览器以不同的方式实现 SVG,但之前的教程描述了如何在所有浏览器都能识别的方式下创建文本、集成 SVG 图像和构建圆形、多边形和路径等基本形状。
结果便是世界上第一个由 SVG 驱动的僵尸启示录生存预测器的起点。到目前为止,该页面看起来还算不错(勉强),但完全无法正常工作。为了将页面提升到新的水平,本教程将深入探讨路径及其各种用途,演示如何构建预测仪,并涉及一些基本动画,为页面增添额外的亮点(对于能够理解它的浏览器而言)。
文本元素(续)
仔细看看 SVG 文本。在第一部分中,<text>
元素用于页面标题的 <svg>
块内。它与 HTML 文本具有相同的样式属性,但示例没有使用“style”属性来枚举它们,而是使用了单独的元素以获得最大的跨浏览器兼容性。
<svg id="cityBox" width="600" height="400"> <text x="300" y="50" font-family="sans-serif" font-size="36" text-anchor="middle" stroke="#000000" fill="#000000"> Surviving the Zombie Apocalypse </text> </svg>
此示例应在所有浏览器中正确显示,但它甚至没有触及 SVG 文本与常规文本不同之处的皮毛。
变换属性
首先,考虑“transform”属性。以前,此属性用于将 SVG 图像缩小到适当的大小,如下所示
<image id="human" x="2750" y="1750" width="343" height="483" transform="scale(.10,.10)" xlink:href="human.svg" />
但是,该属性也可以应用于 <text>
元素。例如,使用“rotate”变换可以使标题稍微倾斜
<text x="300" y="50" font-family="sans-serif" font-size="36" text-anchor="middle" stroke="#000000" fill="#000000" transform="rotate(30 100,100)">
这表示“在坐标 100,100 处,将文本旋转 30 度(顺时针)”。结果应如图 1 所示。
transform 属性包含其他几个可能的值
- translate:按指定量垂直或水平移动文本
- scale:按指定的垂直和水平比例缩放文本
- skewX:沿 x 轴按指定角度倾斜文本
- skewY:沿 y 轴按指定角度倾斜文本
- matrix:允许自定义变换定义
这些变换也适用于其他 SVG 对象,正如 <image> 的 scale 变换所示。变换也可以通过按所需变换的顺序列出它们来嵌套。例如,请注意图 2 中的以下代码结果。相同的两个单词在同一位置,具有相同的变换,但应用顺序相反。
<text x="100" y="100" text-anchor="middle" transform="rotate(30 100,100) skewX(50) scale(2)">Zombie</text> <text x="100" y="100" text-anchor="middle" transform="scale(2) skewX(50) rotate(30 100,100)">Apocalypse</text>
有关更多信息,请阅读 W3 规范中关于transform 属性的内容。
tspan 元素
在之前的示例中,创建了两个单独的 <text>
元素,以便可以对每个单词应用变换。但是,在具有通用样式和变换的 <text>
元素中,可以使用嵌套的 <tspan>
元素来定义单个属性,如位置和颜色。
此外,位置可以绝对(x,y)定义,也可以相对(dx,dy)定义。结合 <tspan>
,这可以实现一些有趣的文本技巧。请注意以下示例中绝对和相对坐标的使用,如图 3 所示。
<text x="50" y="50" text-anchor="left" fill="green"> <tspan>Surviving the</tspan> <tspan x="50" y="50">Zombie</tspan> <tspan x="50" y="50">Apocalypse</tspan> </text> <text x="200" y="50" text-anchor="left" fill="red"> <tspan>Surviving the</tspan> <tspan dx="50" dy="50">Zombie</tspan> <tspan dx="50" dy="50">Apocalypse</tspan> </text>
源坐标实现的巧妙技巧之一是指定多个坐标,然后将这些坐标应用于每个字母。虽然这可以使用绝对坐标完成,但使用相对坐标会更简单,尤其是在应用于单个 <tspan>
元素时,如图 4 所示。(在以下示例中还应用了“letter-spacing”属性,以给字母一些空间。)
<text x="300" y="50" text-anchor="middle"> <tspan>Surviving the Zombie</tspan> <tspan dy="-2 5 -3 -4 6 -5 3 -5 9 -4" letter-spacing="3" fill="red">Apocalypse</tspan> </text>
回到本系列第一部分中创建的原始“cityBox
”元素,将“rotate”变换应用于整个文本,并将多个相对 Y 坐标应用于最后一个单词。现在,即使还没有添加僵尸,这个惊恐的小人类也有东西可以逃跑,如图 5 所示。
<svg id="cityBox" width="600" height="400"> <text x="300" y="50" font-family="sans-serif" font-size="36" text-anchor="middle" stroke="#000000" fill="#000000" transform="rotate(30 100,100)"> Surviving the Zombie <tspan dy="-2 5 -3 -4 6 -5 3 -5 9 -4" letter-spacing="3" fill="red">Apocalypse</tspan> </text> <image id="human" x="2750" y="1750" width="343" height="483" transform="scale(.10,.10)" xlink:href="human.svg" /> </svg>
Animate 元素,或 SMIL 入门
在让标题安宁之前,还有一个有趣的技巧可以应用到它上面以达到戏剧效果:<animate>
元素。但是,这个元素不具备跨浏览器兼容性。
与 SVG 一样,Synchronized Multimedia Integration Language (SMIL) 已经存在了一段时间,其 v1.0 于 1998 年成为 W3C 推荐。它本质上为文本和媒体提供了动画标记。由于开发 SMIL 的工作组与开发 SVG 的工作组相同,因此后者包含 SMIL 规范中定义的某些功能。
到目前为止,并非所有浏览器都通过 SMIL 规范识别 SVG 动画。但是,那些不识别的浏览器只会忽略它,而不会中断。因此,可以将动画视为对一部分用户来说是很好的附加功能。(有关识别 SMIL 动画的浏览器和版本的完整列表,请参阅“我可以使用…”。)
构成 SVG 动画的主要元素包括 <animate>
、<set>
、<animateMotion>
和 <animateTransform>
。许多浏览器仍然识别 <animateColor>
,尽管该元素已被弃用,改为使用 <animate>
。
要使用 SMIL 动画,该元素应嵌套在要为其设置动画的 SVG 元素内。在此示例中,标题文本的笔触颜色将被动画化(attributeName
)。它设置为在加载后 2 秒开始,持续 10 秒(dur),并从黑色变为红色。然后通过将 fill="freeze"
(不要与填充颜色混淆)设置为动画的结束状态,使其在结束状态下冻结。
<text x="300" y="50" font-family="sans-serif" font-size="36" text-anchor="middle" stroke="#000000" fill="#000000" transform="rotate(30 100,100)"> Surviving the Zombie <tspan dy="-2 5 -3 -4 6 -5 3 -5 9 -4" letter-spacing="3" fill="red">Apocalypse</tspan> <animate attributeName="stroke" begin="2s" dur="10s" from="black" to="red" fill="freeze" /> </text>
如果在兼容的浏览器中查看,这将产生一种缓慢、微妙的效果,暗示着文本在流血。(跳到本教程结尾列出的示例页面进行演示。)
Path 元素
构建僵尸启示录生存预测器的第一部分介绍了创建 <path>
元素的基础知识。虽然存在用于不同类型形状的元素,如 <polygon>
和 <rect>
,但 <path>
元素可用于创建相同的形状。上次未提及的是,Closepath (Z) 命令通过自动绘制最后一段来促进闭合形状,如本代码片段所示,如图 6 所示。
<polygon points="50,50 100,25 100,75" stroke="black" stroke-width="1" fill="blue" /> <path d="M 150 50 L 200 25 L 200 75 L 150 50" stroke="black" stroke-width="1" fill="red" /> <path d="M 250 50 L 300 25 L 300 75 Z" stroke="black" stroke-width="1" fill="green" />
但路径还可以做更多事情,从创建简单的直线到设置文本的基线再到定义动画的运动路径。
创建直线
如前一段代码所示,一条线由 Moveto(M)或起点和 Lineto(L)或连接点组成。以下代码创建了三条连续的、笔触宽度不同的线。
<path d="M 100 100 L 175 100" style="stroke: black; stroke-width: 1;" /> <path d="M 100 150 L 175 150" style="stroke: black; stroke-width: 2;" /> <path d="M 100 200 L 175 200" style="stroke: black; stroke-width: 3;" />
这些代码使用绝对坐标定义精确的线节点位置。但是,在更合理的情况下也可以使用相对坐标,通过使用小写“l”表示 Lineto。以下代码创建了与上面示例相同的线条集(另外,请注意“style”属性如何被替换为单个属性,以便可以为整个组设置笔触颜色)。
<g stroke="black"> <path d="M 100 100 l 75 0" stroke-width="1" /> <path d="M 100 150 l 75 0" stroke-width="2" /> <path d="M 100 200 l 75 0" stroke-width="3" /> </g>
在这种情况下,“l 75 0”表示“向右绘制一条 75 个点的线”,而不是例如定义“175,100”的 Lineto 坐标。
如果您绘制多条相同样式的线条,可以使用相同的相对 Moveto(m)方法,通过建立第一个原点,然后在同一
<g stroke="black" stroke-width="2"> <path d="M 100 100 l 75 0" /> <path d="M 100 150 l 75 0" /> <path d="M 100 200 l 75 0" /> </g>
等同于
<path d="M 100 100 l 75 0 m -75 50 l 75 0 m -75 50 l 75 0" stroke="black" stroke-width="2" />
当绘制垂直或水平线时,事情会变得更加容易。Horizontal Lineto (H 或 h) 和 Vertical Lineto (V 或 v) 命令将坐标减少到单个维度,无论是绝对(H 或 V)还是相对(h 或 v)。以下代码绘制了与早期示例相同的三个线条(另外,请注意命令和数字之间多余的空白已删除)。
<path d="M100 100 h75 m-75 50 h75 m-75 50 h75" stroke="black" stroke-width="2" />
除了所有直线之外,路径还可以创建曲线。事实上,
- 二次贝塞尔曲线 (Q):通过指定控制点和终点来绘制曲线
- 平滑二次贝塞尔曲线 (T):将二次曲线平滑地继续到新点
- 曲线 (C):通过指定终点和两个控制点来绘制三次贝塞尔曲线
- 平滑曲线 (S):将三次曲线平滑地继续到新点
- 椭圆弧 (A):通过描述两个椭圆及其交点来绘制弧
由于逃离僵尸并不严格需要曲线路径,因此此处不予介绍。有关更多信息,请参阅W3C 关于 <path> 曲线命令的规范。
创建预测仪面板
在涵盖了 #controlPanelBox
”更改为类样式“.svgBox
”。目前,面板将带有边框以便查看。
.svgBox { border: 1px solid #000000; float: left; }
另外,请务必将类添加到 controlPanelBox <svg>
元素
<svg id="controlPanelBox" class="svgBox" width="400" height="400">
创建一个相同类的同名新元素,用作预测仪的容器
<svg id="meterBox" class="svgBox" width="200" height="400"> </svg>
此仪表将相当基本,包括一些简单的形状,如温度计刻度和一个指针(该指针最终将根据预测算法重新定位)。它还将包括一个按钮,将在本系列的最后一篇文章中进行交互。目前,添加一个文本标签和一些组来包含其他元素。
<text id="oddsText" x="100" y="50" font-size="50" font-family="sans-serif" text-anchor="middle">0%</text> <g id="oddsMeter"> </g> <g id="oddsButton" > </g>
oddsText
元素稍后将通过 JavaScript 进行操作,以反映该科学的计算,但在此期间它将保持静态。
对于 SVG 对象,顺序很重要。由于刻度线将在堆栈底部,因此现在绘制它们。在此教程之前,这些线条可能看起来像这样。
<path d="M 100 100 L 175 100" stroke="#000000" stroke-width="3" /> <path d="M 100 150 L 150 150" stroke="#000000" stroke-width="2" /> <path d="M 100 200 L 175 200" stroke="#000000" stroke-width="3" /> <path d="M 100 250 L 150 250" stroke="#000000" stroke-width="2" /> <path d="M 100 300 L 175 300" stroke="#000000" stroke-width="3" />
但是,通过将笔触信息提取到组元素中,并使用相对 Movetos 和 Horizontal Linetos,代码可以得到简化。
<g id="oddsMeter" stroke="#000000" stroke-width="2"> <path d="M100 100 h75 m-75 100 h75 m-75 100 h75" stroke-width="3" /> <path d="M100 150 h50 m-50 100 h50" /> </g>
在刻度线下方但位于 oddsMeter
组内,绘制一个与控件面板中使用的三角形相似的三角形。这次,由于它是一个单独的对象,不需要复制,因此使用 <polygon>
元素。
<polygon id="oddsPointer" points="150,300 100,275 100,325" fill="#FF0000" stroke-width="1" />
此元素包含在 oddsMeter
组内,因此指定了新的笔触宽度以覆盖组的 stroke-width 属性。
最后,在同一组的底部添加一个圆角矩形,这将使其出现在堆栈的顶部。Predictor 中尚未开始使用 <rect>
元素,但其属性不言自明。请注意用于创建圆角的 radius-x (rx) 和 radius-y (ry) 说明。
<rect x="90" y="75" rx="15" ry="10" width="20" height="250" fill="#0000FF" />
在仪表下方,添加用于计算生存几率的按钮。该按钮由两个 SVG 元素 <text>
和 <rect>
组成,它们的顺序使得文本显示在本质上是白色填充矩形的上方。这里需要考虑的重要一点是,当稍后添加交互性时,需要将其添加到整个按钮上,这就是为什么将这两个元素分组并命名。否则,将事件处理程序添加到文本或其背后的矩形可能会导致用户交互不满意。将它们分组也提供了一种添加光标的简单机制。
<g id="oddsButton" style="cursor: pointer;"> <rect x="35" y="340" rx="10" ry="10" width="130" height="40" fill="#FFFFFF" stroke="#000000" stroke-width="2" /> <text x="100" y="365" font-family="sans-serif" font-size="18" text-anchor="middle">Calculate Odds</text> </g>
完成的预测仪面板应如图 7 所示。
创建文本基线
如前所述,路径也可以为 cityBox
”SVG 元素中的标题,目前如图 5 所示。我们将不对其应用“rotate”变换,而是将此文本的基线固定到一条基本路径。
首先,在“cityBox
”SVG 面板的顶部添加一条弯曲的路径。暂时包含黑色笔触(笔触粗细默认为 1)。由于这不是直线,并且可以视为潜在的多边形,因此将填充设置为“none”。给它命名为“titlePath
”,将在下一步中引用它。默认情况下,如果路径不够长以用作整个文本的基线,剩余文本将被截断,因此为此路径在末尾添加了一个超长的部分:“l250 100”。
<path id="titlePath" d="M0 100 l100 -50 l60 25 l150 -40 l250 100" stroke="#000000" fill="none" />
路径,不包含附加元素,应如图 8 所示。
要将文本锚定到路径,请将文本内容和任何 <tspan>
元素嵌套在 <textPath>
元素内,而 <textPath>
元素本身又嵌套在 <text>
元素内。然后 <textPath>
元素指定一个 xlink 来引用路径。(您也可以在此过程中删除变换并将路径的笔触颜色更改为“none”以使其不可见。)
<svg id="cityBox" width="600" height="400"> <path id="titlePath" d="M0 100 l100 -50 l60 25 l150 -40 l250 100" stroke="none" fill="none" /> <text x="300" y="50" font-family="sans-serif" font-size="36" text-anchor="middle" stroke="#000000" fill="#000000"> <textPath xlink:href="#titlePath"> Surviving the Zombie <tspan dy="-2 5 -3 -4 6 -5 3 -5 9 -4" letter-spacing="3" fill="red">Apocalypse</tspan> </textPath> <animate attributeName="stroke" begin="2s" dur="10s" from="black" to="red" fill="freeze" /> </text> <image id="human" x="2750" y="1750" width="343" height="483" transform="scale(.10,.10)" xlink:href="human.svg" /> </svg>
不幸的是,这里是浏览器差异开始显现的地方。一些浏览器会忽略“x”属性,并根据“text-anchor
”属性将文本定位到基线。其他浏览器则忽略“text-anchor
”属性,并根据“x
”属性将文本定位到基线。因此,为了找到一个适用于两者的修复方法,请将 text-anchor 更改为“left”,并将 x 更改为“15”。
<text x="15" y="50" font-family="sans-serif" font-size="36" text-anchor="left" stroke="#000000" fill="#000000">
普遍的结果应如图 9 所示。
创建动画路径
尽管 <path>
元素的全部潜力尚未被挖掘,在本教程中也不会被完全挖掘,但它可以用于增加更多的吸引力。与路径为文本提供基线的方式类似,它也可以为动画提供基线。需要注意的是,这属于 SVG/SMIL 动画的范畴,因此并非完全跨浏览器兼容。同样,请将其视为对识别它的浏览器的额外奖励,因为它不会破坏不识别它的浏览器的任何内容。
在第一部分中,控件面板包含一个“僵尸速度”的开关,如图 10 所示。
<g id="speedGroup"> <circle id="slowCircle" cx="75" cy="325" r="40" stroke="red" fill="white" stroke-width="4" /> <image id="slowZombie" x="375" y="1875" width="175" height="304" transform="scale(.16,.16)" xlink:href="zombie.svg" /> <text id="speedLabel" font-size="15" font-family="sans-serif" text-anchor="middle" fill="red" x="175" y="315">Zombie Speed</text> <text id="speedText" font-size="25" font-family="sans-serif" text-anchor="middle" fill="red" x="175" y="350">Slow</text> <circle id="fastCircle" cx="275" cy="325" r="40" stroke="black" fill="white" stroke-width="2" /> <image id="fastZombie" x="1630" y="1875" width="175" height="304" transform="scale(.16,.16)" xlink:href="zombie.svg" /> </g>
为了增加一点额外的趣味性,如果这些僵尸真的在四处移动,那会很有趣。为此,将添加两组新元素:隐藏路径和 <animateMotion>
元素,它类似于在标题中使用的 <animate>
元素。
在让僵尸行走(或者说蹒跚)之前,了解动画路径的工作原理很有帮助,因为它不一定直观。使用 <path>
元素进行动画处理与使用它作为文本基线之间的关键区别在于,路径不定义目标元素在页面上的实际放置位置,而是定义其相对移动。
为了探索这一点,创建一个类为“svgBox
”且尺寸为 400x400 的空 <svg>
元素。添加一个简单的圆和一条路径。
<svg class="svgBox" width="400" height="400"> <circle cx="100" cy="100" r="50" fill="red" /> <path id="testPath" d="M 100 100 L 300 50" stroke="#000000" fill="none" /> </svg>
在此示例中,目标是将红色圆从 100,100 移动到 300,50。因此,放置一条从 100,100 开始并以 300,50 结束的路径。要添加运动,请打开 <circle>
元素,并在其中添加 <animateMotion>
元素,设置持续时间(dur)为两秒。然后在其中添加一个 <mpath>
元素,并指定路径名称作为其 xlink 引用。
<svg class="svgBox" width="400" height="400"> <circle cx="100" cy="100" r="50" fill="red"> <animateMotion dur="2s"> <mpath xlink:href="#testPath" /> </animateMotion> </circle> <path id="testPath" d="M 100 100 L 300 50" stroke="#000000" fill="none" /> </svg>
当页面加载时(在支持动画的浏览器中),会发生一些有趣的事情。圆不会从 100,100 移动到 300,50,而是跳到 200,200 并移动到 400,150,这使其一半超出
这很容易处理。对于用于动画的路径,只需从 0,0 开始,唯一的缺点是路径可能更难开发和调试,因为,如本例所示,它可能会超出
<path id="testPath" d="M 0 0 L 200 -50" stroke="#000000" fill="none" />
现在,圆会相对于其起始原点按预期移动。事实上,因为动画路径定义了相对移动,所以可以将圆移动到完全不同的原点,它仍然会在新位置以相同的方式移动,而无需修改任何 <path>
值。
现在,僵尸。首先,创建将要使用的路径。目标是让僵尸在它们下面的 <circle>
元素的范围内移动。由于路径需要从 0,0 开始,并且包含细微的 Lineto 变化,因此优化移动的最简单方法是继续实现动画,然后通过反复试验进行调整。
在以下对 Zombie Speed 开关的修订代码中,添加了两条路径,其点数大致相同。关键区别在于 <animateMotion>
元素的持续时间:行尸走肉僵尸需要四秒才能完成路径,而世界大战 Z 僵尸需要半秒。此外,还添加了“repeatCount
”属性“indefinite”以创建无限循环。
<g id="speedGroup"> <path id="slowPath" d="M 0 0 L 12 0 L -9 0 L 8 -3 L -8 3 L 0 0" fill="none" stroke="none" /> <path id="fastPath" d="M 0 0 L -3 3 L 4 -2 L -2 0 L 3 1 L -1 -3 L 0 0" fill="none" stroke="none" /> <circle id="slowCircle" cx="75" cy="325" r="40" stroke="red" fill="white" stroke-width="4" /> <image id="slowZombie" x="375" y="1875" width="175" height="304" transform="scale(.16,.16)" xlink:href="zombie.svg" > <animateMotion dur="4s" repeatCount="indefinite" > <mpath xlink:href="#slowPath"/> </animateMotion> </image> <text id="speedLabel" font-size="15" font-family="sans-serif" text-anchor="middle" fill="red" x="175" y="315">Zombie Speed</text> <text id="speedText" font-size="25" font-family="sans-serif" text-anchor="middle" fill="red" x="175" y="350">Slow</text> <circle id="fastCircle" cx="275" cy="325" r="40" stroke="black" fill="white" stroke-width="2" /> <image id="fastZombie" x="1630" y="1875" width="175" height="304" transform="scale(.16,.16)" xlink:href="zombie.svg" > <animateMotion dur=".5s" repeatCount="indefinite" > <mpath xlink:href="#fastPath"/> </animateMotion> </image> </g>
结果:僵尸速度开关为用户提供了关于按钮功能及其可能影响的额外视觉信息。蹒跚 vs. 狂怒。罗梅罗 vs. 斯奈德。它就在页面上。
下一步
此时,僵尸启示录生存预测器的所有基本视觉效果都已到位,再加上一些额外的花哨功能。<text>
元素得到了更深入的探讨,<path>
元素也得到了充分的利用。更新后的预测器不仅使用它来绘制线条和完成 UI,还为标题和僵尸图标提供基线和动画路径。
但是还有很多工作要做。本系列的最后一部分将连接所有用户交互。它探索了几种与 <svg>
元素交互的方法,包括将事件处理程序直接添加到元素本身以及调用 JavaScript 函数。所说的 JavaScript 也将用于通过更改文本内容、修改 SVG 属性和向页面添加全新的 SVG 元素来操作 SVG DOM。
到目前为止的代码可以在此处看到实际效果。
本文是来自 Internet Explorer 团队的 HTML5 技术系列文章的一部分。 通过 3 个月的免费 BrowserStack 跨浏览器测试 @ http://modern.IE 尝试本文中的概念
Justin Whitney 是一名自由移动应用开发者、Web 开发者和电影制作人。在撰写本文时,他梦见自己被一种被有毒火鸡皮屑感染的狂暴僵尸群围困。幸运的是,他被博士救了。