Angular学习笔记(14)—动画

在AngularJS应用中创建动画,有三种途径:

  • 使用CSS3动画
  • 使用JavaScript动画
  • 使用CSS3过渡

安装

$ bower install --save angular-animate
//引用库
<script src="js/vendor/angular.js"></script>
<script src="js/vendor/angular-animate.js"></script>
//引用模块
angular.module('myApp', ['ngAnimate']);

它是如何运作的

$animate服务默认给动画元素的每个动画事件添加了两个CSS类。$animate服务支持多个内置的指令,它们无需额外的配置即可支持动画。我们可以为自己的指令创建动画。
所有这些预先存在的支持动画的指令,都是通过监控指令上的事件实现的。例如,当一个新的ngView进入并且把新内容带进浏览器时,这个事件就叫做ngViewenter事件。当ngHide准备显示一个元素的时候,remove事件就会触发。
下面是指令以及在不同状态触发的事件列表。


$animate服务基于指令发出的事件来添加特定的样式类。对于结构性的动画(比如进入、移动和离开),添加上去的CSS类是ng-[EVENT]ng-[EVENT]-active这样的形式。
对于基于样式类的动画(比如ngClass),动画样式类的形式是[CLASS]-add[CLASS]-addactdive[CLASS]-remove[CLASS]-remove-active
最后,对于ngShowngHide,只有.ng-hide类会被添加和移除,它的形式跟ngClass一样:.ng-hide-add.ng-hide-add-active.ng-hide-remove.ng-hide-remove-active

自动添加类

触发enter事件的指令会在DOM变更时收到一个.ng-enter样式类,然后,Angular添加ng-enter-active类,它会触发动画。ngAnimate自动检测CSS代码来判定动画什么时候完成。
这个事件完成时,Angular会从DOM元素上移除这两个类,使我们能够在DOM元素上定义动画相关的属性。
如果浏览器不支持CSS过渡或者动画,动画会开始,然后立即结束,DOM会处于最终的状态,不会添加过渡或者动画的样式类。

使用CSS3过渡

要做任何CSS动画,我们都要确认给动画中关注的DOM元素添加了样式。
CSS3过渡是完全基于样式类的,意思是说,只要我们在HTML上定义了动画的样式,这个动画就会在浏览器中动起来。要定义一个动画,我们需要指定想要添加动画的属性,以及特效的持续时间。

.fade-in {
    transition: 2s linear all;
    -webkit-transition: 2s linear all;
}

设置了这个过渡和时间之后,就可以在DOM元素的不同状态上定义属性了。

.fade-in:hover {
    width: 300px;
    height: 300px;
}

使用ngAnimate,Angular通过给每个动画事件添加两个样式类的方式开始了我们的指令动画:初始的ng-[EVENT]类,不久之后是ng-[EVENT]-active类。
为了自动让上面的DOM元素使用过渡实现Angular动画,我们修改上面初始的.fade-in示例来包含初始状态类:

.fade-in.ng-enter {
    opacity: 0;
}
.fade-in.ng-enter.ng-enter-active {
    opacity: 1;
}

也可以把transition属性放到基准CSS类中。

.fade-in {
    -webkit-transition: 2s linear all;
    transition: 2s linear all;
}
.fade-in.ng-enter { opacity: 0;}
.fade-in.ng-enter.ng-enter-active { opacity:1;}
.fade-in.ng-leave {opacity: 1;}
.fade-in.ng-leave.ng-leave-active { opacity: 0;}

使用CSS3 动画

使用CSS3动画,我们会用同样的初始样式类ng-[EVENT], 但是不需要在ng-[EVENT]-active状态中定义动画状态,因为CSS规则会处理剩余部分。
我们在@keyframes规则中创建动画。在@keyframes规则中定义的CSS元素内部,我们定义要处理的CSS样式。
想让DOM元素动起来时,我们使用animation:属性来绑定@keyframeCSS属性,它把动画添加到CSS元素上。
;当在CSS元素上绑定动画时,我们需要同时指定动画的名称和持续时间。如果我们忘记添加动画的持续时间,它默认会设成0,此时动画就不会运行了。
要创建@keyframes规则,我们需要给关键帧一个名字,并且设置动画的时间阶段,它包含了动画过程中的属性。

@keyframes firstAnimation {
    0% {color: yellow;}
    100% {color: black;}
}
/*对于Chrome和Safari浏览器 */
@-webkit-keyframes firstAnimation {
from {color: yellow;} //from等于0% 
to {color: black;}  //from等于100%
}

我们并不局限于0%和100%:可以分步提供动画,比如10%、15%,等等。要把@keyframe属性赋值到想要应用动画的类上,我们使用animation关键字,它把动画应用到CSS选择器选定的元素上。

.fade-in:hover {
    -webkit-animation: 2s firstAnimation;
    animation: 2s firstAnimation;
}

ngAnimate,我们把firstAnimation值绑定到任意用.fade-in类选定的元素上。Angular自动为我们添加和移除.ng-enter类,所以我们可以简单地把事件添加到.fade-in.ng-enter类上。

.fade-in.ng-enter {
    -webkit-animation: 2s firstAnimation;
    animation: 2s firstAnimation;
}

交错CSS过渡/动画

