LinXi +

CSS3动画详解

CSS3动画

有人认为CSS动画是做了js的事情,较真起来也算,只是已经抢占许多年了,早些年要实现鼠标滑过链接变色的基本效果,需要动用Java Applet,后来只需给HTML元素加事件onclick=changecolor(),再之后正如你所知,只要写:hover:focus这样的伪类即可,同样的,现在有了CSS3动画。

CSS3动画的优势:

劣势:

可以做动画效果的属性

理论上来说,任何单独的CSS属性都可以做动画的效果,比如:

你也可以给redblue这样的赋值的颜色属性加transition或animation,它会被自动转化为对应的RGB值。

不可以做动画效果的属性

看下面这些例子:

#container p {
    display: none;
    transition: all 3s ease;
}

#container:hover p {
    display: block;
}

/**********************/

#container p {
    height: 0px;
    transition: all 3s ease;
}

#container:hover p {
    height: auto;
}

属性从无到有或到不确定值,动画效果不会生效,因为浏览器不知道如何去做,对于元素从无到有,你可以选择opacity属性来处理。

CSS3 Transition

Transition是被用到最多的也是最简单的CSS3动画类型。如果要做一个10px宽的蓝色元素在3s后变成一个100px宽的红色元素的效果,Transition可以平滑实现,你只需要声明起始和终止这两个状态。

Transition的触发也很简单,可以用:hover:focus这样的伪类来触发,也可以通过改变元素的样式来触发。

transition的属性

transition-property

transition-property用来声明transition会被应用到的属性。

#container p.one {
    transition-property: color;
}

#container p.two {
    transition-property: width;
}

#container p.three {
    transition-property: color, width;
}

如果你想应用到所有属性,那可以简单写作all,也可以通过none来关闭transition。

transition-duration

transition-duration用来声明动画持续的时长,可以是s也可以是ms

#container p.one {
    transition-duration: 3s;
}

#container p.two {
    transition-duration: 3000ms;
}

transition-timing-function

transition-timing-function声明了动画的缓动类型,有下面几个选项:

最后,还有cubic-bezier函数,可以自己创造更多更优美的缓动类型。

transition-delay

transition-delay声明了动画延迟开始的时间,很容易理解

#container p.one {
    transition-delay: 0.5s;
}

#container p.two {
    transition-delay: 500ms;
}

transition简写

上面介绍了transition的属性,他们也可以合并成一项,省去了许多拼写,当然也别忘记浏览器前缀:

#container p {
    transition-property: all;
    transition-duration: 3s;
    transition-timing-function: ease-in-out;
    transition-delay: 0.5s;
}


#element {
    /* starting state styles */
    color: #F00;
    -webkit-transition: all 3s ease-in-out 0.5s;
    transition: all 3s ease-in-out 0.5s;
}

#element:hover {
    /* ending state styles */
    color: #00F;
}

transition的高级用法

不同的transition效果

看这样的例子:

p#animate {
    color: #ff6;
    transition: all 3s ease-in-out 0.5s;
}

p#animate:hover {
    color: #0f0;
    transform: scale(4);
}

在这个例子中,当鼠标hover,元素在0.5s之后在3s内放大四倍,鼠标移开,需要同样的时间回到原来的状态。如果想要不同的效果,可以这样写:

p#animate {
    color: #ff6;
    transition: all 0.5s ease-in-out;
}

p#animate:hover {
    color: #0f0;
    transform: scale(4);
    transition: all 3s ease-in-out 0.5s;
}

多个transition

需要给多个transition指定不同的效果时,all属性解决不了,可以这样写:

p#animate {
    width: 10em;
    background-color: #F00;
    border-radius: 5px;
    transition-property: width, border-radius, background-color;
    transition-duration: 1s, 2s;
    transition-timing-function:  ease, ease-out, linear;
}

p#animate:hover {
    width: 20em;
    background-color: #00F;
    border-radius: 50%;
}

注意其中的transition-duration只写了两个,那么第三个transition-property属性background-color就用循环到第一个,也就是说他的transition-duration值是1s

transition示例

