您的位置:首页 > Web前端 > CSS

CSS Secret——Transitions & Animations

2016-07-08 16:21 441 查看

弹性动画

在真实世界中,很少有物体是线性的从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;
}


但是这里使用时有可能会有一点卡。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  css 动画