ngAnimate捆绑了一个额外的特性,用指定的延迟来间隔同时存在的动画。这意味着如果10个项进入了一个ngRepeat列表,每个项可以在上一个之后延迟X毫秒插入。这样产生的特效就是一个交错特效,ngAnimate把CSS过渡和动画处理成这样。

交错CSS过渡

沿用ng-enterng-enter-active这样组织CSS过渡代码的格式,可以添加一个额外的CSS类来提供交错延迟。使用下面的CSS代码,可以用CSS过渡来给我们的.fade-in类添加一个交错特效。

.fade-in.ng-enter-stagger {
    -webkit-transition-delay:200ms;
    transition-delay:200ms;
    /* 防止意外CSS继承的保护措施 */
    -webkit-transition-duration:0;
    transition-duration:0;
}

下面的代码会在每个后续项以动画方式进入之后,执行200毫秒的停顿。注意,另有一个CSS属性指定了持续时间,并且设置成零了。为什么?它在此是一个安全防护,防止意外的CSS继承基础CSS类。要是没有这种保障,交错特效可能就会被忽略了。
但是这对于我们的.fade-in类意味着什么呢?想象一下我们正在使用一个ngRepeat元素,这个元素使用的就是.fade-in类。

<div ng-repeat="item in items" class="fade-in">
    Item: #1 -- {{ item }}
</div>

每次一系列的项插入到列表中之后,交错延迟会逐步启动。Item #1会被正常插入,#1会在200毫秒之后,#3400毫秒之后,以此类推。

交错CSS动画

CSS动画也支持并且遵循与上面提到的CSS过渡交错特效同样的CSS命名约定。唯一的不同是没有使用transition-delay,而是用了animation-delay。如果用CSS动画来实现交错特效,.fade-in类看上去就会像这样:

.fade-in.ng-enter-stagger {
    -webkit-animation-delay:200ms;
    animation-delay:200ms;
    /* css交错动画需要放在这里 */
    -webkit-animation-duration:0;
    animation-duration:0;
}

既然CSS关键帧要等到重排(当浏览器重绘屏幕)时才会发生,可能会出现轻微的闪烁,或者元素自身可能短暂地不动,直到交错动画开始生效。这是因为关键帧动画尚未触发,所以from或者0%的动画还没有开始。为解决这个问题,在赋值了关键帧动画的CSS类中,可以放额外的CSS样式。

.fade-in.ng-enter {
    /* 重排之前的样式 */
    opacity:0;
    -webkit-animation: 2s firstAnimation;
    animation: 2s firstAnimation;
}
.fade-in.ng-enter-stagger { ... }
@keyframes firstAnimation { ... }
@-webkit-keyframes firstAnimation { ... }

什么指令支持交错动画

所有指令都可以,但是仅当同一父容器下的两个或更多相同动画事件同时触发时,才可以使用。所以当10个项被插入一个ngRepeat列表时,交互特效就产生。这意味着如果ngClass被放在一个ngRepeat元素上,ngClass的值在列表中对每个项都产生了变化,样式类变化的动画就会渲染出一个交错特效。
交错动画也可以在自定义指令中触发。在一行中用$animate服务调几次,一个交互动画就呈现出来了。确保每个动画的父元素是同一个,并且每个参与动画元素的className值也是相同的。

使用JavaScript 动画

JS动画不同于前两种Angular动画方法,因为我们直接使用JS设置DOM元素的属性。
所有的主流浏览器都支持JS动画,所以如果想在不支持CSS渐变和动画的浏览器上提供动画的话,这是个好的选择。
这里,我们更新JS来处理动画,而不是操控CSS来让元素动起来。
ngAnimate在模块API上添加了.animation方法;这个方法提供了一个接口,我们可以用来创建动画。
animiation()方法带有两个参数。

  • classname(字符串)
    这个classname会匹配要产生动画的元素的class
  • animateFun(函数)
    animate函数预期会返回一个对象,包含了指令会触发的不同事件函数(当使用的时候)。
angular.module('myApp',['ngAnimate']).animation('.fade-in',function() {
    return {
        enter: function(element, done) {
            // 运行动画
            // 当动画结束的时候调用done
                return function(cancelled) {
                    // 关闭或者取消的回调
                }
            }
        }
    });

$animate服务为指定的元素调用这些函数。在这些函数里,我们可以对这个元素做任何事情。唯一要求是在动画结束时,需要调用回调函数done()
在这些函数中,我们可以返回一个end函数,它会在动画结束或者动画被取消时调用。
当动画触发时,$animate为事件查找匹配的动画函数。如果找到了匹配事件的函数,它会执行这个函数,否则就会完全跳过这个动画。

微调动画

默认情况下,ngAnimate会自动尝试让每个通过$animate服务传递过来的元素都动起来。但是不必担心,只有包含了用CSS或者JS动画注册了的CSS类的元素才会真的动起来。
尽管这个系统在运作时,必须检查每个可能的CSS类,这可能会在低速设备上慢一些。ngAnimate提供了一个配置项,让$animate提供者可以使用正则表达式对元素进行过滤,以去掉不匹配元素上的动画操作。

myModule.config(function($animateProvider) {
    // 唯一合法的参数是正则表达式
    $animateProvider.classNameFilter(/\banimate-/);
});

