CSS Secret——Transitions & Animations

弹性动画

在真实世界中,很少有物体是线性的从A移动到B。想让你的界面动画显得更加生动,你需要模拟弹性的动画。
从技术角度来讲,弹性动画就是在动画达到终止值时,回去一点,再达到终止值时再回去一点,这一点比上一次要小。最终停在终止值。

我们可以使用最强大的关键帧动画来做这件事。
我们使用一个点击input弹出输入提示框来做例子:

<label> Your username: <input id="username" />
    <span class="callout">Only letters, numbers,  underscores (_) and hyphens (-) allowed!</span>
</label>

其基本样式:

.callout {
  position: absolute;
  max-width: 14em;
  padding: .6em .8em;
  border-radius: .3em;
  margin: .3em 0 0 -.2em;
  background: #fed;
  border: 1px solid rgba(0,0,0,.3);
  box-shadow: .05em .2em .6em rgba(0,0,0,.2);
  font-size: 75%;
}

.callout:before {
  content: "";
  position: absolute;
  top: -.4em;
  left: 1em;
  padding: .35em;
  background: inherit;
  border: inherit;
  border-right: 0;
  border-bottom: 0;
  transform: rotate(45deg);
}

使用关键帧动画:

@keyframes elastic-grow {
  from {
    transform: scale(0);
  }
  70% {
    transform: scale(1.1);
    animation-timing-function: cubic-bezier(.1,.25,1,.25); /* Reverse ease */
  }
}
input:not(:focus) + .callout {
  transform: scale(0);
}
input:focus + .callout {
  animation: elastic-grow .5s;
}
.callout {
  transform-origin: 1.4em -.4em;
}

但是使用关键帧动画在这里有点大材小用了,我们直接使用cubic-bezier()来控制transform的执行程度就好了。

input:not(:focus) + .callout:not(:hover) {
  transform: scale(0);
}
.callout {
  transition: .5s cubic-bezier(.25,.1,.3,1.5) transform;
  transform-origin: 1.4em -.4em;
}

这里当点击input时scale会从0最终变到1,但是由于cubic-bezier()的控制,这个过程并不是单调的,在50%时scale第一次达到1,70%时为1.1,100%时再回到1。这就达到了我们刚才要的效果。
但是这样还不够,在input失去焦点的时候,这个曲线就有问题了,会使scale变为-0.1,这个是不可接受的。于是我们需要一个新的transform来覆盖掉他。不用显示声明,使用默认的ease函数就好。

input:not(:focus) + .callout:not(:hover) {
  transform: scale(0);
  transition: .25s transform;
}
.callout {
  transition: .5s cubic-bezier(.25,.1,.3,1.5) transform;
  transform-origin: 1.4em -.4em;
}

逐帧动画

有时动画比较复杂,我们不方便使用CSS来创建,而是想用图片一帧一帧的播放,有人说那还不如用gif呢。但是gif不支持透明色,颜色位数太少,不好维护等等,都是问题。
我们可以使用一个包含所有帧的PNG sprite,然后使用动画在里面切换。当然这时就不能使用连续的cubic-bezier来控制动画进程了。使用step。这个函数是间断的,可以控制分几步完成动画,这正是我们想要的。

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

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

闪烁效果

闪烁效果可以使用动画来模拟:

@keyframes blink-smooth { 50% { color: transparent } }
.highlight { animation: 1s blink-smooth 3; }

这样看起来是我们想要的结果,但是要注意的是这里字出现和消失时的时间函数是一样的,都是加速着出现或消失,在这里或许看不出大问题,但是我们还是想要其出现和消失的过程是完全对称的,包括时间函数。
这里就可以使用animation-direction,它可以翻转每一个动画的过程,或者每奇数个,或者每偶数个。
这里我们就可以翻转每偶数个,前面的尾和后面的尾接起来,原来的两个由黑变白的动画过程被合为了一个。时间函数也完全对称了。

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

打字特效

<h1 id="typeEffect">CSS is awesome!</h1>
@keyframes typing {
  from {
    width: 0
  }
}
@keyframes caret {
  50% {
    border-color: currentColor;
  }
}
#typeEffect {
  width: 15ch; /* Width of text */
  overflow: hidden;
  white-space: nowrap;
  border-right: .05em solid transparent;
  animation: typing 6s steps(15),
            caret 1s steps(1) infinite;
}

