如何在ng-repeat完成绑定时运行动画?

时间:2014-11-06 18:38:18

标签: angularjs angularjs-ng-repeat

此处plunker演示了以下行为: 在ng-repeat完成创建html数据视图之前,会从标记中删除class ng-hide ,因此当removeClass被触发时,element的高度不是最终的,动画也不会更正。

同步数据绑定和动画的任何解决方案?

的index.html

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link href="style.css" rel="stylesheet" />
    <script src="https://code.angularjs.org/1.2.0/angular.min.js"></script>
    <script src="https://code.angularjs.org/1.2.0/angular-animate.min.js"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
    <script src="app.js"></script>
  </head>

  <body ng-app="app">
    <div ng-controller="MainController">
      <button ng-click="container.toggle()">Toggle</button>
      <ul class="animation-slider" ng-show="container.show">
        <li ng-repeat="item in container.items">{{item}}</li>
      </ul>
    </div>
  </body>
</html>

app.js

angular.module('app', [
  'ngAnimate'
])

.factory('Container', function($q, $timeout) {
  return function() {
    var deferred = $q.defer();
    var promise = deferred.promise;

    var container = {
      show: false,
      items: []
    };

    promise.then(function(data) {
      container.items = data;
      container.show = true;
    });

    container.toggle = function() {
      if (!container.show && container.items.length === 0) {
        container.load();
      } else {
        container.show = !container.show;
      }
    };

    container.load = function() {
      $timeout(function() {
        deferred.resolve([
          'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque feugiat arcu in ligula euismod, vitae rhoncus erat porttitor. In vel metus pulvinar metus fermentum dapibus id nec ligula. Nam ac justo id dolor euismod ornare. Morbi sit amet odio quis sapien sodales ornare dignissim eu risus. Nunc efficitur bibendum odio. Quisque vehicula maximus purus vel blandit. Ut eu molestie urna. Nulla elit ligula, tincidunt sit amet iaculis lobortis, elementum et nunc. Nulla eu egestas massa. Aliquam erat volutpat. Duis consequat cursus nibh ut sodales. Mauris sit amet neque volutpat, sodales lectus a, elementum sapien. In hac habitasse platea dictumst. Proin et blandit nulla. Duis eget tempus lorem.',
          'Mauris sit amet sodales massa. Pellentesque ut nunc tempus, maximus mauris consectetur, vehicula ex. Vivamus urna urna, lacinia in eros nec, pulvinar porta augue. Proin ullamcorper lacinia purus vitae efficitur. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec in massa in sem iaculis posuere sit amet nec elit. Ut ac nulla eget lorem tristique porta. Aenean ac consequat sem.',
          'Donec luctus leo a libero vehicula, consequat ullamcorper tortor facilisis. Suspendisse potenti. Etiam sed ultrices nibh, vel maximus enim. Aenean eu nulla vitae lorem commodo tempor. Donec sollicitudin tristique est. Phasellus lobortis et orci vitae posuere. Maecenas auctor cursus porttitor.'
        ]);
        //deferred.resolve([1,2]);
      }, 1000);
    };

    return container;
  };
})

.animation('.animation-slider', function() {
  return {
    beforeAddClass: function(element, className, done) {
      if (className === 'ng-hide') {
        TweenMax.to(element, 1, {
          height: 0,
          onComplete: done
        });

        return function() {
          element[0].style.height = '';
        };
      } else {
        done();
      }
    },
    removeClass: function(element, className, done) {
      if (className === 'ng-hide') {
        var height = element[0].clientHeight;
        console.log(height);
        element.css('height', 0);

        TweenMax.to(element, 1, {
          height: height,
          onComplete: done
        });

        return function() {
          element[0].style.height = '';
        };
      } else {
        done();
      }
    }
  };
})

.controller('MainController', function($scope, Container) {
  $scope.container = Container();
});

1 个答案:

答案 0 :(得分:1)

如果您希望仅在最后一个container.show元素被标记后将true设置为ng-repeat,您可以创建一个指示$last属性的指令在已标记的元素上true。当它为true时,该指令可以将container.show设置为true

因此,您的HTML看起来像这样,在重复handle-last-item-stamped元素上使用新的li指令:

<ul class="animation-slider" ng-show="container.show">
  <li ng-repeat="item in container.items" handle-last-item-stamped="$last">{{item}}</li>
</ul>

新指令看起来像这样(我注入了$timeout以防你需要在显示之前添加一点延迟):

.directive('handleLastItemStamped', ['$timeout', function($timeout){
  return {
    restrict: 'A',
    link: function (scope, element, attrs){
      scope.$watch(attrs.handleLastItemStamped, function (newVal, oldVal){
        if (newVal) {
          scope.container.show = true;
        }
      });
    }
  }  
}]);

当然,您必须从container.show = true;中移除promise.then

以下是这些变化的内容: http://plnkr.co/edit/nVG6gh7vabYhqg5nAula