最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - AngularJS - how to animate ng-include that is dynamically compiled and added - Stack Overflow

programmeradmin2浏览0评论

I have the following AngularJS (v1.1.4) code and am trying to fade-in (animate) the ng-include when it is added to the DOM. What am I doing wrong? Also, if anyone can suggest a better way of passing add/set/remove mands to the directive rather than watching the 'action' property, that would be much appreciated.

Plunker is here:

index.html

<!doctype html>
<html ng-app="plunker" >
<head>
  <meta charset="utf-8">
  <title>AngularJS Plunker</title>
  <link rel="stylesheet" href="styles.css" />
  <script>document.write('<base href="' + document.location + '" />');</script>
  <script src=".1.4/angular.js"></script>
  <script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">

  <button ng-click="loadPartial('partial1.html')">Click to load partial 1</button>
  <button ng-click="loadPartial('partial2.html')">Click to load partial 2</button>
  <button ng-click="loadPartial('partial3.html')">Click to load partial 3</button>

  <div views></div>

</body>
</html>

app.js

var app = angular.module('plunker', []);

app.controller('MainCtrl', ['$scope', 'viewsAPI', function(scope, viewsAPI) {

  scope.loadPartial = function (name) {
    viewsAPI.addView(name);
  };

}]);


app.factory('viewsAPI', function () {

    return {
        views: [],
        action: null,
        addView: function (viewName, options) {
            var view = { name: viewName, options: options };
            this.action = { type: 'add', view: view };
        },
        setView: function (viewName, options) {
            var view = { name: viewName, options: options };
            this.action = { type: 'set', view: view };
        },
        removeView: function () {
            this.action = { type: 'remove' };
        }
    }
});


app.directive('views', ['$pile', function (pile) {

    return {
        restrict: 'A',
        scope: {},
        replace: true,
        template: '<div class="views"></div>',

        controller: ['$scope', 'viewsAPI', function (scope, viewsAPI) {

            scope.api = viewsAPI;

            scope.$watch('api.action', actionChange, true);

            function actionChange (action) {

                if (!action) return;

                if (action.type == 'add') {
                    var view = scope.addView(action.view);
                    scope.api.views.push(view);
                }
                else if (action.type == 'set') {
                    scope.setView(action.view);
                }
                else if (action.type == 'remove') {
                    scope.removeView();
                }
            }
        }],

        link: function (scope, elm) {

            scope.addView = function (view) {
                var v = pile('<div class="view-wrapper" ng-include="\'' + view.name + '\'" ng-animate="fade"></div>')(scope);
                elm.append(v);
                return v;
            };

            scope.setView = function (view) {
            };

            scope.removeView = function () {
            };
        }
    };
}]);

styles.css

.fade-enter-setup { -webkit-transition: all 3s linear; opacity: 0; }
.fade-enter-setup { opacity: 1; }

partial1.html

<div>Hello I am a partial 1</div>

partial2.html

<div>PARTIAL 2-------------------</div>

partial3.html

<div>
33333333333333333333333333
<br />
this is partial 3
<br />
33333333333333333333333333
</div>

I have the following AngularJS (v1.1.4) code and am trying to fade-in (animate) the ng-include when it is added to the DOM. What am I doing wrong? Also, if anyone can suggest a better way of passing add/set/remove mands to the directive rather than watching the 'action' property, that would be much appreciated.

Plunker is here: http://plnkr.co/edit/rcgpI0n8fGWj6o01Mp3b?p=preview

index.html

<!doctype html>
<html ng-app="plunker" >
<head>
  <meta charset="utf-8">
  <title>AngularJS Plunker</title>
  <link rel="stylesheet" href="styles.css" />
  <script>document.write('<base href="' + document.location + '" />');</script>
  <script src="http://code.angularjs/1.1.4/angular.js"></script>
  <script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">

  <button ng-click="loadPartial('partial1.html')">Click to load partial 1</button>
  <button ng-click="loadPartial('partial2.html')">Click to load partial 2</button>
  <button ng-click="loadPartial('partial3.html')">Click to load partial 3</button>

  <div views></div>

</body>
</html>

app.js

var app = angular.module('plunker', []);

app.controller('MainCtrl', ['$scope', 'viewsAPI', function(scope, viewsAPI) {

  scope.loadPartial = function (name) {
    viewsAPI.addView(name);
  };

}]);


app.factory('viewsAPI', function () {

    return {
        views: [],
        action: null,
        addView: function (viewName, options) {
            var view = { name: viewName, options: options };
            this.action = { type: 'add', view: view };
        },
        setView: function (viewName, options) {
            var view = { name: viewName, options: options };
            this.action = { type: 'set', view: view };
        },
        removeView: function () {
            this.action = { type: 'remove' };
        }
    }
});


app.directive('views', ['$pile', function (pile) {

    return {
        restrict: 'A',
        scope: {},
        replace: true,
        template: '<div class="views"></div>',

        controller: ['$scope', 'viewsAPI', function (scope, viewsAPI) {

            scope.api = viewsAPI;

            scope.$watch('api.action', actionChange, true);

            function actionChange (action) {

                if (!action) return;

                if (action.type == 'add') {
                    var view = scope.addView(action.view);
                    scope.api.views.push(view);
                }
                else if (action.type == 'set') {
                    scope.setView(action.view);
                }
                else if (action.type == 'remove') {
                    scope.removeView();
                }
            }
        }],

        link: function (scope, elm) {

            scope.addView = function (view) {
                var v = pile('<div class="view-wrapper" ng-include="\'' + view.name + '\'" ng-animate="fade"></div>')(scope);
                elm.append(v);
                return v;
            };

            scope.setView = function (view) {
            };

            scope.removeView = function () {
            };
        }
    };
}]);