ch这个单位在大多数字体中可以代表一个字符的长度,所以我们将动画设置为通过15步将宽度从0变为15ch。并设置一个永远闪烁的右边框,模拟输入光标。

状态连续的动画

有很多动画是在用户和它所在的元素有一些交互的时候才开始的,比如用户将鼠标浮动在某个元素上面,这个元素开始应用一些动画。这就涉及到一个问题,当动画还没播完的时候,用户就结束了与该元素的交互,这时的动画该做什么处理?
根据具体的使用场景,这里有可能会有两种处理:回到初始状态,等下次交互开始时再重新播放动画,那么回到初始状态的过程我们当然希望还是一个动画的过程,要不就太突兀了;还有一种就是直接暂停动画,下次用户恢复与该元素的交互时,再从上次停下的地方开始动画。
这里要提一下animation和transition的区别:
animation用作动画时,如果在动画未完成时就停止动画的执行,动画会直接跳回初始状态。在动画结束时,会直接跳回初始状态。可以暂停动画
transition用作动画时,如果在动画未完成时就停止动画的执行,动画会反向播放回原始状态。在动画结束时,会停在结束状态。不能暂停动画。
由于存在这些区别,animation和transition天然的就分别适应上面两种情况。
可以用这两个例子试试:

@keyframes test {
  to {
    width:400px;
  }
}
#testAnimation{
  height:100px;
  width:300px;
  background-color: #0074d9;
  color: #fff;

}
#testAnimation:hover{
  animation: test 2s;
  //width:400px;
}
#testTransition{
  height:100px;
  width:300px;
  background-color: #0074d9;
  transition: 2s width;
  color: #fff;
}
#testTransition:hover{
  width:400px;
}

transition的那种情况比较简单,这里就来说说要暂停动画的那个情况。

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

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

.panoramic:hover, .panoramic:focus {
  animation-play-state: running;
}

动画用在元素本身,并暂停,在元素获得交互的时候再播放动画。

沿着环形路径的动画

我们想要一个元素沿着环形路径转圈圈。

两个元素的办法

<div class="path">
    <div class="avatar">
        <img src="img/marker_red.png"/>
    </div>
</div>

我们使用这样的HTML结构,path是一个圆形,avatar使用一个以圆为中心的rotate来旋转,当大的div沿着path旋转的时候,里面的img使用另一个小的rotate来抵消,使其一直是正的。

.path {
  width:300px;
  height:300px;
  border-radius: 50%;
  background-color: #ffbb33;
  text-align: center;
}
@keyframes spin {
  to {
    transform: rotate(1turn);
  }
}
@keyframes spin-reverse {
  from {
    transform: rotate(1turn);
  }
}
.avatar {
  animation: spin 3s infinite linear;
  transform-origin: 50% 150px; /* 150px = path radius */
  //display: inline;
}
.avatar > img {
  animation: spin-reverse 3s infinite linear;
}

这里有优化的空间,这两个动画本质上是一样的,唯一不同的就是顺序,一个是从正着转360度,一个是反着转360度。那么这时,reverse就派上用场了。

@keyframes spin {
  to {
    transform: rotate(1turn);
  }
}
.avatar {
  animation: spin 3s infinite linear;
  transform-origin: 50% 150px; /* 150px = path radius */
}
.avatar > img {
  animation: inherit;
  animation-direction: reverse;
}

一个元素的办法

之前我们使用两个元素,最主要的问题是origin只能有一个,而两个旋转的元素并不是绕着一个点旋转的。
但是我们可以绕过origin,看下面这两种变换,其实是同一种:

transform: rotate(30deg); 
transform-origin: 200px 300px;     
transform: translate(200px, 300px)
    rotate(30deg)    
    translate(-200px, -300px); 
transform-origin: 0 0;

所以你发现了么,其实origin是个语法糖。
这样的话,我们就不需要两个元素了:

<div class="path onlyEle">
   <img src="img/marker_red.png" class="avatar"/>
</div>
@keyframes spin1 {
  from {
    transform: translate(50%, 150px)
      rotate(0turn)
      translate(-50%, -150px)
      translate(50%,50%)
      rotate(1turn)
      translate(-50%,-50%)
  }
  to {
    transform: translate(50%, 150px)
      rotate(1turn)
      translate(-50%, -150px)
      translate(50%,50%)
      rotate(0turn)
      translate(-50%, -50%);
  }
}
.onlyEle .avatar {
  animation: spin1 3s infinite linear;
}

但是这里使用时有可能会有一点卡。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值