#transition1 {
    width:350px;
    background-color:#1abc9c;
    transition-propety:width,background-color;
    transition-duration:.5s, 1s;
}
#transition1:hover {
    width:450px;
    background-color:#8e44ad;
    transition-duration:.5s, 3s;
}

CSS3 Animation

Animation和Transition的不同

定义keyframes

@keyframes colorchange {
    0%   { background-color: #00F; /* from: blue */ }
    25%  { background-color: #F00; /* red        */ }
    50%  { background-color: #0F0; /* green      */ }
    75%  { background-color: #F0F; /* purple     */ }
    100% { background-color: #00F; /* to: blue   */ }
}

@-webkit-keyframes colorchange {
    0%   { background-color: #00F; /* from: blue */ }
    25%  { background-color: #F00; /* red        */ }
    50%  { background-color: #0F0; /* green      */ }
    75%  { background-color: #F0F; /* purple     */ }
    100% { background-color: #00F; /* to: blue   */ }
}

在这个例子中,只是定义了background-color这一个属性,如有需要,可以换做其他。对于0%这个也可以用from关键字来替代,同样的可以用to来代替100%,过渡状态,你可以定义任何百分比,类似12.5%这样的也可以,不过就不用给自己找麻烦了吧。浏览器的prefix也不能少。

应用到元素

animation应用到元素的属性写法,和transition差不太多,顺序都一致,就不在一个个参数重复说明,直接看代码吧:

#myelement {
    animation-name: colorchange; /**这里引用了前面定义的动画**/
    animation-duration: 5s;
    animation-timing-function: linear;
    animation-delay: 1s;
    animation-iteration-count: infinite;
    animation-direction: alternate;
}

/****简写****/
#myelement {
    -webkit-animation: colorchange 5s linear 1s infinite alternate;
    animation: colorchange 5s linear 1s infinite alternate;
}

animation-iteration-count用来指定动画循环的次数,无限循环用infinite

animation-direction有四个值:

Animation示例

Animate color

CSS3 Transform

有了transitionanimation之后,就可以做出些漂亮的动画效果,如果再搭配transform这一CSS3动画利器,就更出彩了。

CSS3 2D Transform

运用CSS3 2D Transform的技术,可以更自由轻松的来修饰HTML元素。CSS3 2D Transform的基本方法有下面这些:

Translate

使用translate()方法,可以将HTML元素在x-y轴平面上做位移,且不会影响到其他元素。

div{
    -webkit-transform: translate(20px,20px);
    -moz-transform: translate(20px,20px);
    -o-transform: translate(20px,20px);
    transform: translate(20px,20px);
}

效果如下:

Normal Div
transform:tranlated(40px, 40px)

Rotate

rotate()方法可以将元素按照时钟方向旋转,参数可以是0deg360deg,也是在x-y轴平面,示例如下:

Normal Div
transform:rotate(-30deg)

Scale

和名字的一样,scale()方法用来放大一个元素,依然是在x-y轴平面,看示例:

Normal Div
transform:scale(1.5,1.3)

Skew

skew()方法可以将元素按照指定参数进行扭曲,你需要指定x、y轴的扭曲角度,看示例:

Normal Div
transform:skew(30deg,0)

Matrix

matrix()方法是以上所有2D效果的方法的总和,写法如下:

div{
    transform: matrix(a,b,c,d,tx,ty);
}

本质上scaleskewrotatetranslate的效果都是通过matrix实现的,txty表示位移量,关于matrix方法更详细的介绍可以参考这里:理解CSS3 transform中的Matrix(矩阵)

CSS3 3D Transform

了解了2D Transform之后,3D Transform的概念也不会太难,他给HTML元素在x-y平面加上了z轴,我们一个个来看看:

来看看例子:

width:100%;
height:100%;
transform: translateZ(-200px);
width:100%;
height:100%;
transform: translateZ(100px);
width:100%;
height:100%;
transform: rotateX(45deg);
width:100%;
height:100%;
transform: rotateY(45deg);
width:100%;
height:100%;
transform: rotateZ(45deg);

任何有3D变换的元素,不论最后只是做了2D的变换,或者什么都没做translate3d(0,0,0),都会触发浏览器去计算。不过,以后会更新优化也不一定。

Perspective

激活元素的3D空间,需要perspective属性,写法有两种:

transform: perspective( 600px );
/**或者**/
perspective: 600px;

这两种不同写法,当应用元素只有一个时候,并没有区别,当有多个元素的时候,我们看看效果:

#pers-red .item{
  background: red;
  transform: perspective( 400px ) rotateY(45deg);
}
 
 
 
 
 
 
 
 
 
#pers-blue {
  perspective: 400px;
}

#pers-blue .item{
  background: blue;
  transform: rotateY( 45deg );
}
 
 
 
 
 
 
 
 
 

上面这两种写法,都触发了元素的3D行为,函数型的写法transform:perspective(400px)适用于单个元素,会对每一个元素做3D视图的变换,而perspective:400px的写法,需写在父元素上,然后以父元素的视角,对多个子元素进行3D变换,多个子元素共享同一个3D空间,可以自己打开console修改感受一下。

perspective的参数值,决定了3D效果的强烈程度,可以想象为距离多远去观察元素。值越大,观察距离就越远,同样的旋转值,看起来效果就弱一些;值越小,距离越近,3D效果就更强烈。

perspective-orgin

通常,对一个元素进行3D变换的时候,变换点都是元素的中心点,如果你想以其他的位置为变换点,那就可以用这个属性来做调整:

perspective-orgin: 20% 70%;

这个是默认值的perspective-orign:50% 50%

Nature Nature Nature

这个是perspective-orgin: 0% 50%;

Nature Nature Nature

transform-style

这个参数用来共享父元素的3D空间,这样说起来有些抽象,下面第一个翻卡片的例子中会讲到。

backface-visibility

backface-visibility 属性可用于隐藏内容的背面。默认情况下,背面可见,这意味着即使在翻转后,变换的内容仍然可见。但当 backface-visibility 设置为 hidden 时,旋转后内容将隐藏,因为旋转后正面将不再可见。该功能可帮助你模拟多面的对象,例如下例中使用的卡片。通过将 backface-visibility 设置为 hidden,可以确保只有正面可见。

CSS3 动画实例

下面例子中的代码,为了方便查看都没有写浏览器前缀,也没有加入其他的修饰属性,所以实际应用时,不要忘记哦,当然也可以直接console查看。

CSS3 翻纸牌

做一个翻纸牌的效果,结构很简单:

<div id="cardflip">
  <div id="card1">
    <div class="front">1</div>
    <div class="back">2</div>
  </div>
</div>

.cardflip是整个3D效果的容器,#card1是翻转效果的元素,.front.back是翻转的两面。添加样式:

#cardflip {
    width: 200px;
    height: 260px;
    position: relative;
    perspective: 800px;
}

#card1 {
    width: 100%;
    height: 100%;
    position: absolute;
    transform-style: preserve-3d;
    transition: transform 1s;
}

首先给#cardflip添加perspective属性,这样才能触发3D变换,之后#card1就在父元素的3D空间中了,用了absolute来定位子元素,设置宽高都是100%,这样就可以让transform-origin在元素的中心点,这个后面再讨论。

transform-style有两个值,一个是默认的flat一个是preserve-3d,由于perspective的3D空间,只能作用于直接的子元素,那么.front.back也需要#cardflip的3D空间的话,就需要给#card1添加这个属性,

#card1 div{
    position: absolute;
    width: 100%;
    height: 100%;
    backface-visibility: hidden;
}

只有#card1 div元素共享了外层元素的3D空间之后,3D变换的属性才能生效,这时候的backface-visibility才有效,设置为hidden

#card1 .front {
    background: red;
}

#card1 .back {
    background: blue;
    transform: rotateY( 180deg );
}

