阅读 338

css揭秘 - 过渡与动画(二)

这是我参与更文挑战的第16天,活动详情查看: 更文挑战

闪烁效果

难题

有一种常见的用户体验设计手法,就是通过数次闪烁(不超过三次)来提示用户界面中有某处发生了变化,或者用来凸显出当前链接的目标(如果页面中某元素的 ID 与 URL 中的 #hash 相匹配,则它就是链接的目标)。在此类场景下使用闪烁,可以有效地把用户的注意力引导到某个特定区域。

解决方案

用 CSS 动画可以实现各种类型的闪烁效果,比如对整个元素进行闪烁(通过 opacity 属性),对文字的颜色进行闪烁(通过 color 属性),对边框进行闪烁(通过 border-color 属性)等等。只讨论文字的闪烁效果,因为这是最常见的需求。不过,同样适用于元素其他部分的闪烁效果。

一个平滑的闪烁效果。

@keyframes blink-smooth {
  to {
    color: transparent
  }
}

.highlight {
  animation: 1s blink-smooth 3;
}
复制代码

227.gif 这基本上成功了。这段文字可以平滑地从它原来的颜色淡化为透明色,但随后会生硬地跳回原来的颜色。这可能正是我们预先设想的效果。如果是这样的话,那就大功告成了!但如果我们希望文字颜色的变化不仅是平滑隐去的,同时还是平滑显现的。那么,可以修改关键帧,让状态切换发生在每个循环周期的中间。

@keyframes blink-smooth { 50% { color: transparent } }
.highlight {
  margin: 30px;
  animation: 1s blink-smooth 3;
}
复制代码

228.gif 还有一个问题,虽然在这个特定的动画中表现得不是很明显(因为颜色或透明度的过渡很难体现出各种调速函数的特征),但:这个动画一直是处在加速过程中的,不论是在文字显现还是隐去时——这对某些动画来说可能会显得不太自然(比如类似脉搏跳动的动画)。在那种情况下,可以从工具箱中请出另一件法宝: animation-direction 。

animation-direction 的唯一作用就是反转每一个循环周期( reverse ),或第偶数个循环周期( alternate ),或第奇数个循环周期( alternate-reverse )。它的伟大之处在于,它会同时反转调整函数,从而产生更加逼真的动画效果。我们可以把它用在需要闪烁的元素上,比如:

@keyframes blink-smooth { to { color: transparent } }
.highlight {
  animation: .5s blink-smooth 6 alternate;
}
复制代码

229.gif 那么,最普通的那种闪烁效果可以使用step来实现。

@keyframes blink {
  50% {
    color: transparent
  }
}

.highlight {
  animation: 1s blink 3 steps(1); /* 或用step-end */
}
复制代码

2210.gif

打字动画

难题

有些时候,我们希望一段文本中的字符逐个显现,模拟出一种打字的效果。这个效果在技术类网站中尤为流行,用等宽字体可以营造出一种终端命令行的感觉。如果使用得当,它确实可以让整个网页的设计感提升一个档次。

解决方案

核心思路就是:让容器的宽度称为动画的主体,把所有文字包裹在这个容器中,然后让它的宽度从 0 开始以步进动画的方式一个字一个字地扩张到它应有的宽度。但是,这个方法是有局限性的,它并不适应于多行文本。然而幸运的是,这种情况通常需要应用在类似标题的但行文本上。另外,要注意:动画的持续时间越长,动画效果越差。

把动画应用在 h1 上,代码结构为:

<h1>这是一个要实现打字动画的标题</h1>
添加动画,让它的宽度从 0 变化到完整的宽度。

@keyframes typing {
  from { width: 0 }
}
h1 {
  width: 14em;
  animation: typing 8s;
}
复制代码

然而,我们看到了这个效果跟想要的打字效果一点关系都没有。首先,忘记用 white-space:nowrap; 来阻止文本折行,因此文本的行数会随着宽度的扩张不断变化。其实没有加 overflow:hidden; 所以超出宽度的文本没有被裁切掉。修改了这两个问题后,效果如下:

2211.gif

修复了上述两个小问题后,发现真正的大问题才出来:

  • 最明显的是整个动画是平滑连贯的,而不是逐字显现的。
  • 另外,h1 的宽度怎么计算。

用 steps() 来修复第一个问题,就像“逐帧动画”和“闪烁效果”中的那样,所需要的步进数量是由字符的数量来决定的,这显然是很难维护的,而且对于动态文字来说更难。h1 的宽度取做字符数,因为用的是汉字,因此单位是 em

根据上述的修改一下:

@keyframes typing {
  from { width: 0; }
}
h1 {
  width: 14em; /* 文本的宽度 */
  overflow: hidden;
  white-space: nowrap;
  animation: typing 6s steps(15);
}
复制代码

2213.gif 看起来还不够逼真,画龙点睛的一步是给它加上一个闪烁的光标,可以使用伪元素来生成光标,用 opacity 实现闪烁效果;也可以用右边框来模拟光标效果,可以把有限的伪元素资源节省下来留作他用:

@keyframes typing {
  from { width: 0 }
}
@keyframes caret {
  50% { border-color: transparent; }
}
h1 {
  width: 14em; /* 文本的宽度 */
  overflow: hidden;
  white-space: nowrap;
  border-right: .05em solid;
  animation: typing 6s steps(15),
    caret 1s steps(1) infinite;
}
复制代码

2214.gif

状态平滑的动画

难题

不是所有的动画都是在页面一加载好就立即播放的。更常见的是:通过动画来响应用户的动作,比如用户的鼠标悬停在某个元素上(:hover),或者按住鼠标键(:enter)等。在这种场景下,就无法控制动画实际的循环次数。举例来说:用户的鼠标可能会触发一个华丽的 :hover 动画,而在动画播放还没结束的时候,鼠标就从元素上移走了,在这种情况下,动画会立即停止播放,并且生硬的跳回开始状态。这是一种生硬的用户体验。

解决方案

假设有一张非常长的宽幅照片,但是只有一个 150×150 的区域显示它。设计一个动画展示:默认情况下显示这张照片的左边缘,当用户跟它交互的时候,让它滚动并漏出剩余的部分。使用一个元素显示这张照片。

.panoramic {
  width: 150px;
  height: 150px;
  background: url("22.jpg");
  background-size: auto 100%;
}
复制代码

给它加上动画试试,将 background-position 属性值从原本的 0 0 一直变化到 100% 0 时,这张图片就会展现从左侧一直滚动到右侧的完整过程。

@keyframes panoramic {
  to {
    background-position: 100% 0;
  }
}

.panoramic {
  width: 150px;
  height: 150px;
  background: url("22.jpg");
  background-size: auto 100%;
  animation: panoramic 10s linear infinite alternate;
}
复制代码

2215.gif

这个动画是在页面加载后就立即触发的,修改成用户鼠标悬停时才开始播放。

.panoramic:hover, .panoramic:focus {
  animation: panoramic 10s linear infinite alternate;
}
复制代码

2216.gif 但是,当用户鼠标离开后,就会看到图片会生硬的立即回到最左侧。为了解决这个问题,需要换个角度来思考问题。可能需要的并不是在 :hover 的时候应用一个动画,而是在失去 :hover 状态时暂停动画。可以使用 animation-play-state 这个属性。

需要把动画加在 .panoramic 这条样式中,但是让它一开始就处于暂停状态,直到 :hover 时再启动动画。这再也不是添加和取消动画的问题了,而只是暂停和继续一个一直存在的动画,因此再也不会有生硬的跳回现象了。

@keyframes panoramic {
  to {
    background-position: 100% 0;
  }
}

.panoramic {
  width: 150px;
  height: 150px;
  background: url("22.jpg");
  background-size: auto 100%;
  animation: panoramic 10s linear infinite alternate;
  animation-play-state: paused;
}

.panoramic:hover,
.panoramic:focus {
  animation-play-state: running;
}
复制代码

2217.gif

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。

文章分类
前端
文章标签