现在有了给定的正则表达式,/animated/,只有以animate开始的CSS类会被为动画而处理。结果,我们的.fade-in动画不会再运行了,它需要被重命名成.animate-fade-in才能真正运行。

DOM回调事件

当动画在一个元素产生时,我们想要检测DOM操作什么时候发生,可以在$animate服务上注册一个事件。

element.on('$animate:before', function(evt, animationDetails) {});
element.on('$animate:after', function(evt, animationDetails) {});

内置指令的动画

ngRepeat动画

ngRepeat指令产生这些事件:

<div ng-controller="HomeController">
    <ul>
        <li class="fade-in" ng-repeat="r in roommates">
            {{ r }}
        </li>
    </ul>
</div>

我们的HomeController默认是这样定义的:

angular.module('myApp',['ngAnimate']).controller('HomeController',         
    function($scope) {
        $scope.roommates = ['Ari', 'Q', 'Sean', 'Anand'];
        setTimeout(function() {
            $scope.roommates.push('Ginger');
            $scope.$apply(); // 触发一次digest
            setTimeout(function() {
                $scope.roommates.shift();
                $scope.$apply(); // 触发digest
            }, 2000);
        }, 1000);
    });

在这些例子中,我们有一个roommates列表,包含了四个元素。在一秒钟之后,加了第五个。两秒之后,移除了第一个元素。

1.CSS3过渡

要让ngRepeat列表中的元素动起来,我们需要确认添加了展现元素初始状态的CSS样式类,以及为enteredit状态定义最终状态的类。
首先,在初始类上定义动画属性:

.fade-in.ng-enter,.fade-in.ng-leave {
    transition: 2s linear all;
    -webkit-transition: 2s linear all;
}

至此,可以简单地在动画中定义初始和最终阶段的CSS属性。这里,我们把元素从绿色的文字淡入,在进入动画的最终阶段把文字变成黑色。在离开(元素移除)动画中,我们把属性反转:

.fade-in.ng-enter {
    opacity: 0;
    color: green;
}
.fade-in.ng-enter.ng-enter-active {
    opacity: 1;
    color: black;
}
.fade-in.ng-leave {}
.fade-in.ng-leave.ng-leave-active {opacity: 0;}
2.CSS3关键帧动画

使用关键帧动画时,无需定义开始和结束的样式类,而是仅定义单个选择器,包含动画样式的键。
首先为关键帧定义动画属性:

@keyframes animateView-enter {
    from {opacity:0;}
    to {opacity:1;}
}
@-webkit-keyframes animateView-enter {
    from {opacity:0;}
    to {opacity:1;}
}
@keyframes animateView-leave {
    from {opacity: 1;}
    to {opacity: 0;}
}
@-webkit-keyframes animateView-leave {
    from {opacity: 1;}
    to {opacity: 0;}
}

设置了关键帧之后,我们可以简单地把动画附加到ngAnimate添加的CSS样式类上:

.fade-in.ng-enter {
    -webkit-animation: 2s fade-in-enter-animation;
    animation: 2s fade-in-enter-animation;
}
.fade-in.ng-leave {
    -webkit-animation: 2s fade-in-leave-animation;
    animation: 2s fade-in-leave-animation;
}
3.JavaScript动画

当用JS做动画时,需要在动画的描述对象上定义enterleave属性。

angular.module('myApp',['ngAnimate']).animation('.fade-in', function() {
    return {
        enter: function(element, done) {
            // 不使用jQuery的原始动画
            // 用jQuery会简单很多
            var op = 0, timeout,
            animateFn = function() {
                op += 10;
                element.css('opacity', op/100);
                if (op >= 100) {
                    clearInterval(timeout);
                    done();
                }
            };
            // 把初始透明度设为0
            element.css('opacity', 0);
            timeout = setInterval(animateFn, 100);
        },
        leave: function(element, done) {
            var op = 100,timeout,
            animateFn = function() {
                op-=10;
                element.css('opacity', op/100);
                if (op <= 0) {
                    clearInterval(timeout);
                    done();
                }
            };
            element.css('opacity', 100);
            timeout = setInterval(animateFn, 100);
        }
    }
});

ngView动画

ngView指令触发这些事件:

<a href="#/">Home</a>
<a href="#/two">Second view</a>
<a href="#/three">Third view</a>
<div class="animateView" ng-view></div>

当跟ng-view指令协作时,我们是在跟Angular内部的路由打交道。可以把路由设置为:

angular.module('myApp',['ngAnimate', 'ngRoute']).config(function($routeProvider) {
    $routeProvider.when('/', {
        template: '<h2>One</h2>'
    }).when('/two', {
        template: '<h2>Two</h2>'
    }).when('/three', {
        template: '<h2>Three</h2>'
    });
})

示例中的三个路由,每个显示了一个不同的视图。

1.CSS3过渡

要让ngView列表中的元素动起来,我们需要确认添加了展现元素初始状态的CSS样式类,以及为enteredit状态定义最终状态的类:

.animateView.ng-enter,.animateView.ng-leave {
    transition: 2s linear all;
    -webkit-transition: 2s linear all;
}

至此,可以简单地在动画中定义初始和最终阶段的CSS属性。这里,我们把元素从绿色的文字淡入,在进入动画的最终阶段把文字变成黑色。在离开(元素移除)动画中,我们把属性反转:

.animateView.ng-enter {
    opacity: 0;
    color: green;
}
.animateView.ng-enter.ng-enter-active {
    opacity: 1;
    color: black;
}
.animateView.ng-leave {}
.animateView.ng-leave.ng-leave-active {opacity: 0;}
2.CSS3关键帧动画

首先,添加我们为动画定义的@keyframe

@keyframes animateView-enter {
    from {opacity:0;}
    to {opacity:1;}
}
@-webkit-keyframes animateView-enter {
    from {opacity:0;}
    to {opacity:1;}
}
@keyframes animateView-leave {
    from {opacity: 1;}
    to {opacity: 0;}
}
@-webkit-keyframes animateView-leave {
    from {opacity: 1;}
    to {opacity: 0;}
}

为了应用动画,需要做的就是在我们的类中添加动画CSS样式:

.animateView.ng-enter {
    -webkit-animation: 2s animateView-enter;
    animation: 2s animateView-enter;
}
.animateView.ng-leave {
    -webkit-animation: 2s animateView-leave;
    animation: 2s animateView-leave;
}
3.JavaScript动画

首先,我们需要下载并且在文档的头部包含jQuery。
当用JS做动画时,需要在动画的描述对象上定义enterleave属性。

angular.module('myApp',['ngAnimate']).animation('.animateView', function() {
    return {
        enter: function(element, done) {
            // 显示如何用jQuery实现动画的例子
            // 注意,这需要在HTML中包含jQuery
            $(element).css({opacity: 0});
            $(element).animate({opacity: 1}, done);
        },
        leave: function(element, done) {done();}
    }
});

ngInclude动画

ngInclude指令触发这些事件:

<div ng-init="template.url='/home.html'" ng-controller="HomeController">
    <button ng-click="template.url='/home.html'">Home</button>
    <button ng-click="template.url='/second.html'">Second</button>
    <button ng-click="template.url='/third.html'">Third</button>
    <div class="animateInclude" ng-include="template.url"></div>
</div>

我们在页面中包含内联模板,也可以把这些视图设置为从远程服务器获取。

<script type="text/ng-template" id="/home.html">Home Template</script>
<script type="text/ng-template" id="/second.html">Second Template</script>
<script type="text/ng-template" id="/third.html">Third Template</script>
1.CSS3过渡

要让ngInclude列表中的元素动起来,我们需要确认添加了展现元素初始状态的CSS样式类,以及为enteredit状态定义最终状态的类:

.animateInclude.ng-enter,.animateInclude.ng-leave {
    transition: 2s linear all;
    -webkit-transition: 2s linear all;
}

至此,可以简单地在动画中定义初始和最终阶段的CSS属性。这里,我们把元素从绿色的文字淡入,在进入动画的最终阶段把文字变成黑色。在离开(元素移除)动画中,我们把属性反转:

.animateInclude.ng-enter {
    opacity: 0;
    color: green;
}
.animateInclude.ng-enter.ng-enter-active {
    opacity: 1;
    color: black;
}
.animateInclude.ng-leave {}
.animateInclude.ng-leave.ng-leave-active {opacity: 0;}
2.CSS3动画

首先,添加为动画定义的@keyframe

@keyframes animateInclude-enter {
    from {opacity:0;}
    to {opacity:1; color: green}
}
@-webkit-keyframes animateInclude-enter {
    from {opacity:0;}
    to {opacity:1; color: green}
}
@keyframes animateInclude-leave {
    from {opacity: 1;}
    to {opacity: 0; color: black}
}
@-webkit-keyframes animateInclude-leave {
    from {opacity: 1;}
    to {opacity: 0; color: black}
}

为了应用动画,需要做的就是在我们的类中添加动画CSS样式:

.animateInclude.ng-enter {
    -webkit-animation: 2s animateInclude-enter;
    animation: 2s animateInclude-enter;
}
.animateInclude.ng-leave {
    -webkit-animation: 2s animateInclude-leave;
    animation: 2s animateInclude-leave;
}
3.JavaScript动画

当用JS做动画时,需要在动画的描述对象上定义enterleave属性。

angular.module('myApp', ['ngAnimate']).animation('.animateInclude', function() {
    return {
        enter: function(element, done) {
            // 显示如何用jQuery实现动画的例子
            // 注意,这需要在HTML中包含jQuery
            $(element).css({opacity: 0});
            $(element).animate({opacity: 1}, done);
        },
        leave: function(element, done) {done();}
    }
});

ngSwitch动画

ngSwitch指令触发这些事件:


ngSwitch指令类似于前面的例子。对于这些例子,我们用下面使用了ng-switch指令的HTML来运行:

<div ng-init="template='home'"ng-controller="HomeController">
    <button ng-click="template='home'">Home</button>
    <button ng-click="template='second'">Second</button>
    <button ng-click="template='third'">Third</button>
    <div ng-switch="template">
        <div class="animateSwitch" ng-switch-when="home">
            <h1>Home</h1>
        </div>
        <div class="animateSwitch" ng-switch-when="second">
            <h1>Second</h1>
        </div>
        <div class="animateSwitch" ng-switch-when="third">
            <h1>Home</h1>
        </div>
    </div>