#card1.flipped {
    transform: rotateY( 180deg );
}

因为设置了backface-visibility,而.back默认就是以Y轴旋转了180度,空间想象一下,.back就转到背面去了,所以hidden属性生效,就看不到.back了。

#card1添加了.flipped的样式,#card1以Y轴旋转了180度,这时候.front转到了背面,而.back从背面转到了前面,所以就完成了切换。这一段需要仔细的想一想。好了,看看下面的实例,点击即可翻转:

1
2

我们再给这个翻转加一些偏移的效果,看起来会不那么生硬。这就用到了transform-origin,这个参数:

#card1 { transform-origin: left center; }

#card1.flipped {
  transform: translateX( 100% ) rotateY( 180deg );
}

默认的transform-origincenter center,我们改成left center之后,就不再以元素的x方向的中心为轴旋转,而是以元素的左边为Y轴旋转,所以还需要给整个#card1加一个位移量translate,值是100%,就是元素本身的宽度。

可以在console里面去掉#card1.flippedtranslate帮助理解。

1
2

这里有一点需要注意,当元素在z轴上有了位移,或者朝向负角度旋转,会导致元素在页面上无法被鼠标点击到,想像一下3D空间,这个元素已经位于整个页面平面的里面,所以无法触及了。

CSS3 立方体

