原文:Pro CSS3 Animation
协议:CC BY-NC-SA 4.0
五、CSS3 关键帧动画
对于在两种状态之间创建简单的元素动画,CSS 转换很容易实现和使用,但是这种简单性有几个明显的限制。为了创建更复杂的动画,你需要 CSS 动画模块。我将 CSS 动画模块创建的动画称为关键帧动画,以进一步区分它们和过渡。
如果您更熟悉 Flash 之类的动画工具,或者来自视频或电影背景,CSS 关键帧动画使用的基于时间的“无帧”方法一开始可能会有点混乱。为了确保每个人都有相同的理解,我将定义关键帧和补间,然后看看它们是如何通过 CSS 动画模块实现的。
关键帧和补间和
现代动画继承了迪士尼和其他动画师在 20 世纪早期开发的传统手绘、cel-shaded 动画的术语和流程。在角色研究、草图和剧本完成后,动画开发是这样的:
- 一个序列的主要帧是由一个监督动画师绘制的。(在迪士尼工作室,这可能是“九个老人”中的一个,他是创造确立镜头的动画大师。)例如,在像 Dumbo 这样的功能中,Dumbo 试图通过拍打他的耳朵来飞翔的序列可能包括两个关键帧:一个是 Dumbo 耳朵朝上的帧,另一个是耳朵朝下的帧。
- 然而,将动画限制在这两帧会使序列看起来非常不稳定(或者使 Dumbo 看起来像蜂鸟一样飞)。为了创建一个更平滑的动画序列,关键帧被交给一个“中间者”,一个低级别的动画师,他将使用第一个和最后一个关键帧作为参考来绘制所需的中间帧。这个过程被称为补间。
- 完整的序列将被着墨、着色和对齐。以每秒 24 帧的速度回放,每幅画之间的过渡看起来很流畅,创建了一个无缝的动画。
今天,你是主动画师,浏览器扮演中间人的角色。创建一个好的 CSS3 动画就是为浏览器提供完整的关键帧,这些关键帧具有足够的信息来在它们之间平滑地进行补间。在补间时,浏览器必须做出许多假设。糟糕的动画通常是由于没有为样式表提供足够的元素信息,或者做出了与 CSS3 内置的假设相反的假设。
注意根据屏幕上的运动速度,如果大约每 50 毫秒显示一个新帧,动画序列就会平滑播放。遵循 CSS 的原则,动画模块不试图定义回放速率或每秒显示的帧数(FPS)。所有 CSS 动画都是由状态(到和从 a 状态)或一个序列需要多长时间(之前的时间和 a 状态之后的时间)定义的。剩下的就交给浏览器或客户端了。您完全可以优化您的 CSS 声明,以减少浏览器的负载并创建更高效的动画,但是您不能“逐帧”创建您的序列(除了可能的步骤过渡,在第三章中讨论),并且您不能定义帧速率。
CSS3 关键帧动画语法
关键帧动画总是以动画名称开始,该名称与 id 值一样,必须是唯一的。如果两个关键帧序列具有相同的名称,则只会识别最后一个。动画序列本身可以用两种方式指定。第一个是作为一个from ... to
声明,如清单 5-1 所示。
*清单 5-1 。*简单的从左到右动画的关键帧
@keyframes slide {
from { left: 0; }
to {left: 100%; }
}
动画也可以指定为时间百分比,如清单 5-2 所示。
清单 5-2 。?? 一个复杂动画的多个关键帧
@keyframes multislide {
0% { left: 20px; }
20% { right: 200px; }
80% { left: 50px; }
100% { right: 180px; }
}
如果您没有在动画声明中明确定义开始或结束状态(from/0%
或to/100%)
),浏览器将从元素的初始或最终状态进行插值。您还可以在声明中创建关键字和值的混合,如清单 5-3 所示。
清单 5-3 。 混合了关键词和百分比值的关键帧序列
@keyframes multislide {
20% { right: 200px; }
80% { left: 50px; }
to { right: 180px; }
}
因为它只描述了两种状态,所以在清单 5-1 中显示的 CSS 动画语法产生了一个本质上等同于过渡的结果,尽管动画方法仍然保留了一些优于过渡序列的优点,你很快就会看到。
实际上,关键帧序列可以写在 CSS 中的任何地方,但是我建议在大多数情况下将它们放在样式表的顶部,与任何@font-face
声明放在一起,以便于参考。您可能希望将很长的序列放在样式表的底部(以避免它们碍事),或者甚至将它们作为一个单独的。css 文件(通过@import
或<link>), although this adds a separate HTTP request.
进入你的页面)
通过对一个元素应用单独的 CSS 属性来调用 CSS 关键帧动画序列,如清单 5-4 所示。
清单 5-4 。 调用 CSS 关键帧动画序列
#redbox {
background-color: red;
width: 100px; height: 150px;
animation-name: slide;
animation-duration: 5s;
animation-timing-function: ease-in;
}
正如你所看到的,这些属性(除了animation-name
之外)非常类似于第三章中介绍的那些过渡,并且它们具有非常相同的功能。一个区别是animation-duration
可以设置为 infinite 关键字,而不是以秒或毫秒为单位的时间。动画模块也有animation-delay
属性,增加了animation-iteration-count
、animation-direction, animation-play-state
和animation-fill-mode
。
该动画也可以在单个animation
快捷方式属性中调用,如清单 5-5 所示。
清单 5-4 。 用快捷方式调用 CSS 关键帧动画序列
#redbox { animation: slide 5s ease-in 2s; }
动画值可以以任何顺序声明,除了duration
和delay
值,必须先声明duration
,后声明delay
。
支持旧浏览器中的关键帧动画
旧的浏览器需要厂商前缀,正如已经讨论过的过渡。这很复杂,因为@keyframes
声明也需要前缀,如清单 5-6 所示。
清单 5-6 。 调用旧 Webkit 浏览器的 CSS 关键帧动画序列
@-webkit-keyframes multislide {
0% { left: 20px; }
20% { right: 200px; }
80% { left: 50 %; }
100% { right: 180px; }
}
#bluebox {
width: 100px; height: 150px;
-webkit-animation-name: multislide;
-webkit-animation-duration: 10.5s;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-delay: 200ms;
}
这种重复显然是在你为支持其他旧版本浏览器而写的时候积累起来的;第十章中讨论的一些工具可以简化和自动化这个过程。
控制关键帧动画回放
如果您尝试回放您创建的动画,您将看到元素重置为其初始状态。如果希望元素在最后一帧停止,有几种选择。
- 为元素设置一个长的
animation-duration
,让它永远到达最后一帧。 - 设置
animation-direction
,使元件在同一位置缠绕。 - 将
animation-fill-mode
设置为forwards
。
animation-fill-mode
的名字很奇怪,但是它有一个特殊的用途,如表 5-1 所示。
表 5-1。动画填充模式属性值
填充模式 | 描述 |
---|---|
forwards | 元素位置在最后一帧结束。 |
backwards | 元素返回到第一帧的位置。 |
both | 将元素设置为页面加载时第一个关键帧的位置,而不考虑元素在其他 CSS 中的默认位置。仅当animation-delay 的值大于 0 时适用。 |
none | 元素在关键帧之外由 CSS 提供的初始默认位置开始和结束。 |
您也可以使用animation-direction
返回动画序列,如表 5-2 所示。
表 5-2。动画方向属性值
动画方向 | 描述 |
---|---|
normal | 动画正常向前播放 |
alternate | 动画向前播放,然后反向播放,返回到其初始位置 |
reverse | 动画向后播放 |
alternate-reverse | 动画在第一次播放时向后播放,第二次播放时反向正常播放 |
混合和链接关键帧动画
合并多个关键帧动画与合并转场非常相似,如清单 5-7 所示。
清单 5-7 。 在一个元素上混合多个关键帧动画
@keyframes lefttoright {
0% { left: 0; }
100% { left: 800px; }
}
@keyframes toptobottom {
0% { top: 0; }
25% { top: 100px; }
50% { top: 0; }
75% { top: 100px; }
100% { top: 0; }
}
#box { background: red; width: 100px; height: 100px; position: absolute;
animation-name: lefttoright, toptobottom;
animation-duration: 4s, 2s;
animation-timing-function: ease-in, ease-in-out;
}
组合这两个动画序列的绘制结果如图 5-1 所示。
图 5-1。一个合并的 CSS3 动画
这个动画可以通过保持相同的关键帧但改变它们的持续时间来改变,如清单 5-8 所示。
清单 5-8 。 一个通过改变关键帧持续时间而改变的合并 CSS3 动画
#box { background: red; width: 100px; height: 100px; position: absolute;
animation-name: lefttoright, toptobottom;
animation-duration: 3s, 6s;
animation-timing-function: ease-in, ease-in-out;
animation-fill-mode: both;
}
结果如图图 5-2 所示。
图 5-2。改变动画时长值后元素的移动
也可以通过在关键帧动画序列之间引入延迟来“链接”它们,如列表 5-9 和图 5-3 所示。
清单 5-9 。 CSS3 连锁动画序列
#box { background: red; width: 100px; height: 100px; position: absolute;
animation-name: lefttoright, toptobottom;
animation-duration: 3s, 6s;
animation-timing-function: ease-in, ease-in-out;
animation-delay: 0s, 3s;
animation-fill-mode: both;
}
图 5-3。设置动画延迟值后元素的移动
您将在 CSS3 动画模块中使用所有这些功能的组合来创建复杂的动画,如下一节中描述的弹跳球和图像幻灯片。
重复动画序列
增加迭代次数允许动画重复多次。使用infinite
关键字可以将动画设置为无休止地重复播放。例如,无限动画可以用来创建一个永无止境的弹跳球(见图 5-4 )。
图 5-4。来自动画弹跳球序列的截图
为了创建一个真实的橡胶球,你需要同时完成几个视觉效果:当球接触到它下面的理论表面时,它应该在反弹之前稍微“挤压”(使用在第四章中讨论的挤压和拉伸原理),而当球升到空中时,它下面的阴影变得更加分散,并远离撞击点。要增加额外的真实感,您可以为某些关键帧插入单独的缓动功能。(本动画来源于tym pus . net/CoDrops/2012/05/22/creating-an-animated-3d-bounding-ball-with-css3/
的 CoDrops 原创作品,经许可使用。)参见清单 5-10 。
清单 5-10 。 CSS 使用挤压和拉伸动画原理制作一个无休止重复的弹跳球
@keyframes ballbounce {
0% { top: 0;
animation-timing-function: ease-in; }
50% { top: 140px; height: 140px;
animation-timing-function: ease-out;}
55% { top: 160px; height: 120px; border-radius: 50 % / 60px;
animation-timing-function: ease-in;}
65% { top: 120px; height: 140px; border-radius: 50 %;
animation-timing-function: ease-out;}
95% { top: 0; animation-timing-function: ease-in;}
100% { top: 0;animation-timing-function: ease-in;}
}
@keyframes shadowshrink {
0% { bottom: 0;
margin-left: -30px;width: 60px;height: 75px;
background: rgba(20, 20, 20, .1);
box-shadow: 0px 0 20px 35px rgba(20,20,20,.1);
border-radius: 30px / 40px;
animation-timing-function: ease-in;}
50% {bottom: 30px;
margin-left: -10px;width: 20px;height: 5px;
background: rgba(20, 20, 20, .3);
box-shadow: 0px 0 20px 35px rgba(20,20,20,.3);
border-radius: 20px / 20px;
animation-timing-function: ease-out;}
100% { bottom: 0;
margin-left: -30px;
width: 60px;
height: 75px;
background: rgba(20, 20, 20, .1);
box-shadow: 0px 0 20px 35px rgba(20,20,20,.1);
border-radius: 30px / 40px;
animation-timing-function: ease-in;}
}
#ball {
width: 140px;
height: 140px;
border-radius: 70px;
background: rgb(187,187,187);
background: linear-gradient(to bottom,
rgba(187,187,187,1) 0%,rgba(119,119,119,1) 99 %);
box-shadow: inset 0 -5px 15px rgba(255,255,255,0.4),
inset -2px -1px 40px rgba(0,0,0,0.4), 0 0 1px #000;
animation: ballbounce 1s infinite;
}
#shadow {top: 280px;
width: 60px;
height: 75px;
box-shadow: 0px 0 20px 35px rgba(20,20,20,.1);
border-radius: 30px / 40px;
transform: scaleY(.3);
animation: shadowshrink 1s infinite;}
#ball, #shadow { position: absolute; top: 0; }
#shadow { left: 65px; }
#wrapper { position: relative; width: 200px; margin: 40px auto; }
<div id = wrapper>
<div id = shadow > </div>
<div id = ball > </div>
</div>
正如你所看到的,通过合并和同步多个 CSS 动画序列,你可以在 HTML 元素中创建复杂的、真实的运动。
暂停关键帧动画
可以通过更改animation-play-state
属性的值来暂停动画。在清单 5-9 和 5-10 的代码中,可以添加清单 5-11 中的代码。
清单 5-11 。 暂停一个 CSS3 动画
#wrapper:hover #ball, #wrapper:hover #shadow {
animation-play-state: paused;
}
因为animation-play-state
的默认值是running
,所以当鼠标从悬停在包装器 div 上移开时,动画将恢复。正如你将在第六章中看到的,完全可以通过其他方式设置running
和paused
状态。
摘要
利用关键帧的 CSS3 动画为网页上的 HTML 元素提供了更多的动画机会,比通过过渡创建的 HTML 元素有更多的变化。虽然为旧的浏览器构建前缀支持可能是一个有些艰难的过程,但基本的语法使 CSS3 动画更加有效和有趣。虽然许多属性与过渡模块有共同之处,但animation-fill-mode
却非常不同:虽然受 CSS3 过渡影响的元素通常会返回到它们的初始状态,但对于成熟的动画来说,这样做是不常见的,需要小心处理animation-direction
和animation-fill-mode
。
在下一章中,您将使用在这里学到的语法来创建 web 内容的高级动画,包括图像幻灯片。
六、Web 内容的 CSS3 关键帧动画
正如您在上一章中看到的,CSS 动画模块的语法比过渡语法更强大,允许通过使用关键帧来更好地控制 web 内容。本章将通过几个例子来说明这种能力:一个循环幻灯片,一个复制 JavaScript 经典 Lightbox 插件外观的图库,以及一个徽标动画。
一个简单的 CSS3 幻灯片演示
传统上,图片库幻灯片效果是使用 Flash 或 JavaScript 创建的,通常以框架插件的形式出现,如 Nivo Slider ( http://nivo.dev7studios.com
)和 Camera ( www.pixedelic.com/plugins/camera
)。虽然使用框架/插件方法有很多优势(主要是在可用的各种过渡方面),但是在 CSS3 中完成幻灯片也有很多优势,稍后您将会看到。
HTML 代码
标记 CSS3 幻灯片的图像是一个相当简单的过程:你将图像放入一个容器中,该容器作为一个条带穿过另一个“窗口”元素,该元素与条带中的每个图像大小相同(参见图 6-1 )。
图 6-1。CSS 幻灯片的组织(红色“窗口”容器被放大以示强调)
在代码中,这是使用清单 6-1 中的创建的,在接下来的例子中会添加更多的标记,
清单 6-1 。 HTML 代码为一个基本的 CSS3 幻灯片
<div id=slideshow>
<figure id=imagestrip>
<img src=black-kite.jpg alt="Photograph of a Black kite">
<img src=red-kite.jpg alt="Profile of a Red kite">
<img src=pelicans.jpg alt="Pelicans on moorings at sea">
<img src=pariah-kite.jpg alt="Photograph of Pariah kite">
</figure>
</div>
在本例中,您有四个 400 像素高、500 像素宽的图像,因此内部容器的总宽度必须为 2000 像素。(注意,所有图像的大小必须完全相同;示例中的图片由 Challiyil Eswaramangalath Pavithran Vipin、Ariful H Bhuiyan、Márcio Cabral de Moura 和 Alan Saunders 根据知识共享协议授权使用。
清单 6-2 中显示的基本 CSS 同样简单明了:
***清单 6-2 。***CSS 3 幻灯片图像库的基本 CSS 代码
div#slideshow { position: relative; background: #000; overflow: hidden; }
figure#imagestrip, div#slideshow { box-sizing: border-box; }
div#slideshow, figure#imagestrip img { width: 500px; height: 400px; float: left; }
figure#imagestrip { position: absolute; width: 2000px; margin: 0; }
要创建最简单的滑块动画,您必须以 500 像素的增量水平移动内部图形,每次移动后都有一个停顿,以便观众有时间欣赏每张图片。正如我在前一章中讨论的,CSS3 动画不能在显式帧中工作。您必须将动画视为时间的一部分:为每个图像指定相等的时间,在此期间它将保持静止,剩余时间指定为运动。在这个例子中,每个图像在 20%的时间里是静止的,而整个带在 20%的时间里是运动的,分为四个序列。因此,每次间隙移动将占用总时间的 5%。
写入关键帧时要记住的重要部分是,如果你想保持它们相同,你改变的属性必须作为多个关键帧之间的设定值出现;否则,浏览器将使用您不希望它使用的值恢复到插值。
你对关键帧声明的第一次尝试可能看起来类似于清单 6-3 。
清单 6-3 。 简单图像滑块的关键帧
@keyframes slider {
0% { transform: translateX(0px); }
20% { transform: translateX(0px); }
25% { transform: translateX(−500px); }
45% { transform: translateX(−500px); }
50% { transform: translateX(−1000px); }
70% { transform: translateX(−1000px); }
75% { transform: translateX(−1500px); }
95% { transform: translateX(−1500px); }
100% { transform: translateX(−2000px); }
}
然后你调用动画序列。与过渡不同,关键帧动画不需要启动事件,这意味着该动画将在页面加载时运行,如清单 6-4 所示。
清单 6-4 。 简单图像滑块的关键帧
figure#imagestrip { animation: slider 10s infinite; }
您会发现这是可行的,但只有一个问题:最后一次移动显示了一个空窗口,因为没有超过 2000 像素的图像用于显示图像条。您可以通过引入一点小技巧来解决这个问题,将第一个图像的副本放在条带的末尾,在我们的 CSS 中将整个条带延长到 2500 像素(清单 6-5 )。
***清单 6-5 。*修改了一个基本 CSS3 幻灯片的 HTML 代码
<div id=slideshow>
<figure id=imagestrip>
<img src=black-kite.jpg alt="Photograph of a Black kite">
<img src=red-kite.jpg alt="Profile of a Red kite">
<img src=pelicans.jpg alt="Pelicans on moorings at sea">
<img src=pariah-kite.jpg alt="Photograph of Pariah kite">
<img src=black-kite.jpg alt="Photograph of a Black kite">
</figure>
</div>
由于最后一个关键帧现在在视觉上与第一个匹配,这就创建了一个完整平滑的动画序列。
背景图像的变化
您可以仅使用背景图像来创建等效的效果,例如,在横幅元素或徽标后面创建幻灯片显示。这将把您的代码简化为一个单独的<figure>
,但是会使您的 CSS 有些复杂。(请注意,您将在背景列表的末尾重复第一幅图像,就像您在上面的示例中所做的一样,原因也是一样的。参见清单 6-6 。)
清单 6-6 。 HTML 和 CSS 代码为一个滑块使用背景图片
<style>
figure#imagestrip { width: 500px; height: 400px;
background: #000; box-sizing: border-box;
background-image: url(black-kite.jpg), url(red-kite.jpg), url(pelicans.jpg),← url(pariah-kite.jpg), url(black-kite.jpg);
background-repeat: no-repeat;
background-position-x: 0, 500px, 1000px, 1500px, 2000px;
animation: slider 20s infinite;
}
@keyframes slider {
0% { background-position-x: 0, 500px, 1000px, 1500px, 2000px; }
20% { background-position-x: 0, 500px, 1000px, 1500px, 2000px; }
25% { background-position-x: -500px, 0px, 500px, 1000px, 1500px, 2000px; }
45% { background-position-x: -500px, 0px, 500px, 1000px, 1500px, 2000px; }
50% { background-position-x: -1000px, -500px, 0px, 500px, 1000px, 1500px; }
70% { background-position-x: -1000px, -500px, 0px, 500px, 1000px, 1500px; }
75% { background-position-x: -1500px, -1000px, -500px, 0px, 500px, 1000px; }
95% { background-position-x: -1500px, -1000px, -500px, 0px, 500px, 1000px; }
100% { background-position-x: -2000px, -1500px, -1000px, -500px, 0px, 500px; }
}
</style>
<figure id=imagestrip></figure>
您还可以创建一个包含多个背景图像的容器元素,并通过一个可见的“窗口”移动它,或者为背景创建一个将所有图像连接在一起的图像。后一种方法会使 CSS 变得更容易,但也会使以后更改图库变得更加困难。
暂停幻灯片放映
允许用户暂停滑块来聚焦一张图片是合理的。启动这样一个动作最简单的方法是滑块本身上的一个hover
;同时,您应该在幻灯片上放置一个视觉标识符,以清楚地表明它处于暂停状态。对于这个例子,我将通过淡化imagestrip
元素并在屏幕上放置一个替代暂停图标的文本作为伪元素(我也可以使用一个图像文件)来实现。所有的变化都是对 CSS 的补充,如清单 6-7 所示。
清单 6-7 。 CSS 代码悬停时暂停一个图库幻灯片
div#slideshow:hover figure#imagestrip { animation-play-state:paused; opacity: 0.5; }
div#slideshow:hover:before {
content: "||"; font-size: 200px;
color: rgba(255,255,255, 0.7);
position: absolute;
left: 160px; top: 80px;
}
注意,您可以通过在内部的figure
元素(比如transition: 1s opacity linear
)上放置一个适当的转换来缓和暂停状态的可视标识符。
改变图像之间的过渡
最终,滑块图库中的每个图像之间有许多种可能的淡入淡出和擦除方式,下面将展示其中的几种。
淡入淡出
在简单的水平或垂直爬行之后,滑块最常见的过渡是让每个图像按顺序淡入/淡出黑色。在不改变标记的情况下,你可以淡出图像条,在它是黑色的时候移动图像条“在黑暗的掩护下”,然后作为一个整体再次淡入图像条,如清单 6-8 所示。
清单 6-8 。 CSS 代码悬停时暂停一个图库幻灯片
@keyframes slider {
0% { transform: translateX(0); }
20% { opacity: 1; }
22% { opacity: 0; transform: translateX(0); }
23% { opacity: 0; transform: translateX(−500px); }
25%, 45% { opacity: 1; }
47% { opacity: 0; transform: translateX(−500px); }
48% { opacity: 0; transform: translateX(−1000px); }
50%, 70% { opacity: 1; }
72% { opacity: 0; transform: translateX(−1000px); }
73% { opacity: 0; transform: translateX(−1500px); }
75%, 95% { opacity: 1; }
97% { opacity: 0; transform: translateX(−1500px); }
98% { opacity: 0; transform: translateX(−2000px); }
100% { opacity: 1; transform: translateX(−2000px); }
}
请注意,我对具有相同属性的关键帧进行分组的方式与对普通 CSS 选择器进行分组的方式相同。然而,通常情况下,您会希望让动画上的调用更长,以防止它显得匆忙。我建议使用animation: slider 20s infinite
。
Because the other images in the strip are not visible during the fade sequences, you can drop the final duplicate image on the end of the slider div, running it back to the beginning in complete darkness before starting the animation again. That will not be possible in the next example.
运动过程中的淡入淡出
通过在图像从左向右移动的过程中淡出图像,您可以保留带有淡出效果的滑块效果。虽然在单个关键帧动画中完全可以做到这一点,但最简单的方法可能是将代码创建为同时运行的两个序列。
为了实现这一点,你恢复到你原来的滑块关键帧序列,现在运行超过 30 秒,并添加一个新的fader
关键帧声明(见清单 6-9 )。
清单 6-9 。 CSS 代码悬停时暂停一个图库幻灯片
@keyframes slider {
0% { transform: translateX(0px); }
20% { transform: translateX(0px); }
25% { transform: translateX(−500px); }
45% { transform: translateX(−500px); }
50% { transform: translateX(−1000px); }
70% { transform: translateX(−1000px); }
75% { transform: translateX(−1500px); }
95% { transform: translateX(−1500px); }
100% { transform: translateX(−2000px); }
}
@keyframes fader {
0% { opacity: 1; }
70% { opacity: 1; }
90% { opacity: 0; ease-out; }
95% { opacity: 0; }
100% { opacity: 1; ease-in; }
}
figure#imagestrip { width: 2500px;
animation: slider 30s infinite, fader 7.5s infinite; }
计时背后的原理很简单:动画总长度为 30 秒,幻灯片中的每个图像将在屏幕上完全显示 6 秒(动画时间的 20%),并在 1.5 秒内移动到左边(总时间的 5%)。通过循环播放第二个动画,使imagestrip
元素在接近结束时淡出超过 7.5 秒,您可以合并这两个动画以获得平滑的结果。
交叉路径〔??〕
要实现交叉渐变效果,您有三个选项,但在所有情况下,图像不再作为“条带”放置,而是一个叠一个,最上面的图像依次淡出。
第一个也是最简单的选择是简单地将每个背景图像设置为一个空的(但大小正确的)元素的关键帧(见清单 6-10 )。
清单 6-10 。 CSS 代码为一个交叉渐变图像滑块
@keyframes imageswap {
0% { background-image: url(black-kite.jpg); }
20% { background-image: url(red-kite.jpg); }
40% { background-image: url(pelicans.jpg); }
80% { background-image: url(pariah-kite.jpg); }
100% { background-image: url(black-kite.jpg); }
}
这将简单容易地在图像之间交叉渐变;然而,它可能不会给你想要的结果或控制程度。另一种方法是使用交叉渐变滤镜将图像带到页面上,如清单 6-11 所示。
清单 6-11 。 一个交叉渐变图像滑块的替代 CSS 代码
@keyframes slider {
0% { background-image: url(black-kite.jpg); }
20% { background-image: cross-fade(url(black-kite.jpg), url(red-kite.jpg),0%); }
25% { background-image: cross-fade(url(black-kite.jpg), url(red-kite.jpg),100%); }
45% { background-image: cross-fade(url(red-kite.jpg), url(pelicans.jpg),0%); }
50% { background-image: cross-fade(url(red-kite.jpg), url(pelicans.jpg),100%); }
70% { background-image: cross-fade(url(pelicans.jpg), url(pariah-kite.jpg),0%); }
75% { background-image: cross-fade(url(pelicans.jpg), url(pariah-kite.jpg),100%); }
95% { background-image: cross-fade(url(pariah-kite.jpg), url(black-kite.jpg),0%); }
100% { background-image: cross-fade(url(pariah-kite.jpg), url(black-kite.jpg),100%); }
}
第三种选择,使用“真实”图像,稍微复杂一些:最上面的图像必须淡出,然后在返回前景之前延迟。(想象一副牌,在第一张牌被放入弃牌堆一段时间之前,上面的牌和下面的牌之间发生了过渡。)CSS 变成了清单 6-12 中所示的样子。
清单 6-12 。 第三个选项 CSS 代码为交叉渐变图像滑块
@keyframes slider {
0%, 25% { opacity: 1; }
30%, 100% { opacity: 0; }
}
figure#imagestrip {
width: 500px; height: 400px;
background: #000; box-sizing: border-box; overflow: hidden;
position: relative;
}
figure#imagestrip img { position: absolute; top: 0; left: 0; }
HTML 也会改变;注意,在新代码中,我已经将图像按照与相反的顺序放置了(清单 6-13 )。绝对定位,图中的最后一个图像将在顶部。(或者,您可以在每个上放置一个内联的z-index
属性)。
清单 6-13 。 第三选项交叉渐变图像滑块的 HTML 代码
<figure id=imagestrip>
<img src=black-kite.jpg alt="Photograph of a Black kite">
<img src=pariah-kite.jpg alt="Photograph of a Pariah kite" style="animation: slider 10s 7.5s infinite;">
<img src=pelicans.jpg alt="Pelicans on moorings at sea "style="animation: slider 10s 5s infinite;">
<img src=red-kite.jpg alt="Photograph of a Red kite" style="animation: slider 10s 2.5s infinite;">
<img src=black-kite.jpg alt=" Photograph of a Black kite" style="animation: slider 10.1s 0s infinite;">
</figure>
由于关键帧序列中的每个图像在动画长度的四分之一中是“实心”的,并且整个动画的长度为 10 秒,因此对关键帧的每个后续调用都会额外延迟总时间的四分之一。提供给第一遍(黑风筝的动画)的额外时间是为了使照片的返回不会“踩到”在图形开始处的淡入淡出。
添加字幕
您可以像在前面的图像过渡示例中一样,为幻灯片添加标题。标记如清单 6-14 中的所示。
清单 6-14 。 带标题的图片滑块的 HTML 代码
<div id=slideshow>
<figure id=imagestrip>
<figure>
<img src=black-kite.jpg alt="Black kite">
<figcaption>Black kite</figcaption>
</figure>
<figure>
<img src=red-kite.jpg alt="Red kite">
<figcaption>Red kite</figcaption>
</figure>
<figure>
<img src=pelicans.jpg alt=Pelicans>
<figcaption>Pelicans</figcaption>
</figure>
<figure>
<img src=pariah-kite.jpg alt="Pariah kite">
<figcaption>Pariah kite</figcaption>
</figure>
</figure>
</div>
向清单 6-2 和 6-3 添加清单 6-15 中所示的标记。
清单 6-15 。 CSS 代码给图片滑块添加标题
figure#imagestrip figure figcaption {
position: absolute; background: rgba(0,0,0,0.4);
color: #fff; width: 500px; padding: 8px;
font-size: 18px; top: -42px;
transition: 1s top linear;
}
figure#imagestrip:hover { animation-play-state:paused; }
figure#imagestrip figure:hover figcaption { top: 0; }
暂停的点击方法
您也可以使用前面讨论的label
方法来创建一个暂停幻灯片动画的替代方法,方法是在开始<div>
之后添加如清单 6-16 所示的代码以及相关的 CSS。
清单 6-16 。 HTML 和 CSS 代码添加一个点击暂停到一个图像滑块
<input type=checkbox id=pause><label for=pause></label>
label {
display: block; z-index: 24; transition: 0.3s all ease-in-out;
}
input:checked ∼ figure#imagestrip {
animation-play-state:paused;
}
input#pause:checked ∼ label {
background: rgba(0,0,0,0.4);
}
input#pause:checked ∼ label:before {
content: "||"; font-size: 200px;
color: rgba(255,255,255, 0.5);
position: relative;
left: 160px; top: 80px;
}
虽然以上是一个选项,但如果您选择使用它,请记住本章前面讨论的可访问性问题。
为旧版本的 Internet Explorer 创建后备
声明的overflow: hidden
部分将被 Internet Explorer (IE) 9 和早期版本读取和遵循,尽管关键帧动画不会。这将模糊其他图像,意味着 IE 10 版之前的用户将看不到它们。这可以通过一个条件注释来避免,这个条件注释将图像的可见性传递给那些用户,如清单 6-17 所示。(请注意,IE 6、7 和 8 将需要 JavaScript 和稍微多一点的 CSS,以便浏览器识别 HTML5 元素,例如在第九章中讨论的<figure>
。)
清单 6-17 。 有条件的 CSS 使图片在早期版本的 IE 中可查看
<!--[if lte IE 9]>
div#slideshow { overflow: visible; }
<![endif]-->
对文本使用字幕动画的警告
标签有着漫长而惨淡的历史,可以追溯到早期版本的 Internet Explorer。在 90 年代后期,<marquee>
从来不是一个标准化元素,也不是任何 HTML 规范的一部分,它通常被用来为网页上的文本创建一种滚动的“滚动条”效果。除了在非常特殊的情况下,<marquee>
和动画 gif 和闪烁的文字一起,成为了糟糕的网页设计的标志之一。
虽然天真的设计者可能会尝试使用这些技术来制作文本爬行动画,但是应该避免使用它们。撇开设计趋势不谈,字幕文字有许多可用性问题:
- 人类视觉系统被运动所吸引,并且字幕文本处于不断的运动中;字幕功能在页面上非常容易分散注意力。
- 出于同样的原因,字幕文本可能很难阅读,尤其是对于有视觉障碍的用户。
- 在字幕文本中包含链接会让糟糕的想法变得更糟:链接很难跟踪和点击。当字幕文本循环移动时,错过一个链接意味着如果用户第一次错过它,就必须等到该链接的下一次出现,这是非常令人沮丧的。因此,出现在字幕中的任何重要链接也应该以静态形式出现在网页上。
新闻字幕/通知动画
在本节中,您将使用 CSS3 动画创建一个新闻提示序列,而不是使用 marquee 来制作文本动画。每个新的通知将堆积在页面的右下角,显示一段时间后消失。用户应该能够单击每个通知以获取更多信息,每个面板的剩余时间将显示在进度条中。(参见图 6-2 。)
图 6-2。【CSS3 驱动的新闻收报机
对此的标记相当简单:通知是包含在一个更大的div
中的div
元素,每个进度条是一个内部带有span
的div
,如清单 6-18 所示。
进步元素呢?
HTML5 有显示进程时间的标记:<progress>
元素。虽然您可以按照自己的方式设置 progress 元素的样式,但是您不能使用 CSS 来修改标记中可视化显示的进度(这就是 JavaScript)。这样的任务超出了本章的范围,因为我们尽可能对每个特性都使用 CSS3,所以在这种情况下使用它并不合适。
清单 6-18 。CSS3 驱动的定时通知系统的 HTML
<div id=breaking-news>
<div class=notification><a href=#><span>☂</span>Rain expected</a>
<div class=progress><span></span></div>
</div>
<div class=notification><a href=#><span>✈</span>Travel plans changed</a>
<div class=progress><span></span></div>
</div>
<div class=notification><a href=#><span>❄</span>Light snow</a>
<div class=progress><span></span></div>
</div>
</div>
样式化通知的基本 CSS 如清单 6-19 所示。
清单 6-19 。 CSS 为一个通知告警序列
div#breaking-news {
position: fixed; bottom: -20px; right: 15%;
}
div.notification {
position: relative; width: 275px; border-radius: 10px;
background: linear-gradient(rgb(215,215,215), rgb(165,164,169));
padding: 60px 20px 40px; border: 2px solid #999;
margin-top: 10px;
box-shadow: 3px 3px 6px rgba(0,0,0,0.1) inset, 0 0 6px 2px rgba(0,0,0,0.1);
opacity: 0.9;
}
div.notification a {
color: white; text-stroke: 1px solid #000;
text-decoration: none; font-family: Futura, sans-serif; font-size: 20px;
}
div.notification a span {
font-size: 60px; padding-right: 20px; vertical-align: middle;
}
div.progress {
height: 5px; border-radius: 2px; border: 1px solid #999;
margin-top: 32px; background: rgb(215,215,215);
}
div.progress span {
background: #000; display: block; width: 0; height: 3px;
}
有三个动画序列:popup
,驱动每个通知向上;progress
,显示剩余时间;还有fade
,让每一个通知都在最后淡去。这些都显示在清单 6-20 的中。
清单 6-20 。 CSS 为一个通知告警序列
@keyframes popup {
0%, 30% { height: 0; padding: 0 20px; display: none; }
}
@keyframes fade {
100% { opacity: 0; }
}
@keyframes progress {
100% { width: 100%; }
}
请注意,您对这些关键帧序列采用了一种稍微不同的方法:因为元素的默认状态已经在清单 6-20 中定义了,所以您仅使用关键帧来定义从状态(在弹出的情况下)或从到状态(在进度和淡入淡出的情况下)的*。浏览器将根据需要自动在这些值和默认的嵌入、内嵌和链接样式之间进行补间。只有在您的@keyframes
声明中包含 0%和 100%的值时,您才能控制元素的整个外观(不考虑animation-fill-mode
)。*
所有通知弹出窗口共享相同的动画,除了为每个弹出窗口初始化动画之前的延迟。有效地使用你的 CSS 意味着你应该在一个声明中放尽可能多的相似的 CSS,如清单 6-21 所示。
清单 6-21 。 从单个共享声明中调用关键帧序列
div.notification {
...
animation-name: popup, fade;
animation-duration: 2s, 1s;
animation-timing-function: cubic-bezier(0.325, 0.730, 0.695, 1.650);
animation-fill-mode: backwards, forwards;
animation-delay: 2s, 14s;
}
没有任何矛盾的陈述,每个通知面板将继承清单 6-21 中的所有样式,但是动画延迟是你必须为每个改变的一件事(见清单 6-22 )。
清单 6-22 。 为后续通知面板设置不同的动画延迟值
div.notification:nth-child(2) {
animation-delay: 6s, 18s;
}
div.notification:nth-child(3) {
animation-delay: 12s, 24s;
}
你对每个面板的进度条采取类似的方法(清单 6-23 )。
*清单 6-23 。*为进度条设置不同的动画延迟值
div.progress span {
background: #000; display: block; width: 0; height: 3px;
animation: progress 12s 4s forwards linear;
}
div.notification:nth-child(2) div.progress span { animation-delay: 6s; }
div.notification:nth-child(3) div.progress span { animation-delay: 12s; }
自然,在现实世界中,用纯 CSS3 手工制作每个通知需要花费大量的精力。正如你将在第九章中看到的,你可以使用这里的工作基础来巧妙地整合 JavaScript 的优势。
CSS3 中的 Lightbox 图像库等价物
Lightbox 是用于显示图库图像的第一个流行模态技术的通称:经典的 Lightbox 效果是页面的淡出,然后是页面中心图像的扩展和淡入。它的流行导致了在 web 上的过度使用,许多开发人员只是因为不熟悉或懒惰而使用代码自带的默认值。用 CSS 编写等价的内容允许开发人员根据自己的需求轻松定制图库的外观。
首先,你将使用一个非常相似的标记(清单 6-24 ),与你在第三章中的第一个图库示例中使用的标记相似。这一次你将使用由罗伯特·洛、乔恩·罗林森和卡米洛·鲁埃达·洛佩斯提供的图片,这些图片是在知识共享协议下获得许可的(图 6-3 )。
图 6-3。CSS3-light box 等效值放大的大图
清单 6-24 。 在 CSS3 中相当于一个灯箱的 HTML
<body id=base>
<dl id=gallery>
<dt><a href=#col1><img src=coliseum-at-night-small.jpeg alt="Coliseum at night"></a>
<dd id=col1><a href=#><img src=coliseum-at-night.jpeg alt="Coliseum at Night" ></a>
<dt><a href=#col2><img src=coliseum-forum-small.jpeg alt="Roman Coliseum and Forum"></a>
<dd id=col2><a href=#><img src=coliseum-forum.jpeg alt="Roman Coliseum and Forum" ></a>
<dt><a href=#col3><img src=grand-via-madrid-small.jpeg alt="Grand Via, Madrid, Portugal"></a>
<dd id=col3><a href=#><img src=grand-via-madrid.jpeg alt="Grand Via, Madrid, Portugal" ></a>
</dl>
你需要dd
是页面的全高和全宽和来居中显示内容。要做到这一点,你将把 CSS 放在 HTML 元素本身上,这样dd
就可以相对于 HTML 元素测量自己,并使用 flexbox 模块将 dd 的子元素居中(清单 6-25 )。
**清单 6-25 。**中的基本 CSS 为一个灯箱等效于 CSS3
html { min-height: 100%; position: relative; }
body { margin: 0; height: 100%; margin-right: 2em; }
dl#gallery { float: left; }
dl#gallery dt { width: 150px; }
dl#gallery dd {
margin-left: 0; background: rgba(0,0,0,0);
position: absolute; top: 0; bottom: 0;
width: 100%; height: 100%;
display: box; box-pack:center; box-align:center;
visibility: hidden;
}
dd a { background: #fff; display: block; transition: 4s all ease-in; }
请注意,如果页面明显超出浏览器窗口的底部,这种方法有一个潜在的缺点,因为这种 CSS 将导致图像始终垂直居中于正文内容的高度。
为了展开并显示dd
元素,您将使用一个关键帧序列来制作图像动画,并通过过渡dd
( 清单 6-26 )的背景来淡化页面。
清单 6-26 。 为灯箱效果的关键帧序列
@keyframes blowup {
0% { width: 0; height: 0; opacity: 0; }
30% { width: 640px; height: 0; opacity: 0; }
60% { width: 640px; height: 480px; opacity: 0; margin: 20px; }
100% { width: 640px; height: 480px; opacity: 1; margin: 20px; }
}
dd:target {
visibility: visible; background: rgba(0,0,0,0.6);
transition: 2s background linear;
}
dd:target a { box-shadow: 0 0 8px 8px rgba(0,0,0,0.3); }
dd:target a img { animation: blowup 3s forwards; }
通过将dd
的内容链接到body
的id
,点击图像将重新定位浏览器并撤销动画。
添加字幕
向 Lightbox CSS3 代码添加标题有几个选项。第一个给现有代码添加了一个最小量,作为span
元素(清单 6-27 )。效果如图图 6-4 所示。
图 6-4。对于一个大的 CSS 灯箱图片,鼠标经过时的标题
清单 6-27 。 为灯箱效果的关键帧序列
<dl id=gallery>
<dt><a href=#col1><img src=coliseum-at-night-small.jpeg alt="Coliseum at night"></a>
<dd id=col1><a href=#><img src=coliseum-at-night.jpeg alt="Coliseum at Night">
<span>Coliseum at Night</span></a>
<dt><a href=#col2><img src=coliseum-forum-small.jpeg alt="Roman Coliseum and Forum"></a>
<dd id=col2><a href=#><img src=coliseum-forum.jpeg alt="Roman Coliseum and Forum">
<span>Roman Coliseum and Forum</span></a>
<dt><a href=#col3><img src=grand-via-madrid-small.jpeg alt="Via Grand, Madrid, Spain"></a>
<dd id=col3><a href=#><img src=grand-via-madrid.jpeg alt="Grand Via, Madrid, Portugal">
<span>Grand Via, Madrid, Portugal</span></a>
</dl>
通过将 CSS 添加到dd
容器中,使其成为position: relative
并将span
绝对定位在其中,您可以在悬停时转换标题(清单 6-28 )。
清单 6-28 。 灯箱图库标题的 CSS
dl#gallery {
float: left; font-family: Futura, Arial, sans-serif; margin-bottom: 12em;
}
dl#gallery dd {
margin-left: 0; background: rgba(0,0,0,0);
position: absolute; top: 0; bottom: 0;
width: 100%; height: 100%;
display: box; box-pack:center; box-align:center;
visibility: hidden;
}
dl#gallery dt { width: 150px; margin: 2em 2em 0 2em; }
dd:target { visibility: visible; background: rgba(0,0,0,0.6); transition: 2s background linear; }
dl#gallery dd a { background: #fff; display: block; text-decoration: none; }
dl#gallery dd a span {
display: block; background-color: rgba(0,0,0,0.3); color: white;
position: absolute; top: 20px; left: 20px;
padding: 10px; opacity: 0;
transition: 1s opacity ease-in;
}
dl#gallery dd a:hover span { opacity: 1; }
dl#gallery dd:target a img { animation: blowup 3s forwards; }
dl#gallery dd:target a {
box-shadow: 0 0 8px 8px rgba(0,0,0,0.3);
transition: 4s all ease-in; position: relative;
}
CSS3 也可用于在页面加载时制作公司标志动画(见图 6-5 )。当我们谈到响应式设计时,我会进一步探讨这个话题(第九章)。只播放一次动画是很重要的:循环播放的动画会分散观众的注意力。
图 6-5。页面加载时添加到公司徽标的标题
页面加载上的徽标动画
由于虹膜组件不会被动画化,只有虹膜作为一个整体,你通常会创建一个图像标志(可能是一个 SVG 矢量文件)。为了便于说明,我们将使用纯 CSS 来制作它。(参见清单 6-29 。)
清单 6-29 。 HTML 标记为企业标志
<div id=container>
<span id=iris1></span>
<span id=iris2></span>
<span id=iris3></span>
<span id=iris4></span>
<span id=iris5></span>
<span id=iris6></span>
<span id=iris7></span>
<span id=iris8></span>
<div id=iris>
</div>
</div>
<h1>Avid <span>Laboratories</span></h1>
为此,您可以添加 CSS 来创建徽标的虹膜部分,并设置文本样式(清单 6-30 )。
清单 6-30 。 基本 CSS 为企业 Logo
div#container span {
display: block; width: 100px; border: 7px solid white;
height: 32px;
position: absolute; top: 90px; left: 90px;
background-color: #333;
transform-origin: top right; z-index: -4;
}
div#iris {
border-radius: 50%; border: 30px solid #fff;
width: 200px; height: 200px; position: relative;
}
div#container span#iris1 { transform: translate(−35px, 55px) rotate(0deg); }
div#container span#iris2 { transform: translate(−70px, 90px) rotate(45deg); }
div#container span#iris3 { transform: translate(−130px, 70px) rotate(90deg); }
div#container span#iris4 { transform: translate(−160px, 20px) rotate(135deg); }
div#container span#iris5 { transform: translate(−145px, -35px) rotate(180deg); }
div#container span#iris6 { transform: translate(−95px, -60px) rotate(225deg); }
div#container span#iris7 { transform: translate(−45px, -45px) rotate(270deg); }
div#container span#iris8 { transform: translate( −20px, 5px) rotate(315deg); }
h1 {
font-family: "Univers LT 55"; text-transform: uppercase; font-size: 80px;
letter-spacing: 10px; position: absolute; top: 10px; left: 150px;
text-align: left; line-height: 50px;
}
h1 span {
font-family: "Univers CE 45 Light"; font-size: 25px; display: block;
letter-spacing: 20px; text-indent: 30px;
}
最后,你可以创建动画(清单 6-31 )。
清单 6-31 。 用于企业标志动画的关键帧序列
@keyframes spinner {
0% {
transform: translate(800px,-25px) rotate(378deg); opacity: 0; }
100% { transform: translate(2px,-25px) rotate(−56deg); opacity: 1; }
}
div#container {
position: relative; width: 200px; height: 200px;
animation-name: spin;
animation-duration: 2s;
transform: rotate(−18deg);
animation: spinner 1.5s 2s linear both;
}
请注意,您没有将动画附加到任何特定的状态,因此关键帧动画将在页面加载时自动运行。
摘要
在这一章中,你已经探索了如何使用 CSS3 关键帧为图片库和其他页面内容创建动画。关键帧序列可以包括任何形式的需要随时调用的复杂运动:鼠标点击、页面加载或其他形式的用户交互。
到目前为止,我在动画中还没有解决的一个问题是显示可伸缩性:序列中的元素越大,浏览器和 GPU 就越难对它们进行转换、过渡和动画制作。此外,位图图像占总页面大小的很大一部分,降低了连接速度,尤其是在移动设备上。图像也不能很好地适应缩放:将显示基于位图的作品的屏幕的 DPI 加倍会导致图像质量下降。
在下一章中,您将看到所有这些问题的一个潜在答案,以 SVG 矢量图形与 CSS3 集成的形式。
七、将 CSS3 动画与 SVG 和滤镜集成
每个支持 CSS 变换、过渡和动画的浏览器还支持 SVG(可缩放矢量图形),这是一种长期以来被大多数 web 开发人员忽略的图像格式。在被 Internet Explorer (IE)忽略了十年之后,SVG 正在经历一场复兴,IE9 和所有其他现代浏览器都支持它,这使它非常适合部署在移动开发和其他用例中。正如您将在本章中看到的,CSS3 转场和关键帧可以很好地与 SVG 集成。
本章后半部分讨论的过滤器对 CSS 来说是新的,但在 SVG 中是标准化的。事实上,CSS 过滤器直接源自 SVG 标准。滤镜允许对 HTML 内容(尤其是图像)进行实时和交互式的可视化编辑,这在以前只能在 PhotoShop 中实现。作为一个 CSS 属性,滤镜可以很容易地被动画化,就像其他任何东西一样。
SVG 简介
SVG 是一种开放的、基于 XML 的格式。这个事实允许在普通的文本编辑器和几乎任何 web 开发语言中创建和修改 SVG 数据。SVG 支持它自己的渐变、交互性、文本和层,但是对于我们的目的来说,最重要的特性是这种格式描述了矢量形状。这意味着 SVG 图像可以缩放到任何大小或分辨率,或者以任何方式转换,而不会有任何质量损失。这种格式提供了基于文本的矢量信息描述,这一事实也使得 SVG 文件相对较小:例如,一个简单的 UI(用户界面)形状(如播放按钮)可以在 SVG 中描述为三个点和一种填充颜色,而不是单独定义的像素。这使得这种格式具有自然的响应性,非常适合移动设计、高 DPI 显示和 CSS 操作。
例如,考虑一个 UI 播放按钮元素。简化到绝对最小值,描述这种形状的 SVG 代码可能类似于清单 7-1 。
清单 7-1 。?? 一个简单的 SVG 文件
<svg version="1.1" FontName1">http://www.w3.org/2000/svg">
<polygon points="0,0 0,400 200,200 "/>
</svg>
SVG 数据可以直接在浏览器中查看。用文件名play.svg
保存清单 7-1 中的代码,并将其加载到浏览器窗口中,得到如图图 7-1 所示的可视化结果。
图 7-1。浏览器中显示的 SVG 播放按钮
注意,SVG 多边形元素使用点来描述形状,类似于 imagemaps。最终,显示器必须将 SVG 元素呈现为像素,默认情况下,为多边形指定的点将映射到屏幕上的像素。(默认情况下,您的播放按钮将在浏览器中显示为 400 像素高、200 像素宽,并位于屏幕的最左上角。)最终,SVG 元素的最终呈现尺寸是任意的:按钮可以呈现为一英里高(如果您的屏幕足够大)或两厘米宽(例如,在打印的页面上),除了所有其他因素,质量是相同的。
同样,与 imagemaps 一样,为 SVG 手工编写所有代码通常效率不高。对于许多任务,您会发现使用绘图应用并将结果导出为 SVG 文件要容易得多。(我将很快讨论这些工具。)
将 SVG 放在网页上
在 HTML 页面上放置 SVG 文件有三种主要方法:作为图像引用、内嵌在页面上(也称为嵌入式 SVG)和作为对象引用。
SVG 作为内嵌图像
web 开发人员最熟悉的将 SVG 元素放入网页的方法是使用<img
>标签。在你这样做之前,你的 SVG 代码必须包含更多关于其“自然”大小的信息(同样,保持代码绝对最小),如清单 7-2 所示。
清单 7-2 。 SVG 代码准备作为图片插入到网页上
<svg version="1.1" FontName1">http://www.w3.org/2000/svg" viewBox="0 0 400 400">
<polygon points="0,0 0,400 200,200 " fill="rgba(90,70,80,0.5)" />
</svg>
属性指定了一个 400×400 像素的“画布”。CSS 中没有为图像元素指定宽度或高度,当 SVG 元素作为标准 HTML 的图像放在页面上时,它为自己保留了 400 × 400 像素的“空间”,如清单 7-3 所示。
清单 7-3 。?? 一个 SVG 元素作为图像插入到网页上
<img src=svg/play.svg alt=Play>
您也可以在 CSS 中通常使用图像的任何地方引用 SVG 作为图像——例如,作为元素的背景(清单 7-4 )。
清单 7-4 。 在 CSS 中应用 SVG 作为背景图片
h1 { background: url(svg/wave.svg); }
虽然以这种方式引用外部 SVG 文件是最简单和最常见的方法,但它确实有几个缺点:
- SVG 文件仅被视为图像;代码中编写的任何交互或脚本都将被忽略。
- 您不能“深入”SVG 代码,使用 CSS 直接改变元素的外观,这种自由度与内联 SVG 相同。
线 SVG〔??〕??㎡线
如果直接在页面上嵌入 SVG 数据,就需要在 SVG 代码中包含更多的信息。你需要提供元素的宽度和高度,要么作为属性(viewBox
、width
或height
属性),要么作为样式(见清单 7-5 )。
清单 7-5 。 内嵌 HTML 应用 SVG
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<title > SVG Embedded File Example</title>
</head>
<body>
<h1 > Standard content</h1>
<svg version="1.1" FontName1">http://www.w3.org/2000/svg" style="width: 200px; height: 400px">
<polygon points="0,0 0,400 200,200" />
</svg>
</body>
</html>
内联 SVG 节省了一个额外的 HTTP 请求,这对于移动页面尤其重要,因为移动页面普遍存在延迟。与简单地将文件作为图像引用相比,其他优点是能够用 CSS 直接影响 SVG 的外观。还支持与 SVG 的脚本交互。
内联 SVG 的主要缺点是它向 HTML 页面添加了更多的代码。
SVG 作为对象或 iframe 添加
将 SVG 作为对象或 iframe 添加到 web 页面是最古老的方法,在这种方法中,脚本交互性保留在元素内部(清单 7-6 )。
清单 7-6 。 SVG 作为对象或 iframe 应用于网页
<object type="image/svg + xml" data="icon.svg">
Warning for older browsers, or alternative content
</object>
<iframe src="icon.svg">
Warning for older browsers, or alternative content
</iframe>
然而,作为一个<object>
或<embed
>标签应用,定制 SVG 元素外观的能力被最小化了。如果 SVG 内容溢出了它的容器,那么<object
>或<embed
>标签也可能和滚动条一起出现。
用 CSS 操作 SVG
正如可以用几种不同的方法将 SVG 放在网页上一样,也可以用几种不同的方法来处理 SVG 元素的外观。SVG 有自己的原生语法来实现某些视觉效果,这使得事情变得更加复杂。
在最简单的层面上,当 SVG 元素作为图像放在页面上时,您可以调整它的大小,如清单 7-7 所示。
清单 7-7 。 内联 CSS 用于调整 SVG 元素的大小
<img src=play.svg alt=Play style="width: 50px; height: 50px">
您还可以从嵌入或链接的样式表中改变元素的外观,就像您为任何其他类型的图像编写表示规则一样。(由于这个原因,页面上使用的 SVG 元素通常被赋予一个id
属性。)
你可以在 SVG 文件中改变 SVG 形状的填充颜色,使用fill
(见清单 7-8 )。(注意fill
可以接受 CSS 中使用的任何颜色值:关键字、十六进制、rgb 或 hsl)。
清单 7-8 。 填充属性用于给一个 SVG 多边形元素着色
<polygon points="0,0 0,400 200,200 " fill="red" />
您还可以使用 SVG 文件中的嵌入样式表来更改填充颜色(清单 7-9 )。
清单 7-9 。 用嵌入的样式表修改 SVG 元素的外观
<svg version="1.1" FontName1">http://www.w3.org/2000/svg" viewBox="0 0 400 400">
<style type="text/css">
polygon { fill: blue; }
</style>
<polygon points="0,0 0,400 200,200" />
</svg>
最后,如果 SVG 文件直接嵌入到您的页面中,您也可以通过 CSS 来实现这一点(清单 7-10 )。
清单 7-10 。使用嵌入的样式表修改内联 SVG 元素的外观
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<title > SVG Embedded File Example</title>
<style>
polygon { fill: red; stroke: black; stroke-width: 9px; }
</style>
</head>
<body>
<svg version="1.1" FontName1">http://www.w3.org/2000/svg" style="width: 200px; height: 400px">
<polygon points="10,19 10,390 190,200" />
</svg>
</body>
</html>
请注意,这并不意味着您可以像您所希望的那样,神奇地为任何 HTML 元素提供新的 stroke 或 fill 属性。这些属性专门应用于 SVG 内容。
您还可以检测 SVG 元素上的悬停,并改变 SVG 内容的外观作为响应(清单 7-11 )。
清单 7-11 。SVG 元素上的 CSS 悬停检测
polygon { fill: red; stroke: black; stroke-width: 9px; }
polygon:hover { fill: black; }
注意,对按钮使用内嵌 SVG 有一个很小但很重要的优点:悬停的“热点”区域正好是多边形的形状。在标准位图图像上,无论图像的形状如何,该区域总是矩形的。
这意味着您也可以转换这些效果,就像您可以转换受 CSS 影响的任何其他元素一样。为了使 CSS 更加清晰和具体,我在 polygon 元素中添加了一个id
,并在我的样式中这样处理,如清单 7-12 中的所示。(请注意,我还将多边形从边缘“向内”移动了一点,因为 stroke 添加到了形状的外部,就像 CSS border 添加到 HTML 元素一样。如果没有这一点,笔画的尖端将被 SVG“画布”的边缘切断,因为它延伸到它们之外)。
清单 7-12 。?? 一个 SVG 元素上的 CSS 过渡
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<title > SVG Embedded File Example</title>
<style>
#play { fill: red; stroke: black; stroke-width: 9px; transition: 1s all linear; }
#play:hover { fill: black; stroke: black; stroke-width: 9px; }
</style>
</head>
<body>
<svg version="1.1" FontName1">http://www.w3.org/2000/svg" style="width: 200px; height: 400px">
<polygon id="play" points="10,19 10,390 190,200" />
</svg>
</body>
</html>
任何可以应用于 HTML 元素的 CSS3 变换、过渡或动画也可以应用于 SVG,这使得矢量形状和 CSS 的结合非常强大。
动画 SVG 图像映射
在本练习中,您将使用 SVG 来复制 HTML imagemap UI 的功能,但是您将添加几个进化步骤:您的 SVG“image map”的各部分将屏蔽图像内容,当 CSS3 悬停在适当的区域上时,这些内容将被过渡(参见图 7-2 )。
图 7-2。一个 SVG 图像映射
首先,您需要一个 SVG 绘图。谢天谢地,网上有大量免费的 SVG 内容:在这种情况下,我将使用 Wikimedia Commons 提供的加拿大地图。为了便于说明,我把地图编辑成了不列颠哥伦比亚省、艾伯塔省和萨斯喀彻温省。
每个省由一条路径来区分,路径以坐标字符串的形式提供。我们首先在页面中内联 SVG,并在每个路径上创建一个简单的悬停效果。结果将类似于清单 7-13 中的。
清单 7-13 。 在 SVG 图像上简单高亮显示
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<title > SVG Embedded File Example</title>
<style>
path { fill: #fdfdfd; }
path:hover { fill: red; }
</style>
</head>
<body>
<svg version="1.1" xmlns:svg=http://www.w3.org/2000/svg
FontName1">http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="1000px" height="600px" viewBox="0 0 1000 660">
<path id="alberta" d="M1654.393,678.219 . . .>
<path id="british-columbia" d="M982.854,27.912l150.51,33.221c17.. . .>
. . .
</svg>
为了实现蒙版效果,你要在每条路径后嵌入每张“透明”图片,然后将路径转换成剪辑路径。您将通过引用图像的适当剪辑路径的 id 将元素关联在一起,并用<g>
将元素分组在一起。
SVG 图像的开始代码,包括阿尔伯塔省的路径,看起来类似于清单 7-14 。
清单 7-14 。图像的 SVG 剪辑路径
<g>
<clipPath id="ab-clipper">
<path fill="#D3D3D3" d="M1654.393,678.219 . . .>
</clipPath>
<image clip-path="url(#ab-clipper)" height="100%" width="100%" x="800" y="50"
xlink:href="lake-louise.jpeg" preserveAspectRatio="xMidYMin slice" />
</g>
clipPath
在其中定义路径,作为引用clipPath id
的元素的遮罩,就像lake-loise.jpe
g 图像一样。图像元素被交叉链接到与地图相同位置的位图图片。图像被设置为其完整的“自然”宽度和高度,并在 x 和 y 轴上移动,直到它与路径位于相同的位置。(您可能需要将链接设置为剪辑路径,作为您对图像采取的最后一个操作;否则,当图像被遮盖并且可能不可见时,您将玩一个关于图像位置的猜谜游戏。
现在你已经创建了一个蒙版位图图像。显示这部分地图的 CSS(清单 7-15 )很简单。
清单 7-15 。 CSS 在一个 SVG 元素中显示悬停时的剪辑图像
svg image { opacity: 0; transition: 400ms opacity ease; }
svg image:hover { opacity: 1; }
只有一个问题:裁剪路径是不可见的,这意味着在你的地图上有一个阿尔伯塔省大小的缺口。悬停效果有效,但是如果您将相同的效果应用到其他省份路径,用户将看不到他们的鼠标移动到哪里。一旦变成剪贴画,路径上的填充就没用了。你可以整天改变颜色,永远看不到任何不同。
解决方案是在代码中的剪辑路径和图像之间放置一个路径的填充副本,如清单 7-16 所示。
清单 7-16 。 添加了复制填充路径的 SVG 中可见的裁剪区域
<g>
<clipPath id="ab-clipper">
<path fill="#D3D3D3" d="M1654.393,678.219 . . .>
</clipPath>
<path fill="#FF0000" d="M1654.393,678.219\. . . />
<image clip-path="url(#ab-clipper)" height="100%" width="100%" x="800" y="50"
xlink:href="wheat.jpeg" preserveAspectRatio="xMidYMin slice" />
</g>
位图图像(由 Kenny Louie 创作,在 Creative Commons: http://flickr.com/photos/kwl/3102355428
下授权)仍然不可见,但它被渲染在复制路径的“上方”,因此当用户将鼠标悬停在复制路径填充的区域上时,它仍然会显示。
最后一步是链接 SVG 图像中的每个区域。链接位于 SVG 文件本身的每个组内部,并且需要使用xlink
名称空间(清单 7-17 )。
***清单 7-17 。***SVG 中的链接裁剪图像
<g>
<a xlink:href="http://www.hellobc.com/">
<clipPath id="bc-clipper">
<path d=" M982.854,27.912l150.51,33.221c17.. . .>
</clipPath>
<path id="british-columbia" d="M982.854,27.912l150.51,33.221c17.. . .>
<image clip-path="url(#bc-clipper)" height="100%" width="100%" x="80" y="50"
xlink:href="false-creek.jpeg" preserveAspectRatio="xMidYMin slice" />
</a>
</g>
请注意,链接的区域沿着路径的边缘,就像真实的图像映射一样,并且过渡发生在同一区域内。
SVG 雪花动画
矢量形状的可伸缩性意味着您可以使用不同大小的 SVG 元素的多个副本,而不必担心图像质量。我将通过为季节性背景场景制作矢量雪花动画来演示这一点。(为此,我将使用在http://upload.wikimedia.org/wikipedia/commons/5/50/Snow_flake.svg. See Figure 7-3
从维基共享下载的稍加修改的 SVG 雪花。)
图 7-3。静止来自一个 SVG 飘落雪花动画
首先,您要将雪花作为多个图像放在页面上。雪花将有不同的大小,并将在不同的位置开始,但它们将共享相同的动画特征:它们将从天空落下,在微风中横向漂移。(参见清单 7-18 。)
清单 7-18 。 SVG 雪花作为图片放置在页面上
<img src="snowflake.svg" alt="" class="flake" style="top: -50px" >
<img src="snowflake.svg" alt="" class="flake" style="left: 200px; width: 60px; height: 60px; top: -120px;" >
<img src="snowflake.svg" alt="" class="flake" style="left: 640px; width: 120px; height: 120px; top: -400px;" >
接下来,您将应用 CSS 通过渐变创建冬天天空的效果,并为雪花创建基本大小。同时,您将调用两个关键帧序列:一个是在雪花向底部下落时旋转雪花(称为雪),另一个是将雪花从一边飘到另一边(漂移)。(参见清单 7-19 。)
清单 7-19 。 基础 CSS 为雪花动画
html { min-height: 100%; }
body { height: 100%; background: linear-gradient(#b5d3ff, #30509a); }
img.flake { width: 150px; height: 150px; position: relative;
animation: snow 8s linear infinite forwards,
drift 12s ease-in-out forwards infinite; }
关键帧序列以不同的长度运行,每个雪花从不同的高度开始(清单 7-20);这种组合创造了随机循环运动的印象。
清单 7-20 。 为雪花动画的关键帧序列
@keyframes snow {
100% { top: 700px; transform: rotate(2.2turn); }
}
@keyframes drift {
0% { left: -5px; }
25% { left: 55px; }
55% { left: -15px; }
100% { left: 0px; }
}
这是可行的,但是如你所见,有两个问题。首先,要添加更多雪花,需要在标记中添加更多图像,这很快就会变得很累。第二个问题是,较小的雪花会被解释为距离更远,因此从屏幕底部落下需要更长的时间(在这个例子中,我们假设它的高度为 700 像素)。你可以通过使用添加的具有不同动作计时的类来调用雪花,如清单 7-21 所示。
清单 7-21 。 通过创建二级类来减缓动画
img.flake { width: 150px; height: 150px; position: relative;
animation: snow 8s linear infinite forwards,
drift 12s ease-in-out forwards infinite;
}
img.slow {
animation: snow 16s linear infinite forwards,
drift 24s ease-in-out forwards infinite;
}
分开的类意味着较慢的雪花可以通过调用两个类来控制。创建更多的变化意味着创建更多的类,这又回到了添加更多雪花的问题上。当你在第九章开始整合 JavaScript 和 CSS3 动画时,你会解决这个问题。
用于 SVG 的工具
目前,广泛使用 SVG 的最大障碍之一是设计工具的相对缺乏。最受欢迎的包括:
- Adobe Illustrator 有一个 SVG 导出选项,但是,与许多所见即所得工具一样,它创建的代码并不十分高效:生成的 SVG 文件通常包含远远多于所需的代码。
- 开源软件 Inkscape (
http://inkscape.org/
)与 Adobe Illustrator 面临同样的问题。然而,Inkscape 确实有处理本地 SVG 文件的优势,它支持 SVG 过滤器。 - Raphaë是一个小型的 JavaScript 库,可以在 JavaScript 中轻松创建和操作 SVG。
CSS3 滤镜简介
CSS 过滤器允许在网页内容出现之前对其进行处理。最常见的(但绝不是唯一的),这些过滤器适用于位图图像。滤镜极大地改变了典型的 web 开发图像制作工作流程:设计师可以从 PhotoShop 中优化和导出图像,而不是永久地“烘焙”图像像素中的视觉效果,这将保持相对不变,将视觉变化留给 CSS。
这意味着图像可以在 CSS 中动态修改,而不是必须在 PhotoShop 中重新编辑原始图像,导出它们,然后重新上传到服务器。这也意味着这些效果可以被动画化。
黑白/灰度滤镜过渡
灰度滤镜效果非常适合在线作品集或照片库。CSS 滤镜可以轻松地将彩色图像转换为黑白图像,而不是使用复杂的 JavaScript 或 Flash 解决方案。你可以在悬停时撤销这种转换,并附加一个过渡以缓解两种状态之间的转换,如图 7-4 中所示的图像(由 Andrew Larsen 在www.flickr.com/photos/papalars/4013594219
创作,经知识共享许可)。参见清单 7-22 。
图 7-4。用 CSS(左)过滤的彩色图像(右)
清单 7-22 。应用灰度滤镜过渡
img { border: 20px solid #fff; box-shadow: 10px 10px 8px rgba(0,0,0,0.3); }
img.bw { filter: grayscale(1); transition: 1s filter linear; }
img.bw:hover { filter: grayscale(0); }
<img src=lake-louise.jpg alt="Lake Louise, Alberta, Canada" class=bw>
和我在本章中描述的其他滤镜一样,灰度滤镜采用介于 0(无效果)和 1(全效果)之间的值,浮点值介于两者之间。请注意,您不能平稳地过渡到“无”或未应用过滤器的状态;必须给过滤器一个新值。
棕褐色滤镜过渡
棕褐色滤镜可以用来创建一个“老化”的照片效果,如图图 7-5 (应用于 Robb North 的一张照片,www.flickr.com/photos/robbn1/3650713106
)。
图 7-5。用 CSS(左)过滤的彩色图像(右)
实现图 7-5 所示效果的代码与灰度非常相似(见清单 7-23 )。
清单 7-23 。在图像上过渡棕褐色滤镜的 CSS
img { border: 20px solid #fff; box-shadow: 10px 10px 8px rgba(0,0,0,0.3); }
img.old { filter: sepia(1); transition: 1s filter linear; }
img.old:hover { filter: sepia(0); }
<img src=barn.jpg alt="Old barn" class=old>
虽然图 7-5 中的谷仓照片之前已经在图像编辑器中处理过,添加了“风化”和部分晕影外观,但你只使用 CSS 为图像提供了棕褐色调。其他效果也可以用 CSS 添加。
显影宝丽来照片效果
通过用div
包围图像并制作应用于容器元素的样式的动画,可以将滤镜和带有inset
值的box-shadow
组合起来,重新创建正在显影的宝丽来照片的外观。保存在里面的图像需要使用z-index
被“推回”,这样div
的内嵌内阴影就会覆盖它(见清单 7-24 )。
清单 7-24 。用宝丽来效果转换图像
div.polaroid { float: left; border: 25px solid #f3f4e3; border-bottom-width: 45px;
box-shadow: 0 0 200px 200px rgba(29,25,4,1) inset;
filter: sepia(.8); transition: 3s all ease-in; }
div.polaroid img { position: relative; z-index: -1; }
div.polaroid:hover { filter: sepia(.2); box-shadow: 0 0 50px 0 rgba(29,25,4,0.2) inset }
<div class="polaroid" > <img src="barn.jpg" alt="Photograph of an old barn" > </a>
模糊滤镜过渡
模糊滤镜需要小心使用;过度使用它们会使网站难以阅读或互动。模糊是为数不多的不采用 0 到 1 之间的值的滤镜之一。相反,它使用长度测量来设置模糊量。(参见图 7-6 ,其中使用了 Louise Docker 的另一幅知识共享图像www.flickr.com/photos/aussiegall/6311469113
.
)
图 7-6。使用 CSS 模糊(左)过滤的彩色图像(右)
请注意,模糊效果会延伸到整个图像,包括边框和阴影。通过使边框和阴影成为第二个父元素(如div
)的属性,可以限制模糊的程度。(参见清单 7-25 。)
清单 7-25 。将模糊滤镜应用于图像
img { border: 20px solid #fff; box-shadow: 10px 10px 8px rgba(0,0,0,0.3); }
img.old { filter: blur(2px); transition: 1s filter linear; }
img.old:hover { filter: blur(0px); }
摘要
SVG 和滤镜是可以添加到 CSS 动画中的两个最强大的功能。SVG 允许无分辨率的过渡和动画,而过滤器允许快速的客户端视觉图像处理。
SVG 十几年了;CSS 滤镜是全新的。两者都立即受到你用它们创造的东西是单一的这一事实的限制:你的创造不能立即被重复。在屏幕上制作一个元素的动画很容易,您可以重用该动画来制作另一个元素的动画,但是每个新元素都必须单独创建。
您可以使用 JavaScript 来简化、增强和改变您的 CSS3 动画和过渡,轻松地复制动画元素并制作新的动画,您将在下一章中探索这一功能。
八、将 CSS3 动画与响应式网页设计和 JavaScript 整合
现在是时候把你到目前为止看到的所有元素放到你的网页上了。要做到这一点,你需要将 CSS3 转换、过渡和动画与当前的网站开发原则相结合,包括响应式网页设计(RWD) 。
在基本层面上,一个响应式网站将由一个流动的设计组成,大多数元素以百分比、rem
、em
、vh
或vw
单位来衡量,而不是像素,结合一系列在 CSS 媒体查询中定义的 CSS 断点。这些断点的位置通常由浏览器宽度(更准确地称为视窗)改变时页面设计“中断”的值来定义。为了方便起见,这些值通常用像素来度量,尽管有很好的理由用 rems 或 ems 来度量,以尽可能脱离“屏幕尺寸”的概念
使用响应式 web 设计原则设计网站,可以让访问者在使用各种设备访问网站时获得流畅、连续和不间断的体验:桌面浏览器、平板电脑、手机和其他设备。
在设计失败的宽度处指定断点是一个很好的做法,而不是使用最新 iPhone 或 iPad 型号的显示尺寸。移动设备变化很快,智能手机和平板电脑的种类比大多数人意识到的要多得多,尤其是在 Android 市场。让网站响应与你的设计相关的尺寸要比今年技术的任意分辨率好得多。
在每个断点处,您指定布局中的更改:元素被调整大小或重新定位,以及出现或消失。一个移动优先的理念颠倒了典型的开发过程:从一开始你就为小屏幕(水平分辨率为 320 像素或更低)设计一个站点,并随着视窗的扩大调整页面,让站点有更多的空间“呼吸”Mobile first 的优势在于,在大多数移动设备的空间和带宽都非常有限的情况下,它可以让你集中精力开发网站绝对需要的功能。
注意一般来说,移动用户在使用桌面浏览器时,应该拥有完全相同的工具、导航和网站功能。在响应站点中为移动用户删除一个特性之前,考虑一下这个组件是否是必需的。
许多 web 开发人员纯粹从@media
查询的角度考虑响应式 web 设计。虽然查询是一个非常重要的组件,并且将是这里的重点,但重要的是要理解许多响应式解决方案将需要 JavaScript 和 PHP 等服务器端解决方案的额外贡献(通常称为 RESS:响应式设计+服务器端组件)。
在这一章中,我将重点介绍如何使用 CSS 动画来简化响应页面中断点之间的转换。应该注意的是,虽然设计师们喜欢这个东西——你可能会发现自己不断地来回拖动浏览器窗口的右下角来欣赏你将要创造的效果——但你的许多用户永远不会看到它。大多数访问者在访问网站时,浏览器的宽度是固定的,并且在整个访问过程中保持不变,尤其是移动用户,他们没有改变视窗大小的选项。因此,你将在本章中探索的许多技术应该被认为是“最好拥有”的,而不是必需的。
在这一章的后面,你将会看到 JavaScript 如何与 CSS3 动画和过渡集成,以使它们更加有效和高效。
在无过渡的响应式网页设计中调整元素大小
使用我上面讨论的原则,您可以在调整视窗大小时“动画化”网页内容,而根本不需要使用过渡或关键帧动画。
动态调整图像和视频的大小
首先,你将根据视窗大小调整图像和视频的大小(见图 8-1 )。
图 8-1。页面内响应图像的两种状态
通过相对于元素的容器调整元素的大小,您可以在调整浏览器窗口大小时平滑地“激活”元素的大小。严格来说,这根本不是动画。在这个阶段,你只是通过相对于它的容器缩放来动态地调整图像的大小(见清单 8-1 )。
清单 8-1。 一个有求必应的形象
html { font-size: 62.5%; }
p { font-size: 1rem; }
.left { max-width: 100%; height: auto; float: left; margin-right: 2rem; margin-bottom: 1rem; }
<section>
<p><img src="pentacon-bike.jpg" alt="Pentacon Bike" class="left">Lorem ipsum dolor sit amet. . .
</section>
当用户调整浏览器大小时,他们会有图像(由 filtran,www.flickr.com/photos/filtran/2978448269/
)被动态调整大小的印象。如果浏览器设置得足够宽,照片将以其自然大小显示,但会缩放以适应无法包含其完整尺寸的视窗宽度。
这种方法有两个可能的缺点:根据图像相对于正文文本的初始原始大小,图像可能在大或小的屏幕宽度下显示超出比例。或者,可以将图像设置为其容器宽度的百分比,以便它在整个视口范围内缩小。例如,假设您已经确定段落的整体尺寸为 900 像素宽,每个尺寸(文本行)为 90 个字符。给定 HTML 和段落的表示规则,这意味着 section 元素的宽度等于 90rem。图像的自然大小为 425 像素宽。要使图像完全可缩放,您需要将所有这些值转换为百分比:
425 / 900 = 0.4722
这意味着图像占据了段落宽度的 47.22%,并转化为你在清单 8-2 中看到的 CSS。
清单 8-2 。 一个交替反应的形象
section { max-width: 90rem; }
.left { width: 47.22%; height: auto; float: left; margin-right: 2.5%; margin-bottom: 2%; }
CSS 声明的这种组合将创建如图 8-2 所示的页面外观。
图 8-2。备选响应图像
剩下的一个问题是在极端的视口尺寸下创建非常大或非常小的图像的可能性。你可能希望通过设置图像的最小和最大尺寸来保护设计(见清单 8-3 )。
清单 8-3 。 具有最小和最大尺寸的响应式图像
.left { width: 47.22%; height: auto; float: left;
margin-right: 2.5%; margin-bottom: 2%;
max-width: 425px; min-width: 150px;
}
您可以使用相同的技术在<video>
元素上实现相同的效果。(通过 YouTube 或 Vimeo 等服务嵌入视频时,让视频变得有响应性要复杂得多;我推荐 Dave Ruppert 在http://fitvidsjs.com/
的 FitVids JQuery 插件来实现这一点。
提示不使用关键帧、过渡或媒体查询也可以动态调整文本大小。vw
单位测量视窗宽度:1vw
是浏览器窗口宽度的 1/100 th 。因此,如果视口宽度为 400 像素,1vw
将等同于4px
。当浏览器调整大小时,您可以使用此单位来缩放文本:
h1 { font-size: 4vw; }
有了这个 CSS,网页上的 h1 元素会随着浏览器的伸缩而调整大小。你也可以在其他元素上使用 vh 和 vw。
响应背景图像
通过使用background-size
属性(这里使用的是 Vinoth Chandar 在www.flickr.com/photos/vinothchandar/6168933212/
的一幅图像),你可以很容易地动态调整背景图像的大小,以响应视窗的变化,如清单 8-4 所示。
清单 8-4 。 一幅有求必应的背景图像
html, body { min-height: 100%; font-size: 62.5%; }
body { background-image: url(fog.jpg); background-size: cover; }
结合一些正文,这给出了如图 8-3 所示的效果。
图 8-3。有反应的背景图像
有关“过渡”背景图像的其他方法,请参见第三章。
使用过渡调整响应式网页设计中元素的大小
在@media 查询断点之间转换元素是完全可能的。从设计的角度来看,需要注意的主要问题是元素在调整视窗大小时可能会在不同状态之间“跳跃”,这可能会让用户感到惊讶。
让我们创建一个设计,在页面中间有一个大的 h1 元素,在一张照片的上面(清单 8-5 )。
清单 8-5 。 用于响应背景图像和过渡文本的 HTML
body { background-image: url(fog.jpg); background-size: cover;
background-repeat: no-repeat; color: #fff; font-family: Avenir, Arial, sans-serif;
}
h1 { font-family: 'Calluna Sans', Arial, sans-serif; text-align: center;
font-size: 10rem; margin: 8rem auto;
}
随着视窗变窄,标题文本会自然地在空格处断开,如图图 8-4 所示。
图 8-4。一个无响应的标题元素在一个狭窄的视口下的空间上断开
相反,如果我们想让标题保持在一行,我们可以减小断点处的文本大小,并在它们之间转换,如清单 8-6 所示。
清单 8-6 。 用于响应背景图像和过渡文本的 HTML
h1 { font-family: 'Calluna Sans', Arial, sans-serif;
text-align: center; font-size: 10rem; margin: 8rem auto; transition: 1s font-size linear;
}
@media screen and (max-width: 1100px) {
h1 { font-size: 8rem; }
}
@media screen and (max-width: 900px) {
h1 { font-size: 7rem; }
}
@media screen and (max-width: 800px) {
h1 { font-size: 6rem; }
}
清单 8-6 中的代码给出了如图图 8-5 所示的结果。
图 8-5。响应式标题元素
用 CSS3 媒体查询和过渡指示视窗大小
使用媒体查询,您可以在浏览器变窄时触发元素的外观“变形”——例如,在响应页面中显示不同的查看模式,以便用户了解他们不是在移动平台上查看桌面站点的简单解释,而是在查看根据浏览器窗口的大小进行自我定制的页面(参见图 8-6 )。
图 8-6。三种不同状态的响应转换符号,代表三种不同的视窗尺寸
首先,您将设置一系列断点:
- 120em 宽及以上将被视为“宽屏”。
- 在 80em 宽的情况下,你可以假设用户正在用平板电脑浏览网站。
- 在 40em 及以下,你应该假设用户正在使用智能手机。
接下来,您将创建用于从纯 CSS 中显示这些不同状态的元素。总共有三个元素:一个包含div
的元素,它有一个绝对位置,将所有的元素保持在屏幕的右上角;一个span
代表显示器,另一个span
代表底座或按钮,如清单 8-7 中的所示。
清单 8-7 。 响应页面中转换显示模式的 HTML 代码
<div id="viewingmode">
<span id="display"></span>
<span id="buttonbase"></span>
</div>
接下来,您将为清单 8-8 中的元素创建基本 CSS。这将包括过渡设置。
清单 8-8 。 CSS 代码创建响应式设计中的查看模式符号
* { box-sizing: border-box; }
body { font-family: Avenir, sans-serif; margin: 100px 0; }
#viewingmode {
width: 150px; height: 150px; background: rgba(0,0,0,0.2);
position: absolute; top: 0; right: 0; text-align: center;
}
/* screen, in default widescreen presentation */
#display {
width: 80%; height: 50%; border: 12px solid #585858;
border-radius: 5px; margin-top: 20px; background-color: #eee;
}
/* base, in default monitor stand - keyboard configuration */
#buttonbase {
width: 90px; border: 12px solid #585858; border-radius: 5px;
position: absolute; top: 100px; left: 30px; transform-origin: 25px 5px;
}
#display, #buttonbase { display: inline-block; transition: .5s all linear; }
最后,您将创建媒体查询来改变#display
和#buttonbase
元素的外观(清单 8-6 )。请注意,这些更改是级联的:较小的屏幕尺寸将继承在较大屏幕的媒体查询中所做的更改。
清单 8-6 。 CSS 代码创建响应式设计中的查看模式符号
@media screen and (max-width: 80em) {
#display { width: 50%; height: 60%; border: 10px solid #585858; }
#buttonbase { width: 8px; height: 8px; border: none;
border-radius: 50%; background: #fff; top: 101px; left: 70px; }
}
@media screen and (max-width: 40em) {
#display { border-width: 23px 5px; width: 45%; height: 73%; }
#buttonbase { top: 110px; }
}
如上所述,大多数用户——尤其是移动用户——不会看到这些动画,因为移动设备的屏幕宽度是固定的,没有什么可调整的。另一种方法可能是显示屏幕处于纵向还是横向模式,如清单 8-7 所示。
清单 8-7 。 媒体查询以在设备上显示横向和纵向模式
@media screen and (orientation: landscape) and (max-width: 80em) {
#display { transform: rotate(90deg); }
}
这就产生了你在图 8-6 中看到的变化显示,当视口根据设备的方向调整大小时,每个设备“变形”到下一个。同样的原理也可以应用于动画网页的不同方面。
为移动设备优化 CSS 过渡和动画
随着时间的推移,移动设备中的处理器不可避免地会变得更快、更强大,但它们将永远落后于全台式机型号的能力。同样不可避免的是,开发人员将倾向于在他们的鼻子前面为平台编码,而不是他们的观众可能在使用什么(正如我们在浏览器战争和供应商前缀偏见中看到的)。开发人员为他们面前的屏幕编写代码,其结果可能并不总是符合移动设备更受限制的能力。
有几种方法可以改善在移动设备上运行缓慢的 CSS 效果:
-
尝试通过设备的 GPU 进行过渡和优化:由于其更复杂和要求更高的性质,许多浏览器会尝试通过运行 CSS 动画的设备的专门图形处理单元来平滑 3D 变换、过渡和动画(在第九章中介绍)。你可以通过一个“空的”3D 操作启动 CSS 声明块来搭这个优化流的顺风车,这个操作不会在视觉上改变元素,但会允许 GPU 访问同一个声明中的 2D 变换:
transform: translate3d(0,0,0);
注意 Remy Sharp 在www.youtube.com/watch?v=IKl78ZgJzm4
的 YouTube 上有一个非常有效的视频,展示了通过设备的 GPU 进行过渡和优化的优势。
- 使用媒体查询限制或替换动画:您可以通过在
@media
查询下创建不同版本,为移动设备设置更多受限版本的动画。 - 确保包含动画元素的页面将完全缩小到移动屏幕大小:边缘被剪掉的动画在移动设备上显然不好看或不能有效地运行。
将 CSS3 媒体查询与 SVG 集成
您还可以使用媒体查询(以及其他 CSS3 特性)来定位 SVG 中的元素。
就像 Adobe PhotoShop 和其他图形应用一样,SVG 包含了多层的概念。这意味着您可以将多个图形合并到一个 SVG 文件中,并使用 CSS 切换每个图形的可见性。
让我们回到把图标放在浏览器窗口右上角的想法,但是改为把它们做成 SVG 在这种情况下,一系列不同体型的分组 SVG 图纸,包括中胚层和外胚层类型。为了节省空间,简化的清单 8-8 中只显示了第一种体型的代码。
清单 8-8 。 多个图层的 SVG 文件
<svg version="1.1" width="142px" height="340px"
viewBox="0 0 142 340">
<style>
g { visibility: hidden; }
g:target { visibility: visible; }
</style>
<g id="ectomorph">
<path d="M11.356,682.57c5.297,6.354,10.253,10.084,17.781,14.844
C18.907,694.043,15.905,690.475,11.356,682.57z"/>
. . . .
</g>
<g id="mesomorph">
<path d="M9.981,679.538c0,0-8.719,7.188-8.719,17.125 . . ." />
. . . .
</g>
图 8-7。SVG 矢量图形的不同命名层相互叠加
每个分组的 SVG 图都是一层一层的,如图 8-7 所示。然后用 CSS 隐藏这些层。如果 URL 指向某个组,嵌入样式表中的下一行将打开该组的可见性。您可以使用 CSS 将 SVG 文件作为背景图片放在div
中,如清单 8-9 中的所示。
清单 8-9 。 多个图层的 SVG 文件
div#shapes { background-image: url('bodyshapes.svg#mesomorph');
position: absolute; top: 0; right: 0; width: 145px; height: 355px; }
使用相同的定位技术,您可以交换用于div
( 清单 8-10 )背景图像的 SVG 文件中的图层的可见性。
清单 8-10 。 多个图层的 SVG 文件
@media screen and (max-width: 1000px) {
div#shapes { background-image: url('bodyshapes.svg#ectomorph); }
}
最后,应该注意的是,SVG 元素可以使用<animate />
元素来制作动画,如果 SVG 被直接插入到页面上,而不是被用作背景图像,那么每个层的可见性都可以使用过渡来制作动画。
用 JavaScript 触发 CSS3 转换
JavaScript 可以用来触发 CSS3 中的过渡和动画。例如,当用户到达页面底部时,你可以让元素淡入以增加他们对相关内容的兴趣(见图 8-8 )。CSS 无法检测滚动条的状态——您需要使用 JavaScript 来完成这项工作,然后使用 CSS3 将出现的元素制作成动画。
图 8-8。用 JQuery 触发的 CSS 转换制作页脚元素动画
注意有一种观点认为,如果你用 JavaScript 开始制作动画,你也可以继续用同样的语言来制作动画。然而,正如在第一章中所讨论的,CSS3 过渡将会更加平滑和高效,并且将会获得比 JavaScript 更高的帧速率。JQuery Transit ( http://ricostacruz.com/jquery.transit/
)和 Move.js ( http://visionmedia.github.com/move.js
)等库越来越多地被用于将 JavaScript 与 CSS3 过渡和动画方法挂钩,这是有原因的。这种功能分离反映了内容、表示和行为之间的分离:在这个例子中,JavaScript 用于检测 DOM 事件,CSS 用于向内容呈现外观上的变化。
首先,让我们假设你有足够的内容来填充视窗:我将在清单 8-11 中显示一个标题和一段 Lorem ipsum 填充文本,以指示正文的开始。在页面的最底部,你会在一个footer
元素中看到两个链接。第一个链接将引导用户到逻辑上“先前”于他们当前所在页面的内容;右边的第二个链接将引导他们进入当前页面之后的“下一个”页面。为了节省空间,我在链接中使用了 Unicode 黑色的向左和向右的三角形:你应该分别使用合适的 HTML 实体(◀
和▶
。
清单 8-11 。 超长页面的 HTML 代码,页脚中的内容采用 JavaScript 和 CSS3 转换
. . .
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
</head>
<body>
<article>
<header><h1>A History of the Roman Empire</h1></header>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. . .
<footer id="articlefooter">
<a href=# id=prevpage>◀<img src=cicero.png alt=Cicero>Cicero and Claudius</a>
<a href=# id=nextpage>Caligula and Ceasar <img src=caesar.png alt=Caeasar>▶</a>
</footer>
</article>
</body>
文章中的内容应该将footer
推到视窗底部边缘以下。您将设计页面样式,稍微缩进footer
中的链接,并通过降低它们的不透明度使它们不可见(清单 8-12 )。
清单 8-12 。 带页脚导航页面的基本 CSS 代码
body { font-family: Avenir, sans-serif; margin: 100px 0; }
article { width: 768px; margin: 0 auto; }
footer#articlefooter { padding: 0 25px; }
footer#articlefooter a {
text-decoration: none; color: #000; opacity: 0; position: relative;
}
footer#articlefooter a img { width: 77px; height: 77ps; vertical-align: middle; }
a#prevpage { padding-left: 70px; float: left; transition: 1s 1s opacity linear, 1s 1s translateX ←linear; }
a#nextpage { padding-right: 70px; float: right; transition: 1s .5s opacity linear, 1s 1s ← translateX linear; }
您已经将 CSS3 转换代码与链接相关联:如果它们同时被触发,与#nextarticle
元素相关联的.linkmoveright
将首先移动。短暂的延迟之后,紧接着是与#prevarticle
相关的.linkmoveleft
级。
请注意,通过声明您正在更改的属性,您已经使转换更加有效:因为它们是多个属性,所以您使用了由逗号分隔的重复。
与其将元素的变化与:hover
或:focus
伪选择器相关联,不如将它们定义为一个新的类(参见清单 8-13 )。
清单 8-13 。 变换为页脚导航元素
.linkmoveleft { transform: translateX(-70px); opacity: 1; }
.linkmoveright { transform: translateX(70px); opacity: 1; }
最后,您将在页面的最底部添加一个脚本,它将查看一些变量并判断何时将这些类添加到元素中。
如清单 8-14 中的所示,articleheight
变量决定了主体的总高度,包括其所有内容。scrollTop
测量页面中有多少像素比浏览器窗口的顶部边缘*高:这将是页面加载时的0
,随着用户向下滚动,该值增加。通过将articleheight
除以 2 并将结果与scrollTop,
进行比较,您可以确定用户何时滚动了半个页面,然后应用这些类(*清单 8-14 )。
清单 8-14 。 JQuery 代码将类放置在导航页脚元素上
<script>
$(function() {
var articleheight = $("body").height();
$(window).scroll(function() {
if ($(this).scrollTop() > (articleheight / 2)) {
$("#prevpage").toggleClass("linkmoveleft");
$("#nextpage").toggleClass("linkmoveright");
});
});
</script>
CSS3 transitions 将响应于与它们相关联的元素状态的任何适当变化而触发,无论这些变化是由 CSS、JavaScript 还是其他任何东西强加的。在这种情况下,使用 JQuery 放置包含元素不透明度和位置更改的新类就足以引发转换。
虽然这是可行的,但仔细观察结果会发现这种方法有几个可能的缺点:
- 特别长的文章的正文可能会超过浏览器窗口高度的两倍。清单 8-14 的脚本中的比较意味着对于这样的文章,过渡可能会在用户到达页面底部之前触发(也就是说,在阅读了一半以上的文章后,他们可能仍然看不到页脚)。
toggleClass
函数意味着当用户向上滚动时,JQuery 将尝试撤销对这些类的应用,当用户返回页面底部时,JQuery 将再次触发它们。这种反复的转变可能会令人讨厌。- 最后,我们假设页脚总是与页面底部一致。这不一定是真的:页脚下面可能会有注释,这会显著增加文章的整体高度,导致 JavaScript 过早地应用这些类。
注意在 HTML5 规范下,嵌套在另一篇文章中的文章元素被假定为包含对父文章的评论。
另一种方法是,当页脚清楚地显示在页面上时,只应用一次过渡(清单 8-15 )。
清单 8-15 。 改进了 JQuery 代码,将类放置在导航页脚元素中
$(function() {
var footerBottom = $("#articlefooter").offset().top + $("#articlefooter").height();
$(window).scroll(function() {
if ($(this).scrollTop() > (footerBottom - $(window).height())) {
$("#prevpage").addClass("linkmoveleft");
$("#nextpage").addClass("linkmoveright");
}
});
});
如果您想支持 Internet Explorer 6 到 8,您可以使用 Modernizr ( http://modernizr.com/
)来检测浏览器对 CSS 转换的支持。如果浏览器缺乏支持,JQuery 可以依靠动画元素本身(见清单 8-16 )。
清单 8-16 。 改进了 JQuery 代码,将类放置在导航页脚元素中
<script src=//ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js></script>
<script src=scripts/modernizr.js></script>
</head>
<body>
<article>
. . .
</article>
<script>
$(function() {
var footerBottom = $("#articlefooter").offset().top + $("#articlefooter").height();
$(window).scroll(function() {
if ($(this).scrollTop() > (footerBottom - $(window).height())) {
if (Modernizr.csstransitions) {
$("#prevpage").addClass("linkmoveleft");
$("#nextpage").addClass("linkmoveright");
} else {
$("#prevpage").animate({ opacity: 1, left: '-=70'}, 1000, function() { });
$("#nextpage").animate({ opacity: 1, left: '+=70'}, 1000, function() { });
}
}
});
});
</script>
如果你想创建一个更复杂的效果,你可以编写关键帧动画,或者将它们作为类应用,如清单 8-16 中的所示,或者直接使用 JQuery 调用动画。最后一个例子,您将使用后一种方法,调用关键帧动画来复制您刚刚创建的带有过渡的效果(清单 8-17 )。
清单 8-17 。 JQuery 代码用于应用 CSS3 动画
@keyframes leftmove {
100% { transform: translateX(-70px); opacity: 1; }
}
@keyframes rightmove {
100% { transform: translateX(70px); opacity: 1; }
}
<script>
$(function() {
var footerBottom = $("#articlefooter").offset().top + $("#articlefooter").height();
$(window).scroll(function() {
if ($(this).scrollTop() > (footerBottom - $(window).height())) {
$("#prevpage").css('animation', 'leftmove 1s 2s forwards');
$("#nextpage").css('animation', 'rightmove 1s 1s forwards');
});
});
</script>
注意通过使用form
标签和checkbox
按钮,清单 8-17 中显示的技术可用于避免第四章中演示的语义可疑的交互方法。您可以简单地使用 JavaScript 来检测任何元素上的 DOM 事件,并启动其他元素上的 CSS3 转换、过渡和动画,而不是跳过标记环来获得您想要的 CSS 结果,如清单 8-18 所示。
清单 8-18 。 JQuery 代码用于在点击时应用 CSS3 动画
$(function() {
$("#at").click(function() { $(".box").toggleClass("wobble"); });
});
用 JavaScript 定制 CSS3 过渡
在第六章中,你看到了用 CSS3 制作多个 SVG 元素的动画。当你这样做的时候,你会遇到一个主要的问题:为了制作元素动画,你必须在页面上将它们创建为单独的元素,这使得创建随机性的外观变得困难(见图 8-9 )。
图 8-9。用 CSS3 制作的随机元素动画
为了减少您必须在任何页面中进行的手工编码,您将使用 JavaScript 来解决这两个问题。为了简单起见,您使用红色的div
元素作为动画对象。在页面底部,你将添加清单 8-19 中的脚本。
清单 8-19 。 JavaScript 代码创建随机分散元素
<script>
var html = [];
for (i = 0; i < 30; i++) {
var randomX = Math.random() * (100 - 1) + 1;
var randomY = Math.random() * (1200 - 1) + 1;
var randomZ = Math.random() * (100 - 1) + 1;
html.push('<div style="left:'+randomX+'%;top:'+randomY+'px;width:'+randomZ+'px;height: ←'+randomZ+'>
</div>');
}
$("body").append( html.join('') );
</script>
您可以使用一个 JavaScript 循环来创建任意多的div
元素的副本。对于每个元素,您将使用三个变量来确定其水平位置、距视口顶边的偏移量以及水平和垂直大小。
然而,你拥有的许多元素都将遵循相同的关键帧动画规则,如清单 8-20 所示。
清单 8-20 。 JavaScript 代码创建随机分散元素
html { height: 100%; }
body { min-height: 100%; margin: 0; }
@keyframes snowflake {
100% { transform: translateY(1800px) rotate(1200deg); }
}
div { background: red; position: absolute; animation: snowflake 40s linear infinite; }
虽然这种方法可行,但有几个问题:所有的形状都以相同的速度下落,并同步转动。小的元素看起来离得更远,应该下落得更慢,而所有的元素都应该以随机旋转开始。为了实现这一点,您将创建几个类,这些类将调用具有不同计时的关键帧动画,并根据元素的大小用 JavaScript 应用这些类(清单 8-21 )。
清单 8-21 。 CSS 和 JavaScript 代码创建随机化的分散元素
<style>
@keyframes snowflake {
100% { transform: translateY(1800px) rotate(1200deg); }
}
div { background: red; position: absolute;
animation-name: snowflake;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
.small { animation-duration: 40s; }
.medium { animation-duration: 20s; }
.large { animation-duration: 10s; }
</style>
<body>
. . .
</body>
<script>
var html = [];
for (i = 0; i < 30; i++) {
var randomX = Math.random() * (100 - 1) + 1;
var randomY = Math.random() * (1200 - 1) + 1;
var randomZ = Math.random() * (100 - 1) + 1;
var randomR = Math.random() * (360 - 1) + 1;
var sizes = ['small','medium','large'];
var dim = sizes[Math.round(randomX/50)];
html.push('<div style="left:'+randomX+'%;top:- ←
'+randomY+'px;width:'+randomZ+'px;height:'+randomZ+'px;transform:translateY(0px) ←
rotate('+randomR+'deg)" class='+dim+'></div>');
}
$("body").append( html.join('') );
</script>
这段 JavaScript 代码是基础的,还可以做得更深入,但是需要注意的重要方面是,您正在利用每种技术的核心优势:您使用 CSS 提供外观规则,使用 JavaScript 对 DOM 进行快速更改。
摘要
本章介绍了 CSS3 动画、转换和过渡与响应式 web 设计、JavaScript 和 SVG 的集成。通过使用百分比和 vw 单位缩放内容,并使用@media
查询在断点处动画化元素,您可以创建“动画”元素的印象来响应视窗大小调整。
您还可以使用 JavaScript 为 CSS 本身无法检测的动画提供触发点,并使用脚本语言为动画序列制作多个随机的“克隆”元素。这些技术的集成可以更进一步:一个很好的例子是 Sebastian Markbå ge 通过使用 SVG 路径数据生成关键帧动画声明(http://csspathanimation.calyptus.eu/
)来解决让元素以恒定的运动速度跟随复杂路径的技术难题。
在本书中,到目前为止,您一直在使用 CSS3 在页面的平面上移动元素。在下一章中,你将在 3D 空间中操作 HTML 元素。