</div>
1.CSS3过渡

要让ngSwitch列表中的元素动起来,我们需要确认添加了展现元素初始状态的CSS样式类,以及为enteredit状态定义最终状态的类:

.animateSwitch.ng-enter,.animateSwitch.ng-leave {
    transition: 2s linear all;
    -webkit-transition: 2s linear all;
}

至此,可以简单地在动画中定义初始和最终阶段的CSS属性。这里,我们把元素从绿色的文字淡入,在进入动画的最终阶段把文字变成黑色。在离开(元素移除)动画中,我们把属性反转:

.animateSwitch.ng-enter {
    opacity: 0;
    color: green;
}
.animateSwitch.ng-enter.ng-enter-active {
    opacity: 1;
    color: black;
}
.animateSwitch.ng-leave {}
.animateSwitch.ng-leave.ng-leave-active {opacity: 0;}
2.CSS3动画

首先,添加为动画定义的@keyframe

@keyframes animateSwitch-enter {
    from {opacity:0;}
    to {opacity:1; color: green}
}
@-webkit-keyframes animateSwitch-enter {
    from {opacity:0;}
    to {opacity:1; color: green}
}
@keyframes animateSwitch-leave {
    from {opacity: 1;}
    to {opacity: 0; color: black}
}
@-webkit-keyframes animateSwitch-leave {
    from {opacity: 1;}
    to {opacity: 0; color: black}
}

为了应用动画,需要做的就是在我们的类中添加动画CSS样式:

.animateSwitch.ng-enter {
    -webkit-animation: 2s animateSwitch-enter;
    animation: 2s animateSwitch-enter;
}
.animateSwitch.ng-leave {
    -webkit-animation: 2s animateSwitch-leave;
    animation: 2s animateSwitch-leave;
}
3.JavaScript动画

当用JS做动画时,需要在动画的描述对象上定义enterleave属性。

angular.module('myApp',['ngAnimate']).animation('.animateSwitch',function() {
    return {
        enter: function(element, done) {
            // 显示如何用jQuery实现动画的例子
            // 注意,这需要在HTML中包含jQuery
            $(element).css({opacity: 0});
            $(element).animate({opacity: 1}, done);
        },
        leave: function(element, done) {done();}
    }
});

ngIf动画

ngIf指令触发这些事件:

<div ng-init="show=false" ng-controller="HomeController">
    <button ng-click="show=!show">Show</button>
    <div ng-if="show" class="animateNgIf">
        <h2>Show me</h2>
    </div>
</div>
1.CSS3过渡

要让ngIf中的元素动起来,我们需要确认添加了展现元素初始状态的CSS样式类,以及为
enter和edit状态定义最终状态的类:

.animateNgIf.ng-enter,.animateNgIf.ng-leave {
    transition: 2s linear all;
    -webkit-transition: 2s linear all;
}

至此,可以简单地在动画中定义初始和最终阶段的CSS属性。这里,我们把元素从绿色的文字淡入,在进入动画的最终阶段把文字变成黑色。在离开(元素移除)动画中,我们把属性反转:

.animateNgIf.ng-enter {
    opacity: 0;
    color: green;
}
.animateNgIf.ng-enter.ng-enter-active {
    opacity: 1;
    color: black;
}
.animateNgIf.ng-leave {}
.animateNgIf.ng-leave.ng-leave-active {opacity: 0;}
2.CSS3动画

首先,添加为动画定义的@keyframe

@keyframes animateNgIf-enter {
    from {opacity:0;}
    to {opacity:1;}
}
@-webkit-keyframes animateNgIf-enter {
    from {opacity:0;}
    to {opacity:1;}
}
@keyframes animateNgIf-leave {
    from {opacity: 1;}
    to {opacity: 0;}
}
@-webkit-keyframes animateNgIf-leave {
    from {opacity: 1;}
    to {opacity: 0;}
}

为了应用动画,需要做的就是在我们的类中添加动画CSS样式:

.animateNgIf.ng-enter {
    -webkit-animation: 2s animateNgIf-enter;
    animation: 2s animateNgIf-enter;
}
.animateNgIf.ng-leave {
    -webkit-animation: 2s animateNgIf-leave;
    animation: 2s animateNgIf-leave;
}
3.JavaScript动画

当用JS做动画时,需要在动画的描述对象上定义enterleave属性。

angular.module('myApp',['ngAnimate']).animation('.animateNgIf', function() {
    return {
        enter: function(element, done) {
            // 显示如何用jQuery实现动画的例子
            // 注意,这需要在HTML中包含jQuery
            $(element).css({opacity: 0});
            $(element).animate({opacity: 1}, done);
        },
        leave: function(element, done) {done();}
    }
});

ngClass动画

当视图中的样式类发生变化时,是可以基于行为去产生动画的。当一个CSS类变更时(比如在ngShowngHide指令中),$animate会通知和触发动画,不管是增加了新类,还是移除了旧类。
不同于使用进入动画的命名约定,我们为ngClass使用一个新的CSS约定,依次为新类加后缀,变为[CLASSNAME]-add[CLASSNAME]-remove
类似于上面的进入事件,ngAnimate会在合适的时间为具体的事件添加[CLASSNAME]-addactive[CLASSNAME]-remove-active
当我们在这些样式类上做动画时,动画先触发,然后最终的类在动画结束时才被添加。当一个类被移除时,它直到动画结束之前都还在元素上。
ngClass指令触发这些事件:

<div ng-init="grow=false" ng-controller="HomeController">
    <button ng-click="grow=!grow">Grow</button>
    <div ng-class="{grown:grow}" class="animateMe">
        <h2>Grow me</h2>
    </div>
</div>
1.CSS3过渡

要让ngClass中的元素动起来,我们需要确认添加了展现元素初始状态的CSS样式类,以及为enteredit状态定义最终状态的类:

.animateMe.grown-add,.animateMe.grown-remove {
    transition: 2s linear all;
    -webkit-transition: 2s linear all;
}

至此,可以简单地在动画中定义初始和最终阶段的CSS属性。

.grown {font-size: 50px;}
.animateMe.grown-add {font-size: 16px;}
.animateMe.grown-add.grown-add-active {font-size: 50px;}
.animateMe.grown-remove {}
.animateMe.grown-remove.grown-remove-active {font-size:16px;}
2.CSS3动画

首先,添加为动画定义的@keyframe

@keyframes animateMe-add {
    from {font-size: 16px;}
    to {font-size: 50px;}
}
@-webkit-keyframes animateMe-add {
    from {font-size: 16px;}
    to {font-size: 50px;}
}
@keyframes animateMe-remove {
    to {font-size: 50px;}
    from {font-size: 16px;}
}
@-webkit-keyframes animateMe-remove {
    to {font-size: 50px;}
    from {font-size: 16px;}
}

为了应用动画,需要做的就是在我们的类中添加动画CSS样式:

.animateMe.grown-add {
    -webkit-animation: 2s animateMe-add;
    animation: 2s animateMe-add;
}
.animateMe.grown-remove {
    -webkit-animation: 2s animateMe-remove;
    animation: 2s animateMe-remove;
}
3.JavaScript动画

当用JS做动画时,需要在动画的描述对象上定义addClassremoveClass属性。

angular.module('myApp',['ngAnimate']).animation('.animateMe', function() {
    return {
        addClass: function(ele, clsName, done){
            // 显示如何用jQuery实现动画的例子
            // 注意, 这需要在HTML中包含jQuery
            if (clsName === 'grown') {
                $(ele).animate({'font-size': '50px'}, 2000, done);
            } else { done(); }
        },
        removeClass: function(ele, clsName, done){
            if (clsName === 'grown') {
                $(ele).animate({'font-size': '16'}, 2000, done);
            } else { done(); }
        }
    }
});

ngShow/ngHide动画

ngShowngHide指令在显示或者隐藏元素时,使用了.ng-hide类。可以在显示和隐藏DOM元素之间的这段时间添加动画。
当在这些样式类上做动画时,动画会先触发,它完成时,最终的.ng-hide才会被加到DOM元素上。
因为当移除ng-hide类时,ng-hide指令还在DOM元素上,所以在它完成之前,我们是看不到动画的。因此,需要告诉CSS把我们的样式类显示出来,不要折叠。
ngShowngHide指令触发这些事件:

<div ng-init="show=false" ng-controller="HomeController">
    <button ng-click="show=!show">Show</button>
    <div ng-show="show" class="animateMe">
        <h2>Show me</h2>
    </div>
</div>
1.CSS3过渡

要让ngHide中的元素动起来,我们需要确认添加了展现元素初始状态的CSS样式类,以及为enteredit状态定义最终状态的类:

.animateMe.ng-hide-add,.animateMe.ng-hide-remove {
    transition: 2s linear all;
    -webkit-transition: 2s linear all;
    display: block !important;
}

注意CSS块中的最后一行:它告诉CSS渲染这个类,并且,对于display属性而言,没有其他备选值。没有这行的话,这个元素就不会显示了。至此,可以简单地在动画中定义初始和最终阶段的CSS属性。

.animateMe.ng-hide-add {opacity: 1;}
.animateMe.ng-hide-add.ng-hide-add-active{opacity: 0;}
.animateMe.ng-hide-remove {opacity: 0;}
.animateMe.ng-hide-remove.ng-hide-remove-active {opacity: 1;}
2.CSS3动画

首先,添加为动画定义的@keyframe

@keyframes animateMe-add {
    from {opacity: 1;}
    to {opacity: 0;}
}
@-webkit-keyframes animateMe-add {
    from {opacity: 1;}
    to {opacity: 0;}
}
@keyframes animateMe-remove {
    from {opacity:0;}
    to {opacity:1;}
}
@-webkit-keyframes animateMe-remove {
    from {opacity:0;}
  to {opacity:1;}
}

为了应用动画,需要做的就是在我们的类中添加动画CSS样式:

.animateMe.ng-hide-add {
    -webkit-animation: 2s animateMe-add;
    animation: 2s animateMe-add;
}
.animateMe.ng-hide-remove {
    -webkit-animation: 2s animateMe-remove;
    animation: 2s animateMe-remove;
    display: block !important;
}
3.JavaScript动画

当用JS做动画时,需要在动画的描述对象上定义addClassremoveClass属性。

angular.module('myApp',['ngAnimate']).animation('.animateMe', function() {
    return {
        addClass: function(ele, clsName, done){
            // 显示如何用jQuery实现动画的例子
            // 注意,这需要在HTML中包含jQuery
            if (clsName === 'ng-hide') {
                $(ele).animate({'opacity': 0}, 2000, done);
            } else { done(); }
        },
        removeClass: function(ele, clsName, done){
            if (clsName === 'ng-hide') {
                $(ele).css('opacity', 0);
                // 强制移除ng-hide类这样我们就可以真的把动画显示出来
                $(ele).removeClass('ng-hide');
                $(ele).animate({'opacity': 1}, 2000, done);
            } else { done(); }
        }
    }
});

创建自定义动画

$animate服务给我们在指令中实现自定义动画提供了帮助。把$animate服务注入到我们自己的应用中之后,可以用暴露出的事件为每个事件触发$animate对象上的关联函数。
要在我们自己的指令中开始动画,需要注入$animate服务。

angular.module('myApp',['ngAnimate']).directive('myDirective',     
    function($animate) {
        return {
            template: '<div class="myDirective"></div>',
            link: function(scope, ele, attrs) {
            // 在这里添加动画  例如:
                $animate['addClass'](element, 'ng-hide');
            }
        }
    });

至此,就可以把事件绑定到指令上,开始显示我们的动画了。
建立了指令之后,我们可以调用$animate函数创建一个动画,与我们的指令通信。

angular.module('myApp',['ngAnimate']).animation('.scrollerAnimation',function() {
    return {
        animateFun: function(element, done) {
            // 我们可以在这个函数中做任意想做的事
            // 但是需要调用done来让angular知道动画结束了
        }
    }
});

$animate服务暴露了一些方法,为内置指令的动画事件提供帮助。这些$animate服务暴露出来的事件是:enterleavemoveaddClassremoveClass
$animate服务把这些事件以函数的方式提供,让我们能在自己的指令中处理自定义动画。

addClass()

addClass()方法触发了一个基于className变量的自定义动画事件,并且把className值作为CSS类添加到元素上。当在DOM元素上添加样式类时,$animate服务给这个className添加了一个叫-add的后缀来让我们建立动画。
如果没有CSS过渡,在CSS选择器([className]-add)上也没有定义关键帧动画,ngAnimate就不会触发这个动画,只是会把这个样式类加上。
addClass()方法带三个参数。

  • element(jQuery/jqLite元素):正在建立动画的元素。
  • className(字符串):正在建立动画,并且添加到元素上的CSS类。
  • done(函数):当动画完成时调用的回调函数。
angular.module('myApp',['ngAnimate']).directive('myDirective', function($animate) {
    return {
        template: '<div class="myDirective"></div>',
        link: function(scope, ele, attrs) {
            ele.bind('click', function() {
                $animate.addClass(ele, 'greenlight');
            });
        }
    }
});

调用addClass()方法会经过如下步骤:
(1) 运行所有在元素上用JS定义的动画;
(2) [className]-add类被添加到元素上;
(3) $animate检查CSS样式来寻找过渡/动画的持续时间和延迟属性;
(4) [className]-add-active类被添加到元素的classList中(触发CSS动画);
(5) $animate用定义过的持续时间等待完成;
(6) 动画结束,$animate移除两个添加的类:[className]-add[className]-add-active
(7) className类被添加到元素上;
(8) 触发done()回调函数(如果定义了的话)。

removeClass()

removeClass()方法触发了一个基于className的自定义动画事件,并且移除在className值中定义的CSS类。当从DOM元素上移除一个类的时候,$animate服务给这个className添加了一个叫-remove的后缀来让我们建立动画。
如果没有CSS过渡,在CSS选择器([className]-remove)上也没有定义关键帧动画,ngAnimate就不会触发这个动画,只是会把这个样式类加上。
removeClass()方法带三个参数。

  • element(jQuery/jqLite元素):正在建立动画的元素。
  • className(字符串):正在建立动画,并且从元素上移除的CSS类。
  • done(函数):当动画完成时调用的回调函数。
angular.module('myApp',['ngAnimate']).directive('myDirective', function($animate) {
    return {
        template: '<div class="myDirective"></div>',
        link: function(scope, ele, attrs) {
            ele.bind('click', function() {
                $animate.addClass(ele, 'greenlight');
            });
        }
    }
});

调用·removeClass()·动画方法会经历如下步骤:
(1) 运行所有在元素上用JS定义的动画;
(2) [className]-remove类被添加到元素上;
(3) $animate检查CSS样式来寻找过渡/动画的持续时间和延迟属性;
(4) [className]-remove-active类被添加到元素的classList中(触发CSS动画);
(5) $animate用定义过的持续时间等待完成;
(6) 动画结束,$animate移除三个添加的类:[className][className]-remove[className]-remove-active
(7) 触发done()回调函数(如果定义了的话)。

enter()

enter()方法把元素添加到它在DOM中的父元素,然后运行enter动画。动画开始之后,$animation服务会添加ng-enterng-enter-active类,给指令一个机会来建立动画。
enter()方法最多可以带四个参数。

  • element(jQuery/jqLite元素):正在建立动画的元素。
  • parent(jQuery/jqLite元素):这个元素的父元素,它是我们enter动画的焦点。
  • after(jQuery/jqLite元素):这个元素的兄弟元素,它将会成为enter动画的焦点。
  • done(函数):当动画完成时调用的回调函数。
angular.module('myApp',['ngAnimate']).directive('myDirective',function($animate) {
    return {
        template: '<div class="myDirective"><h2>Hi</h2></div>',
        link: function(scope, ele, attrs) {
            ele.bind('click', function() {
                $animate.enter(ele, ele.parent());
            });
        }
    }
});

调用enter()动画方法会经历如下步骤:
(1) 本元素被插入父元素中,或者是after元素后面;
(2) $animate运行所有在元素上用JS定义的动画;
(3) ·.ng-enter·类被添加到元素的classList中;
(4) $animate检查CSS样式来寻找过渡/动画的持续时间和延迟属性。
(5) .ng-enter-active类被添加到元素的classList中(触发动画);
(6) $animate用定义过的持续时间等待完成;
(7) 动画结束,$animate从元素移除.ng-enter.ng-enter-active类;
(8) 触发done()回调函数(如果定义了的话)。

leave()

leave()方法运行leave动画。当它结束运行时,会把元素从DOM移除。动画开始之后,它会在元素上添加.ng-leave.ng-leave-active类。
leave()方法带两个参数。

  • element(jQuery/jqLite元素):正在建立动画的元素。
  • done(函数):当动画完成时调用的回调函数。
angular.module('myApp',['ngAnimate']).directive('myDirective',function($animate) {
    return {
        template: '<div class="myDirective"><h2>Hi</h2></div>',
        link: function(scope, ele, attrs) {
            ele.bind('click', function() {
                $animate.leave(ele);
            });
        }
    }
});

调用leave()动画方法会经历如下步骤:
(1) $animate可运行所有在元素上用JS定义的动画;
(2) .ng-leave类被添加到元素的classList中;
(3) $animate检查CSS样式来寻找过渡/动画的持续时间和延迟属性;
(4) .ng-leave-active类被添加到元素的classList中(触发动画);
(5) $animate用定义过的持续时间等待完成;
(6) 动画结束,$animate从元素移除.ng-leave.ng-leave-active类;
(7) 元素被从DOM移除;
(8) 触发done()回调函数(如果定义了的话)。

move()

move()函数触发move DOM动画。在动画开始之前,$animate服务或者把元素插入父容器中,或者直接加到after元素之后,如果有的话。动画开始后,为了动画的持续,.ng-move.ng-move-active就会被添加。
move()方法带有四个参数。

  • element(jQuery/jqLite元素):正在建立动画的元素。
  • parent(jQuery/jqLite元素):这个元素的父元素,它是我们enter动画的焦点。
  • after(jQuery/jqLite元素):这个元素的兄弟元素,它将会成为enter动画的焦点。
  • done(函数):当动画完成时调用的回调函数。
angular.module('myApp',['ngAnimate']).directive('myDirective',function($animate) {
    return {
        template: '<div class="myDirective"><h2>Hi</h2></div>',
        link: function(scope, ele, attrs) {
            ele.bind('click', function() {
                $animate.move(ele, ele.parent());
            });
        }
    }
});

调用move()动画方法会经历如下步骤:
(1) 元素被移到父元素中,或者在after元素之后;
(2) $animate可运行所有在元素上用JS定义的动画;
(3) .ng-move类被添加到元素的classList中;
(4) $animate检查CSS样式来寻找过渡/动画的持续时间和延迟属性;
(5) .ng-move-active类被添加到元素的classList中(触发动画);
(6) $animate用定义过的持续时间等待完成;
(7) 动画结束,$animate从元素移除.ng-move.ng-move-active类;
(8) 触发done()回调函数(如果定义了的话)。

与第三方库集成

Animate.css

要使用这个Animate.css,从https://github.com/yearofmoo/ngAnimate-animate.css上下载animate.cssanimate.js。只需在HTML中引用它们即可。

<!-- HTML的头部 -->
<link rel="stylesheet" type="text/css" href="css/animate.css">
<!-- HTML的主体 -->
<script type="text/javascript" src="js/vendor/animate.js"></script>

无需把ngAnimate当作我们应用的依赖项,只要把ngAnimate-animate.css当作依赖项包含进来就可以了。这种替代方式能运行,是因为ngAnimate-animate.css模块默认就请求了ngAnimate模块。
这个转换做完之后,我们就可以简单地通过ng-class指令来引用动画类了。

<div class="animateMe" ng-class="{'dn-fade':dn_fade}"></div>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,873评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,483评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,525评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,595评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,018评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,958评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,118评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,873评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,643评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,813评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,293评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,615评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,306评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,170评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,968评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,107评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,894评论 2 278

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,635评论 1 92
  • 导言 最近在学AngularJS的实例教程PhoneCat Tutorial App,发现网上的中文教程都比较久远...
    minxuan阅读 1,531评论 0 6
  • ng-model 指令ng-model 指令 绑定 HTML 元素 到应用程序数据。ng-model 指令也可以:...
    壬万er阅读 832评论 0 2
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,594评论 25 707
  • 江南的初春有一丝丝的沉闷,微风拂面略感寒意。虽然已经是开工上班了,但街上的行人还是稀疏,小区里的停车位也不那么挤了...
    自由最珍贵阅读 178评论 0 0