做完了反转卡片的效果,肯定还想做更炫的,来试试做一个立方体吧:

<section id="cube-con">
  <div id="cube">
    <figure class="front">1</figure>
    <figure class="back">2</figure>
    <figure class="right">3</figure>
    <figure class="left">4</figure>
    <figure class="top">5</figure>
    <figure class="bottom">6</figure>
  </div>
</section>

#cube-con {
    width: 200px;
    height: 200px;
    position: relative;
    perspective: 1000px;
}

#cube {
    width: 100%;
    height: 100%;
    position: absolute;
    transform-style: preserve-3d;
}

#cube figure {
    width: 196px;
    height: 196px;
    display: block;
    position: absolute;
    border: 2px solid black;
}

这一部分和上一个例子没有太大的差别,应该都能理解每一个属性的含义了。

#cube .front  { transform: rotateY(   0deg ) translateZ( 100px ); }
#cube .back   { transform: rotateX( 180deg ) translateZ( 100px ); }
#cube .right  { transform: rotateY(  90deg ) translateZ( 100px ); }
#cube .left   { transform: rotateY( -90deg ) translateZ( 100px ); }
#cube .top    { transform: rotateX(  90deg ) translateZ( 100px ); }
#cube .bottom { transform: rotateX( -90deg ) translateZ( 100px ); }

立方体的每一个面,经过rotate旋转之后,就放置在了他该被放置的地方,但是这时候会发现,这些层叠加在一起,还没有成为一个立方体,这时候需要给Z轴一个位移,想象一下我们的视角点在#cube正中间,拉伸z轴之后,.rightleft等面就会有一定的角度,参考画画时候的透视,因为刚好在中心点,所以位移量就是宽度的一半。分步过程可以看这里

因为z轴拉伸之后,原来的对象会被放大一些,这样就会模糊掉,为了去掉这个影响,我们需要把立方体再推回原来的视角平面,于是:

#cube { transform: translateZ( -100px ); }

完成了立方体,想让某个面旋转到前方,只需转动整个立方体,不用去调整每个面:

#cube.show-front  { transform: translateZ( -100px ) rotateY(    0deg ); }
#cube.show-back   { transform: translateZ( -100px ) rotateX( -180deg ); }
#cube.show-right  { transform: translateZ( -100px ) rotateY(  -90deg ); }
#cube.show-left   { transform: translateZ( -100px ) rotateY(   90deg ); }
#cube.show-top    { transform: translateZ( -100px ) rotateX(  -90deg ); }
#cube.show-bottom { transform: translateZ( -100px ) rotateX(   90deg ); }

/**还有过渡效果**/
#cube { transition: transform 1s; }

1
2
3
4
5
6

3D 旋转跑马灯

做幻灯片展示的方法有很多,我们用CSS3的3D技术来试试看:

<section class="container">
  <div id="carousel">
    <figure>1</figure>
    <figure>2</figure>
    <figure>3</figure>
    <figure>4</figure>
    <figure>5</figure>
    <figure>6</figure>
    <figure>7</figure>
    <figure>8</figure>
    <figure>9</figure>
  </div>
</section>

.container {
  width: 210px;
  height: 140px;
  position: relative;
  perspective: 1000px;
}

#carousel {
  width: 100%;
  height: 100%;
  position: absolute;
  transform-style: preserve-3d;
}

