使用 CSS3 动画构建出色的应用程序





5.00/5 (10投票s)
本文将展示一个有趣的演示,突出 CSS3 动画的潜力,介绍如何构建简单的动画以及如何在 JavaScript 中处理回退。
如今的 HTML5 应用借助新的 CSS3 规范,可以提供出色的用户体验。其中之一就是 CSS3 动画。它可以帮助您在 HTML 元素上构建丰富的动画。这可以为用户提供有趣的反馈,并实现快速流畅的用户界面。由于这些新动画大多数都由 GPU 进行硬件加速,因此它们无疑提升了新一代 HTML5 应用的质量标准。
根据 W3C 网站上的“CSS 动画模块级别 3”规范,CSS3 动画引入了定义的动画,这些动画指定了 CSS 属性在给定时间间隔内将采用的值。此规范是 CSS 过渡的扩展。
由于 CSS3 动画是 CSS3 过渡的扩展,您应该首先阅读我同事 David Catuhe 关于过渡的文章:CSS3 过渡简介。
本文将展示一个有趣的演示,突出 CSS3 动画的潜力,介绍如何构建简单的动画以及如何在 JavaScript 中处理回退。
首先,我们快速演示一下 CSS3 动画是什么。这是一个星球大战 AT-AT 的示例动画,它使用 CSS3 动画来驱动运输部分的动画(如果您的浏览器不支持 CSS3 动画,它将回退到 JavaScript)。
您也可以在此处的新窗口中测试此示例:http://david.blob.core.windows.net/html5/css3atat/index.htm
注意:此示例已在 IE10 PP3/PP4、Chrome 15、Firefox 8 和 iPad 2 中使用原生动画成功测试,并在 IE9 桌面和移动版(Windows Phone)中使用 JS 回退进行了测试。出于未知原因,它在 Opera 11.50 中的表现有些奇怪,但在 11.60 中运行正常。此外,我们友好的博客平台通常通过 meta 标签强制使用 IE9 渲染引擎。要将其强制恢复到 IE10 标准模式,请按 F12 键并将“文档模式”的值改回 IE10。否则,请在新窗口中查看演示。
此示例基于 Anthony Calzadilla 的精彩作品。您可以在他的网站上查看其他令人惊叹的演示:http://www.anthonycalzadilla.com。我非常喜欢使用 SVG 和 CSS3 动画的 I twitty the fool 示例。CSS3 动画
引言
首先,我们回顾一下您可以用来构建动画的内容。CSS3 动画基本上与 CSS3 过渡使用相同的数值。
以下是它们:单击此处显示/隐藏它们
- 颜色:通过红色、绿色、蓝色和 Alpha 分量进行插值(将每个分量视为数字,如下所示)。
- 长度:作为实数进行插值。
- 百分比:作为实数进行插值。
- 整数:通过离散步长(整数)进行插值。插值发生在实数空间中,并通过 floor() 转换为整数。
- 数字:作为实数(浮点数)进行插值。
- 变换列表:请参阅 CSS 变换规范:http://www.w3.org/TR/css3-2d-transforms/
- 矩形:通过 x、y、宽度和高度分量进行插值(将每个分量视为数字)。
- 可见性:通过离散步长进行插值。插值发生在 0 和 1 之间的实数空间中,其中 1 表示“可见”,所有其他值表示“隐藏”。
- 阴影:通过颜色、x、y 和模糊分量进行插值(将它们视为颜色和数字(如果适用))。如果存在阴影列表,则较短的列表将在末尾填充颜色透明且所有长度(x、y、模糊)为 0 的阴影。
- 渐变:通过每个停止点的位置和颜色进行插值。它们必须具有相同的类型(径向或线性)和相同的停止点数量才能进行动画处理。
- 绘画服务器(SVG):仅支持渐变到渐变和颜色到颜色之间的插值。然后它们的工作方式如上。
- 以上属性的空格分隔列表:如果列表具有相同的项目数,则使用上述规则对列表中的每个项目进行插值。否则,不进行插值。
- 简写属性:如果简写的所有部分都可以进行动画处理,则插值将像每个属性都单独指定一样进行。
并且必须支持以下属性进行动画处理:单击此处显示/隐藏它们
- background-color (颜色)
- background-image (仅渐变)
- background-position (百分比和长度)
- border-bottom-color (颜色)
- border-bottom-width (长度)
- border-color (颜色)
- border-left-color (颜色)
- border-left-width (长度)
- border-right-color (颜色)
- border-right-width (长度)
- border-spacing (长度)
- border-top-color (颜色)
- border-top-width (长度)
- border-width (长度)
- bottom (长度和百分比)
- color (颜色)
- crop (矩形)
- font-size (长度和百分比)
- font-weight (数字)
- grid-* (各种)
- height (长度和百分比)
- left (长度和百分比)
- letter-spacing (长度)
- line-height (数字、长度和百分比)
- margin-bottom (长度)
- margin-left (长度)
- margin-right (长度)
- margin-top (长度)
- max-height (长度和百分比)
- max-width (长度和百分比)
- min-height (长度和百分比)
- min-width (长度和百分比)
- opacity (数字)
- outline-color (颜色)
- outline-offset (整数)
- outline-width (长度)
- padding-bottom (长度)
- padding-left (长度)
- padding-right (长度)
- padding-top (长度)
- right (长度和百分比)
- text-indent (长度和百分比)
- text-shadow (阴影)
- top (长度和百分比)
- vertical-align (关键字、长度和百分比)
- visibility (可见性)
- width (长度和百分比)
- word-spacing (长度和百分比)
- z-index (整数)
- zoom (数字)
SVG
SVG 对象的属性在 SVG 规范中定义为 animatable:true 时是可动画的:http://www.w3.org/TR/SVG/struct.html。但在撰写本文时,我尚未能在任何最新浏览器版本中直接将 CSS3 动画与 SVG 元素结合使用。因此,网络上的当前示例会采用一个小技巧:它们会将 SVG 资源嵌入到由 CSS3 动画的各种 DIV 中,例如 I twitty the fool 示例。
声明
要在 CSS 文件中声明动画,您需要编写类似这样的通用代码:
@keyframes name_of_the_animation {
from {
property_to_animate: initial_value;
}
50% {
property_to_animate: intermediate_value;
}
to {
property_to_animate: final_value;
}
}
也可以这样写:
@keyframes name_of_the_animation {
0% {
property_to_animate: initial_value;
}
50% {
property_to_animate: intermediate_value;
}
100% {
property_to_animate: final_value;
}
}
此动画定义声明了 3 个步骤:0、50 和 100%。您至少需要设置一个 from(或 0%)和一个 to(或 100%)步骤来构建正确的动画(因此最少需要 2 个步骤)。完成此操作后,您可以在 0% 和 100% 之间添加任意数量的关键帧,以精确处理动画的各个步骤。
声明定义后,您可以使用经典的 CSS3 选择器将其应用于元素,还需要配置动画选项。以下是您将看到的通用块类型:
#id_of_the_html_element {
animation-name: name_of_the_animation;
animation-duration: number_of_seconds s;
animation-iteration-count: number | infinite;
}
为了更好地理解,让我们回顾一个实际示例。首先,由于 CSS3 动画规范仍处于草案阶段,您需要使用适当的供应商前缀。让我们以 IE10 为例,使用 –ms 前缀。现在让我们看看我们的 AT-AT 的头部是如何移动的。
这是动画声明:
@-ms-keyframes rotate-skull {
0% {
-ms-transform: rotate(0deg)
}
25% {
-ms-transform: rotate(15deg)
}
50% {
-ms-transform: rotate(-5deg)
}
55% {
-ms-transform: rotate(0deg)
}
75% {
-ms-transform: rotate(-10deg)
}
100% {
-ms-transform: rotate(0deg)
}
}
我们有 6 个步骤(0%、25%、50%、55%、75% 和 100%),通过更改旋转值来处理 CSS3 2D 变换属性。
然后通过此 CSS 规则应用动画:
#skull
{
-ms-animation-name: rotate-skull;
-ms-animation-duration: 7s;
-ms-animation-iteration-count: infinite;
}
我们以“id=skull”的 <div> 元素为目标,并对其应用名为“rotate-skull”的动画。该动画将在 7 秒内完成,并播放 无限次。
如果您的浏览器支持 CSS3 动画,以下是实时结果:
我们可以使用动画简写属性以更简洁的方式编写此规则:
#skull {
-ms-animation: rotate-skull 7s infinite;
}
一旦应用了匹配的规则,动画就会触发。然后您可以通过 JavaScript 或通过 CSS3 更改标签的类来播放或停止动画。
非线性动画
如果您想要非线性动画,可以使用“animation-timing-function”属性。您甚至可以在每个关键帧中混合使用不同类型的计时函数。
基本上,CSS3 动画将使用 三次贝塞尔曲线 来平滑动画,通过计算其持续时间内的不同速度。
支持以下函数:
- linear:恒定速度
- cubic-bezier:速度将根据由两个控制点 P0 和 P1 定义的三次贝塞尔曲线进行计算(因此您需要在此处定义 4 个值:P0x、P0y 和 P1x、P1y)。
- ease:速度将使用 cubic-bezier(0.25, 0.1, 0.25, 1) 进行计算。
- ease-in:速度将使用 cubic-bezier(0.42, 0, 1, 1) 进行计算。
- ease-inout:速度将使用 cubic-bezier(0.42, 0, 0.58, 1) 进行计算。
- ease-out:速度将使用 cubic-bezier(0, 0, 0.58, 1) 进行计算。
以下是 David Catuhe 编写的一个使用纯 JavaScript 来显示每种计时函数影响的模拟工具。
注意:此工具使用 Firefox、Chrome、Opera 11.60 和 IE9/10 支持的内联 SVG。因此,它在 Opera 11.50 和 iPad 上的 Safari 中将无法正常工作。
这是一个很棒的 SVG 工具。您甚至可以用鼠标操作自定义函数来编辑曲线。如果您想了解更多关于这个工具的信息,请再次查看 David 的文章。
如果您的浏览器支持 CSS3 动画,现在让我们看一个简单的演示,该演示使用缓动函数为包含带动画精灵的 canvas 标签添加动画。
以下是此演示中将使用的 CSS3 动画代码:
@-ms-keyframes demo {
from {
-ms-animation-timing-function: ease;
-ms-transform: translateX(0px);
}
50% {
-ms-animation-timing-function: ease-in;
-ms-transform: translateX(300px);
}
to {
-ms-animation-timing-function: ease-inout;
-ms-transform: translateX(900px);
}
}
#testCanvas
{
-ms-animation-delay: 0s;
-ms-animation-duration: 6s;
-ms-animation-iteration-count: infinite;
-ms-animation-name: demo;
}
以及所有供应商前缀的变体,使其也能在 Google Chrome 和 Mozilla Firefox 中运行。这是实时输出:
如果您的浏览器不支持 CSS3 动画但支持 canvas,则应该会显示精灵的跑步动画,但角色不会在屏幕宽度上移动。
注意:如果您想了解更多关于 canvas 和精灵动画的信息,可以查看这篇文章:HTML5 游戏:使用 EaselJS 为 Canvas 中的精灵设置动画
延迟
“animation-delay”属性允许动画在应用后一段时间开始执行。
事件
动画在动画过程中可以触发 3 个事件。它们分别命名为“AnimationStart”、“AnimationEnd”和“AnimationIteration”。根据您的浏览器,正确的名称可能是
- Chrome:webkitAnimationEnd
- Firefox:mozAnimationEnd
- Internet Explorer:MSAnimationEnd
事件将为您提供以下详细信息:
- animationName:触发事件的动画名称
- elapsedTime:动画运行的时间(以秒为单位)
以下是 IE10 的用法示例:
elementToAnimate.addEventListener("MSAnimationEnd", function () {
alert("the end !");
}, false);
更多关于 CSS3 动画
CSS3 动画在 2 个主要方面非常有用:
- 硬件加速:CSS3 动画大多数时候直接由 GPU 处理,可以产生更流畅的结果。这对于移动设备来说是一个非常有趣的方法。
- 更好地分离代码和设计:我知道这一点存在一些争论,但我和 David 都认为,开发者应该尽可能少地了解动画或与设计相关的任何内容。同样,设计师/艺术家也不应该了解 JavaScript。CSS3 因此提供了这种可能性,并允许设计师使用他们的经典工具来生成元素、屏幕之间的适当动画等。
为了强调性能的重要性,我用纯 <canvas> 编写的以下 HTML5 游戏:HTML5 平台游戏 在我的 PC 上以 60 fps 在 IE9/IE10 中运行,但在 iPad 2 上最多只能达到 10 fps。这是因为其 CPU 的限制更大,而 iPad 目前不支持 <canvas> 的硬件加速。使用 CSS3 过渡/动画来为多个较小的 <canvas> 元素设置动画,可以为这款游戏带来巨大的性能提升。在针对移动设备时,请考虑这一点!
浏览器支持
自 IE10 在 Windows 开发者预览版 中推出的 Platform Preview 3 起,我们开始支持 CSS3 动画。正如您在 caniuse.com 生成的以下报告中看到的,CSS3 动画现在已得到广泛浏览器的支持。
但是,由于规范尚未完成(工作草案),您必须使用供应商前缀,例如 –ms-、–moz-、–webkit-、–o-,以实现跨浏览器兼容的应用程序。
但是问题是:如何处理不支持此新功能的浏览器?
第一个选择是“什么都不做”。得益于优雅降级的优点,如果您工作得当,用户只会看到静态图像。例如,Anthony 的这两个原始示例就是这种情况:I Twitty the Fool! 和 Pure CSS3 AT-AT Walker。在 IE9 中查看时,它看起来就像一个静态图像。在 IE10 中查看时,相同的代码会显示漂亮的动画。IE10 用户将获得增强版本,而 IE9 仍能正常查看和使用该网站。您的浏览器越现代化,您获得的视觉优势就越多。
第二个选择是通过 Modernizr 等 JS 库检测该功能,并尝试通过一个 JavaScript 库来提供相同的动画,该库将模仿动画。这通常称为回退机制。不幸的是,我今天还没有找到一个可用且完整的 JS 库可以在浏览器不支持 CSS3 动画时取代它。
因此,我编写了一个或多或少专门为 AT-AT 示例设计的 JS 库。
CSS3 动画 JavaScript 回退库
动画只不过是一系列过渡,由关键帧定义的持续时间分隔。因此,我重用了 David Catuhe 在其过渡助手库中构建的概念。我让您回顾他的文章,以了解代码背后概念的基础。
在我这边,我添加了一些支持来动画 CSS3 2D 变换的旋转和位移值,以及一种遍历关键帧的方法。
以下是您需要审查的库的主要部分:
// Animation object
// It need the HTML targeted element, the name of the animation, its duration & iteration count and
// the keyframes contained in an array object
// View the animation simply as a sequence of transitions played a certain number of times
ANIMATIONSHELPER.animation = function (target, name, duration, iterationcount, keyframes) {
// saving the properties values
this.name = name;
this.duration = duration;
this.iterationcount = iterationcount;
this.target = target;
var elapsedtime = 0;
var keyframeduration = 0;
var elapsedtime = 0;
// Transforming the percentage of each keyframe into duration value
for (var i = 0; i < keyframes.length; i++) {
keyframeduration = ((keyframes[i].percentage * duration) / 100) - elapsedtime;
keyframes[i].duration = keyframeduration;
elapsedtime += keyframeduration;
}
this.currentTransition = { isPlaying: false };
this.keyframes = keyframes;
this.keyframesCount = keyframes.length;
this.currentKeyFrameIndex = 0;
// The nextTransition() function return the next transition to run
// based on the current keyframe to play
this.nextTransition = function (keyframe, ease, customEaseP1X, customEaseP1Y, customEaseP2X, customEaseP2Y) {
var properties = [];
var finalValues = [];
var transition;
// Compared to the original TRANSITIONSHELPER of David Catuhe
// We need a specific code to play with the CSS3 2D Transform properties values
if (keyframe.propertyToAnimate === "transform") {
for (var i = 0; i < keyframe.transformType.length; i++) {
properties.push(keyframe.transformType[i].type);
if (keyframe.transformType[i].type == "rotate") {
finalValues.push({ deg: keyframe.transformType[i].value1 });
}
else {
finalValues.push({ x: keyframe.transformType[i].value1, y: keyframe.transformType[i].value2 });
}
}
// Create a new transition
transition = {
name: this.name + this.currentKeyFrameIndex,
target: this.target,
properties: properties,
finalValues: finalValues,
originalValues: ANIMATIONSHELPER.extractValues(target.style[ANIMATIONSHELPER.currentTransformProperty], this.name),
duration: keyframe.duration,
startDate: (new Date).getTime(),
currentDate: (new Date).getTime(),
ease: ease,
customEaseP1X: customEaseP1X,
customEaseP2X: customEaseP2X,
customEaseP1Y: customEaseP1Y,
customEaseP2Y: customEaseP2Y,
isPlaying: true,
type: "transform"
};
return transition;
}
// If it's a classic property to animate, we're using more or less the TRANSITIONSHELPER as-is
else {
return TRANSITIONSHELPER.transition(this.target, keyframe.propertyToAnimate, keyframe.value, keyframe.duration, TRANSITIONSHELPER.easingFunctions.linear);
}
};
// each animation object has a tick function
// that will be called every 17 ms (to target 60 fps)
// This ticker is monitoring the current state of the transition and
// create a new transition as soon as the old one is finished/dead
this.tick = function () {
if (this.iterationcount > 0) {
if (!this.currentTransition.isPlaying) {
this.currentTransition = this.nextTransition(this.keyframes[this.currentKeyFrameIndex], ANIMATIONSHELPER.easingFunctions.linear);
// We're using our own global ticker only for the 2D transformations
// Otherwise, we're using the one from the TRANSITIONSHELPER library
if (this.currentTransition.type === "transform") {
ANIMATIONSHELPER.currentTransitions.push(this.currentTransition);
}
this.currentKeyFrameIndex++;
// We've reached the last keyframe (100%). We're starting back from the beginning
if (this.currentKeyFrameIndex >= this.keyframesCount) {
this.currentKeyFrameIndex = 0;
this.iterationcount--;
}
}
}
};
};
代码的第一部分遍历每个关键帧,以计算每个百分比指定的精确持续时间。然后,我们定义一个 nextTransition() 函数,该函数将根据当前在关键帧集合中的索引动态构建要播放的下一个过渡。最后,我们有一个 tick() 函数,它将监视应用过渡的当前状态。一旦过渡完成或失效,它会请求下一个过渡,将其推送到要播放的过渡堆栈中,然后移动索引。
此 tick() 函数通过以下代码调用:
ANIMATIONSHELPER.launchAnimation = function (animation) {
// Launching the tick service if required
if (ANIMATIONSHELPER.tickIntervalID == 0) {
ANIMATIONSHELPER.tickIntervalID = setInterval(ANIMATIONSHELPER.tick, 17);
}
// Little closure to launch the tick method on the appropriate animation instance
setInterval(function () { animation.tick(); }, 17);
};
最后,我们有这种类型的代码帮助我们构建关键帧:
// Object to build a new generic keyframe (not working on the CSS3 2D Transform properties thus)
ANIMATIONSHELPER.keyframe = function (percentage, propertyToAnimate, value) {
this.percentage = percentage;
this.propertyToAnimate = propertyToAnimate;
this.value = value;
};
//Objects to build specific rotation keyframes
ANIMATIONSHELPER.rotationkeyframe = function (percentage, value) {
this.percentage = percentage;
this.propertyToAnimate = "transform";
this.transformType = [];
this.transformType.push(new ANIMATIONSHELPER.transformType("rotate", value));
};
为了突出其用法,让我们使用此库重新创建前面简单的 CSS3 动画骨骼示例:
// number of times you'd like the animations to be run
var iterationsNumber = 100;
var skullElement = document.getElementById("skull");
var keyframes = [];
keyframes.push(new ANIMATIONSHELPER.rotationkeyframe(25, 15));
keyframes.push(new ANIMATIONSHELPER.rotationkeyframe(50, -5));
keyframes.push(new ANIMATIONSHELPER.rotationkeyframe(55, 0));
keyframes.push(new ANIMATIONSHELPER.rotationkeyframe(75, -10));
keyframes.push(new ANIMATIONSHELPER.rotationkeyframe(100, 0));
var animation1 = new ANIMATIONSHELPER.animation(skullElement, "rotate-skull", 7000,
iterationsNumber, keyframes);
ANIMATIONSHELPER.launchAnimation(animation1, ANIMATIONSHELPER.easingFunctions.linear);
这是结果,现在可以在所有支持 CSS3 2D 变换的浏览器中运行:
最后,本文开头演示的第一个示例使用 Modernizr 来检查对 CSS3 动画的支持。如果不支持,它将加载在 master.css、moz-master.css 和 ms-master.css 文件中定义的关键帧的代码。
// Checking if CSS3 animations is supported
if (!Modernizr.cssanimations) {
// if so, we can use our JS fallback library
supportElement.innerHTML = "CSS3 Animations <strong>are not supported</strong>";
LoadJSAnimationsFallback();
}
else {
// if CSS3 animation is supported, we have nothing to do.
// The *master.css stylesheets will be automatically applied & used.
supportElement.innerHTML = "CSS3 Animations <strong>are supported</strong>";
}
LoadJSAnimationsFallback() 函数定义在 jsfallback-master.js 中,该文件仅包含所有关键帧声明以及模仿 Anthony 用纯 CSS3 创建的行为所需的 19 个动画。在这种方法中,设计者需要使用库重写所有规则。另一种方法是使用 XHR 调用解析一个 CSS 文件,并动态创建对库的 JavaScript 调用。这需要更多工作,因为您几乎需要在 JavaScript 中重新实现 CSS3 动画规范!
现在您对如何构建回退机制以支持更多浏览器,同时开始使用最新的 CSS3 规范有了一些想法。
您可以在此处下载主要示例的文件:http://david.blob.core.windows.net/html5/css3atat/CSS3ATATNonMinified.zip
它包含 animationsHelper.js、transitionsHelper.js、jsfallback-master.js JavaScript 文件的未压缩版本,以及针对主要供应商前缀的各种 CSS3 声明文件。
结论
CSS3 动画是一项强大的技术,可以将 HTML5 应用提升到一个新的水平。它提供了有趣的应用场景。设计师可以利用它来创建新一代 UI 屏幕,实现流畅自然的动画,而无需开发人员的参与。由于它大多数时候都经过硬件加速,因此开发人员也应关注此规范。最后,两者都可以协同工作。设计师可以处理一系列预定义的动画,涵盖大多数场景。然后,开发人员可以创建实现这些动画的 JavaScript 库。该库可以以透明的方式提供两种实现:动态即时生成 CSS3 或为旧版浏览器提供回退。
深入研究
- David Catuhe 关于 CSS3 过渡的文章:CSS3 过渡简介
- CSS3 动画规范:http://www.w3.org/TR/css3-animations/
- IE 上的 CSS3 动画测试驱动:http://ie.microsoft.com/testdrive/Graphics/hands-on-css3/hands-on_animations.htm
- 其他有用帖子
- CSS3 动画规范中的事件相对有限。Joe Lambert 提出了一种触发每个关键帧事件的有趣解决方案:CSS 动画关键帧事件(JavaScript 解决方案)