styles.css

.fade-enter-setup { -webkit-transition: all 3s linear; opacity: 0; }
.fade-enter-setup { opacity: 1; }

partial1.html

<div>Hello I am a partial 1</div>

partial2.html

<div>PARTIAL 2-------------------</div>

partial3.html

<div>
33333333333333333333333333
<br />
this is partial 3
<br />
33333333333333333333333333
</div>
Share Improve this question asked May 9, 2013 at 12:49 romiemromiem 9,0407 gold badges32 silver badges37 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

If you are still interested, i accidently created a plunker today, that should answer your long ago question.

http://plnkr.co/Gd5f6J

Now with the latest 1.2 stable release, if angular finds ngAnimate module, it will add specific css classes to the dom, when a built-in directive like ng-if, ng-switch, ng-view detected, that a child has changed. Those classes trigger your css transitions, so if your view partial takes a long time to load, the animation will start, when it was fully loaded.

index.html:

<div class="view-animate-container">
  <div class="well slide" ng-view>
    <!-- view container. Asynchronously loaded view templates are placed here -->
  </div>
</div>

app.js:

angular.module('myApp', ['ngRoute', 'ngAnimate'])
.run(function($rootScope, $location){
  $rootScope.matchesRoute = function() {
    for(var i=0; i<arguments.length; i++) {
      if($location.path() === arguments[i]) {
        return true;
      }
    }
    return false;
  }
})
.config(function($routeProvider) {
    $routeProvider
      .when('/bellsAndWhistles', {templateUrl: 'bellsAndWhistles.html', controller: angular.noop()})
      .when('/about',            {templateUrl: 'about.html',            controller: angular.noop()})
      .otherwise({redirectTo: '', templateUrl: 'home.html'});
});

styles.css:

html, body{
  height: 100%;
}

.view-animate-container {
    position: relative;
    overflow: hidden;
    height:100%;
}

.view-animate-container > .slide.ng-enter,
.view-animate-container > .slide.ng-leave {
    -webkit-transition: all ease 0.5s;
       -moz-transition: all ease 0.5s;
         -o-transition: all ease 0.5s;
            transition: all ease 0.5s;  
    width: 100%;
    position:absolute;
}

.view-animate-container > .slide.ng-enter {
    left:100%;
    opacity: 0;
}
.view-animate-container > .slide.ng-enter.ng-enter-active {
    left:0;
    opacity: 1;
}
.view-animate-container > .slide.ng-leave.ng-leave-active {
    left: -100%;
    opacity: 0;
}

I came across this same issue. However, the behavior of content below the animated view will be jumpy and undesired. See this Plunker Example.

So, to expand on @angabriel 's answer, I worked on an alternative. See the following Plunker Example, which is a modification of @angabriel's work. You will need to use Chrome to see it working, since I did not had the chance to add the css for other browsers.

The main difference is the CSS. It is worth mentioning that I had to override the following:

  • min-height
  • margin-top
  • margin-bottom
  • padding-top
  • padding-bottom

To make the transition smooth, since bootstrap adds some rules that make the transition jumpy once the element that leaving is removed from the DOM.

/* CSS */
.view-animate-container > .slide.ng-enter,
.view-animate-container > .slide.ng-leave {
    -webkit-transition: all ease 0.5s;
    -moz-transition: all ease 0.5s;
    -o-transition: all ease 0.5s;
    transition: all ease 0.5s;
    width: 100%;
    position: relative;
}

.view-animate-container > .slide.ng-enter {
    -webkit-animation-name: slideIn;
    animation-name: slideIn;
    -webkit-animation-duration: .5s;
    animation-duration: .5s;
    -webkit-animation: slideIn .5s;
    animation: slideIn .5s;
}

.view-animate-container > .slide.ng-leave {
    -webkit-animation-name: slideOut;
    animation-name: slideOut;
    -webkit-animation-duration: .5s;
    animation-duration: .5s;
    -webkit-animation: slideOut .5s;
    animation: slideOut .5s;
}

@keyframes slideOut {
    from {
        opacity: 1;
        max-height: 500px;
    }
    50% {
        opacity: 0;
        max-height: 0px;
        margin-top: 0;
        min-height: 0;
        margin-bottom: 0;
        padding-bottom: 0;
        padding-top: 0;
    }
    to {
        opacity: 0;
        max-height: 0px;
        margin-top: 0;
        min-height: 0;
        margin-bottom: 0;
        padding-bottom: 0;
        padding-top: 0;
    }
}

@keyframes slideIn {
    from {
        opacity: 0;
        max-height: 0px;
        margin-top: 0;
        min-height: 0;
        margin-bottom: 0;
        padding-bottom: 0;
        padding-top: 0;
    }
    50% {
        max-height: 0;
        opacity: 0;
        margin-top: 0;
        min-height: 0;
        margin-bottom: 0;
        padding-bottom: 0;
        padding-top: 0;
    }
    to {
        max-height: 500px;
        opacity: 1;
    }
}

PROS

Content below the view div will not be jumpy

CONS

Content height: If the view content is grater than 500px (in my case) it will animate to 500px then jump to its full height (as pointed out by Isaac)

Thanks

发布评论

评论列表(0)

  1. 暂无评论