#carousel figure {
  display: block;
  position: absolute;
  width: 186px;
  height: 116px;
  left: 10px;
  top: 10px;
  border: 2px solid black;
}

上面这一段,没什么特别要说明的,基本的结构样式,以及之前重点说明过的perspectivepreserve-3d。现在有9个卡片,要环绕成一圈,那么每个的角度就是40deg (360/90)。

#carousel figure:nth-child(1) { transform: rotateY(   0deg ); }
#carousel figure:nth-child(2) { transform: rotateY(  40deg ); }
#carousel figure:nth-child(3) { transform: rotateY(  80deg ); }
#carousel figure:nth-child(4) { transform: rotateY( 120deg ); }
#carousel figure:nth-child(5) { transform: rotateY( 160deg ); }
#carousel figure:nth-child(6) { transform: rotateY( 200deg ); }
#carousel figure:nth-child(7) { transform: rotateY( 240deg ); }
#carousel figure:nth-child(8) { transform: rotateY( 280deg ); }
#carousel figure:nth-child(9) { transform: rotateY( 320deg ); }

好了,和立方体的例子到同样的步骤了,现在所有的卡片做了Y轴旋转,但因为观察的视角点没有变,所以看起来还是平面,如下这样:

caro

立方体的位移很好计算,只要是宽度、高度、或者深度的一半就可以了,这个旋转的跑马灯应该怎么计算呢?

caro-cmpu

从旋转跑马灯的上方观察,每个卡片的宽度是210px,角度是40deg,要计算到中心点的距离,根据旁边的三角形可得:

r = 105 / tan(20deg) = 288px

所以:

#carousel figure:nth-child(1) {transform:rotateY(  0deg) translateZ(288px);}
#carousel figure:nth-child(2) {transform:rotateY( 40deg) translateZ(288px);}
#carousel figure:nth-child(3) {transform:rotateY( 80deg) translateZ(288px);}
#carousel figure:nth-child(4) {transform:rotateY(120deg) translateZ(288px);}
#carousel figure:nth-child(5) {transform:rotateY(160deg) translateZ(288px);}
#carousel figure:nth-child(6) {transform:rotateY(200deg) translateZ(288px);}
#carousel figure:nth-child(7) {transform:rotateY(240deg) translateZ(288px);}
#carousel figure:nth-child(8) {transform:rotateY(280deg) translateZ(288px);}
#carousel figure:nth-child(9) {transform:rotateY(320deg) translateZ(288px);}

知道了计算方法,如果要改变卡片的个数,或者宽度,只要按照那个公式再计算就好:

var tz = Math.round( ( panelSize / 2 ) / 
  Math.tan( ( ( Math.PI * 2 ) / numberOfPanels ) / 2 ) );
// or simplified to
var tz = Math.round( ( panelSize / 2 ) / 
  Math.tan( Math.PI / numberOfPanels ) );

计算好卡片的位置之后,然后旋转#carousel就可以了,当然这个要用js来控制了:

#carousel{
    transform: translateZ( -288px ) rotateY( -160deg );
}

js代码如下:

$(function(){
    $('#car-pre').click(function(){
        var deg = $('#carousel').attr('data-deg') || 0;
        deg = parseInt(deg)+40;

        var value = 'translateZ(-288px) rotateY('+deg+'deg)';

        $('#carousel')
            .attr('data-deg',deg)
            .css({
                '-webkit-transform':value
                ,'-moz-transform':value
                ,'-o-transform':value
                ,'transform':value
            });
    });
    $('#car-next').click(function(){
        var deg = $('#carousel').attr('data-deg') || 0;
        deg = parseInt(deg)-40;

        var value = 'translateZ(-288px) rotateY('+deg+'deg)';

        $('#carousel')
            .attr('data-deg',deg)
            .css({
                '-webkit-transform':value
                ,'-moz-transform':value
                ,'-o-transform':value
                ,'transform':value
            });
    });

结语

终于完成了这篇,梳理的过程对我自己很有提高,希望对你也能有些帮助,有兴趣可以关注我,期待下以后的博客~

Blog

Webgl

Project