Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[译] [802] [#43] 逐帧动画 #22

Closed
cssmagic opened this issue Dec 21, 2015 · 9 comments
Closed

[译] [802] [#43] 逐帧动画 #22

cssmagic opened this issue Dec 21, 2015 · 9 comments

Comments

@cssmagic
Copy link
Owner


本文是早期译版,未经校审。仅供参考。


Frame-by-frame animations

逐帧动画

Prerequisites

  • Basic CSS animations
  • the {#secret-xxx}

背景知识

  • 基本的 CSS 动画
  • “{$secret$}” 攻略(第 {$page$} 页)

The problem

难题

Quite often, we need an animation that is difficult or impossible to achieve by transitioning CSS properties on elements. For example, a cartoon moving or a complex progress indicator. In this case, image-based frame-by-frame animations are perfect, but surprisingly challenging to accomplish on the Web in a flexible manner.

在很多时候,我们需要一个动画,但这个动画又很难(或不可能)只通过某些 CSS 属性的过渡来实现。比如说,一段卡通影片,或是一个复杂的进度指示框。在这种场景下,基于图片的逐帧动画才是完美选择,但是,想在网页中以一种灵活的方式来实现这种动画,可谓是一项惊人的挑战啊。

At this point, you might be wondering, “Can’t we just use animated GIFs?” The answer is yes, for many cases, animated GIFs are perfect. However, they have a few shortcomings that might be a deal-breaker for certain use cases:

看到这里,你可能会产生这样一种疑问:“难道我们用 GIF 动画不行吗?” 对大多数情况来说,答案是 “行”,GIF 动画可以完美胜任。但是,GIF 动画也有一些短板,在某些场景下可能会让整体效果大打折扣:

  • They are limited to a 256 color palette, shared across all frames.
  • GIF 图片的所能使用的颜色数量被限制在 256 色
  • They cannot have alpha transparency, which can be a big problem when we don’t know what will be underneath our animated GIF. For example, this is very common with progress indicators (see Figure XX.XX).
  • GIF 不具备 Alpha 透明的特性。当我们不确定 GIF 动画的下层是什么东西时,这往往就是一个大问题。比如对于加载提示来说,半透明效果是十分常见的(参见 图 8.13)。
  • There is no way to modify certain aspects from within CSS, such as duration, repetitions, pausing, and so on. Once the GIF is generated, everything is baked into the file and can only be changed by using an image editor and generating another file. This is great for portability, but not for experimentation.
  • 我们无法在 CSS 层面修改动画的某些参数,比如动画的持续时间、循环次数、是否暂停等等。一旦 GIF 动画生成之后,上述所有参数就固定在文件内部了;如果想作修改,就只能动用图像处理软件去重新生成一个 GIF 文件。从内聚性的角度来看,这种特性确实不错,但对调试过程来说就相当不便
图 8.13

FIGURE

A semi-transparent progress indicator (on dabblet.com); this is impossible to achieve with animated GIFs

半透明的加载提示(来自 dabblet.com 网站);这个效果用 GIF 动画是无法实现的。

Back in 2004, there was an effort by Mozilla to address the first two issues by allowing frame-by-frame animation in PNG files, akin to the way we can have both static and animated GIF files. It was called APNG and was designed to be backward compatible with non-supporting PNG viewers, by encoding the first frame in the same way as traditional PNG files, so old viewers would at least display that. Promising as it was, APNG never got enough traction and to this day, has very limited browser and image editor support.

在 2004 年的时候,Mozilla 发起了一个建议:在 PNG 格式中增加对逐帧动画的支持,就像 GIF 格式同时支持静态图像和动画一样。这种格式被称作 “APNG”,它在设计时就考虑到了如何向后兼容——它会把动画的第一帧画面以传统 PNG 文件的方式进行编码,因此,对于那些不支持 APNG 特性的旧版看图软件来说,至少可以把第一帧正常显示出来。尽管它看起来很有前途,但 APNG 并没有获得足够的推广,时至今日,只有极少数的浏览器和图像处理软件支持这种格式。

{原书注释!}

For more information about APNG, see wikipedia.org/wiki/APNG.

关于 APNG 的更多信息,请参阅 wikipedia.org/wiki/APNG

Developers have even used JavaScript to achieve flexible frame-by-frame animations in the browser, by using an image sprite and animating its background-position with JS. You can even find small libraries to facilitate this! Is there a straightforward way to achieve this with only nice, readable CSS code?

后来,网页开发者们甚至会动用 JavaScript 来在浏览器中实现灵活的逐帧动画,比如用一张拼合图片(image sprite)作为背景,然后用 JS 去动态地控制它的 background-position。你甚至还可以找到一些专门为此设计的小型类库!我们有没有可能只借助清爽易读的 CSS 代码就把这个需求给搞定呢?

The solution

解决方案

Let’s assume we have all frames of our animation in a PNG sprite like the one shown in Figure XX.XX.

假设我们已经把动画中的所有帧全部拼合到一张 PNG 图片中了,如 图 8.14 所示。

图 8.14

FIGURE

Our spinner’s eight frames (dimensions: 800×100)

旋转菊花所需要的八帧画面已经合并到一起了(图片尺寸为 800×100)。

We also have an element that will hold the loader (don’t forget to include some text, for accessibility!), to which we have already applied the dimensions of a single frame:

然后我们用一个元素来容纳这个加载提示(别忘了在里面写一些文字,来确保可访问性!),并把它的宽高设置为单帧的尺寸:

<div class="loader">Loading...</div>
.loader {
    width: 100px; height: 100px;

    background: url(img/loader.png) 0 0;

    /* Hide text */
    /* 把文本隐藏起来 */
    text-indent: 200%;
    white-space: nowrap;
    overflow: hidden;
}
图 8.15

FIGURE

The first frame of our loader shows, but there is no animation yet

加载提示的第一帧显示出来了,但还没有动画效果。

Currently, the result looks like Figure XX.XX: the first frame is displayed, but there is no animation. However, if we play with different background-position values, we will notice that -100px 0 gives us the second frame, -200px 0 gives us the third frame, and so on. Our first thought could be to apply an animation like this:

目前它的效果就是 图 8.15 中的这个样子了:第一帧显示出来了,但还没有动画效果。但是,如果我们尝试对它应用各种不同的 background-position 值,就会发现 -100px 0 会让它显示出第二帧,-200px 0 会得到第三帧,以此类推。于是我们的第一反应就是用下面的方法来让它动起来:

@keyframes loader {
    to { background-position: -800px 0; }
}

.loader {
    width: 100px; height: 100px;
    background: url(img/loader.png) 0 0;
    animation: loader 1s infinite linear;

    /* Hide text */
    /* 把文本隐藏起来 */
    text-indent: 200%;
    white-space: nowrap;
    overflow: hidden;
}

However, as you can see in the following stills (taken every 167ms), this doesn’t really work:

但是,在下面这几幅静态截图(动画每经历 167ms 时的情形)中,你会发现这样其实是行不通的:

图 8.16

FIGURE

Our initial attempt for a frame-by-frame animation failed, as we did not need interpolation between keyframes

我们为了实现逐帧动画所做出的首次尝试失败了,因为我们并不需要帧与帧之间的过渡状态。

It might seem like we’re going nowhere, but we are actually very close to the solution. The secret here is to use the steps() timing function, instead of a Bézier-based one.

看起来似乎我们走错了路,但其实我们离真正的答案已经相当接近了。这里的秘诀在于,采用 steps() 这个调速函数,而不是基于贝塞尔曲线的调速函数。

“The what timing function?!” you might ask. As we saw in the previous chapter, all Bézier-based timing functions interpolate between keyframes to give us smooth transitions. This is great; usually, smooth transitions are exactly the reason we are using CSS transitions or animations. However, in this case, this smoothness is destroying our sprite animation.

“采用哪个调速函数?!” 你可能会这样问。就像我们在前一节所看到的,所有基于贝塞尔曲线的调速函数都会在关键帧之间进行插值运算,从而产生平滑的过渡效果。这个特性很棒,因为在通常情况下,平滑的过渡确实是我们使用 CSS 过渡和动画的原因。但在眼前的这个场景下,这种平滑特性恰恰毁掉了我们想实现的逐帧动画效果

Very unlike Bézier timing functions, steps() divides the whole animation in frames by the number of steps you specify and abruptly switches between them with no interpolation. Usually this kind of abruptness is undesirable, so steps() is not talked about much. As far as CSS timing functions go, Bézier-based ones are the popular kids that get invited to all the parties and steps() is the ugly duckling that nobody wants to have lunch with, sadly. However, in this case, it’s exactly what we need. Once we convert our animation to the following, our loader suddenly starts working the way we wanted it to:

跟贝塞尔曲线调速函数迥然不同的是,steps() 会根据你指定的步进数量,把整个动画切分为多帧,而且整个动画会在帧与帧之间硬切,而不会做任何插值处理。通常这种硬切效果是我们极力避免的,因此我们很少听到关于 steps() 的讨论。在 CSS 调速函数的世界里,贝塞尔曲线家的孩子就像是处处受众人追捧的校花校草,而 steps() 则是旁人避之唯恐不及的丑小鸭。不过,在我们这个案例中,后者却是我们通向成功的关键。一旦我们把整个动画的代码修改为下面的形式,这个加载提示就瞬间变成我们想要的样子了:

animation: loader 1s infinite steps(8);
图 8.17

FIGURE

A comparison of steps(8), linear and the default timing function, ease

对比 steps(8)linear 以及默认的 ease 这三种调整函数的差异。

Keep in mind that steps() also accepts an optional second parameter, start or end (default) that specifies when the switch happens on every interval (see Figure XX.XX for the default behavior of end), but that is rarely needed. If we only need a single step, there are also shortcuts: step-start and step-end, which are equivalent to steps(1, start) and steps(1, end), respectively.

别忘了 steps() 还接受可选的第二个参数,其值可以是 startend(默认值)。这个参数用于指定动画在每个循环周期的什么位置发生帧的切换动作(关于默认值 end 的行为,参见 图 8.17),但实际上这个参数用得并不多。如果我们只需要一个单步切换效果,还可以使用 step-startstep-end 这样的简写属性,它们分别等同于 steps(1, start)steps(1, end)

{试一试} play.csssecrets.io/frame-by-frame


{致敬}

Hat tip to Simurai for coming up with this useful technique in Sprite sheet animation with steps().

Simurai 脱帽致敬,感谢他在《用 steps() 实现拼合图片的动画效果》这篇文章中提出了这个实用的技巧。


Related specs

相关规范

@YIXUNFE
Copy link

YIXUNFE commented Dec 22, 2015

原来还有 steps 方法,涨姿势了。

@Bruno-Ye
Copy link

感觉易读性不太好,所以我都不喜欢看译本(当然原著也看不懂)

@cssmagic
Copy link
Owner Author

@Bruno-Ye
谢谢反馈,能具体举些例子吗? 😃

@crafteverywhere
Copy link

第一段的 a cartoon moving 翻译成 卡通的运动(或者意译成 卡通动画) 似乎更好: moving 这里不是指影片.
另外译文有一点点略显啰嗦, 大概缘于长句拆成短句后的副作用.

Quite often, we need an animation that is difficult or impossible to achieve by transitioning CSS properties on elements. For example, a cartoon moving or a complex progress indicator. In this case, image-based frame-by-frame animations are perfect, but surprisingly challenging to accomplish on the Web in a flexible manner.
我们经常会用到一些通过过渡元素的 css 属性难以或无法实现的动画. 例如, 某张卡通的运动或某个复杂的进度指示器. 遇到这种情况, 基于图片的逐帧动画表现完美, 但是, 将其以灵活的方式运用到网页中, 可谓一项惊人的挑战.

@cssmagic
Copy link
Owner Author

@crafteverywhere
我原以为 “moving” 是 “movie” 的笔误。我后来跟原作者确认过,并非笔误。这里我会在最终译稿中修正的,谢谢提醒。

@cssmagic
Copy link
Owner Author

@crafteverywhere
关于译文啰嗦的问题,我原以为这是我的缺点。但后来我想通了,作为技术书的中文译者,我不应该刻意追求译文的简洁。我的首要目标应该是原意的精确传达,同时尽量做到易读,至于语言简洁性什么的根本不是我的重点。是的,简洁不一定易读。

举个例子吧(虽然我其实是不太方便评论的),台湾版译文的风格是偏简洁的,但我自己的体验是,有些句子确实很简练,读起来也很顺,像在读母语,但我就是看不懂它想说的是什么。我翻出原文,发现原文其实说得很清楚。

我的行文风格是偏口语化的,这可能跟我长期以来翻译影视台词有关系吧,我一直努力把译文写成一句句可能从普通中国人嘴里说出来的话。我自己是喜欢的,同时我浅薄地认为这样更易读,读者在文字层面发生思考或回溯的频率也可能会因此少一些。总之这是我的取舍,大家多包涵吧。

最后需要提到的是,上述的这些风格在审校阶段磨平了不少(编辑老师对译稿的干预比我想像得要多得多),大家拿到实物再看吧。 😃

@cssmagic
Copy link
Owner Author

我原以为 “moving” 是 “movie” 的笔误。我后来跟原作者确认过,并非笔误。

尽管如此,我最终还是决定把这里译成 “一段卡通影片”。翻译成 “一个移动的卡通” 没有意义,无法理解。我认为 “某张卡通的运动” 同样无法表达出正确的意思。

请原谅我的任性,但我真的认为那样更合适。

@hax
Copy link

hax commented Feb 29, 2016

@cssmagic 卡通在英文里应是指漫画或动画,因此 cartoon moving 只是强调是“动”的卡通。翻译的困难大概是 animation 跟 cartoon 在中文里都可翻译为“动画”【前者偏技术(动的画)后者偏内容】。而这里的上下文已经用了 animation。“影片”的翻法也不是不可以,只是可能让人以为是那种非常正式的动画电影。我个人建议翻译为“卡通动画”。

更新:我刚翻了下台湾版,是译为“一个卡通或复杂的进度指示”。(也就是默认“卡通”在中文里,或在此上下文中,已经是指动画,而不是不动的漫画,所以不需要再其他词额外强调了。我觉得也是一法)

@cssmagic
Copy link
Owner Author

“影片”的翻法也不是不可以,只是可能让人以为是那种非常正式的动画电影。

即使真的有读者对这个词产生误解,他得到的结果也是正确的。CSS 中也只有逐帧动画能实现动画电影。 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants