Dowemo
0 0 0 0


Question:

I have a dynamic list of items using ng-repeat. When something happens an item may disappear. I have handled smoothly animating the removal of these items using ng-animate, but after they are gone, the remaining items simply snap to their new position. How can I animate this movement smoothly?

I've tried applying an "all" transition to the repeated class and using ng-move with no success.


Best Answer:


TLDR: Jank is bad, do animations with transform. Check out this fiddle for css and demo.

Explanation

Note that animating height, max-height, top, ... is really bad performance wise because they cause reflows and thus jank (more information on html5rocks|high-performance-animations).

There is however a method getting this type of animation using only transforms by utilizing the sibling selector.

When elements are added there is one reflow because of the new item, all items below are transformed up so they stay at the same position and then the transformation is removed for a smooth slide-in.

In reverse when elements are removed they are transformed to the new position for a smooth slide-out and when the element is finally removed there is again one reflow and the transform is removed instantly so they stay at their position (this is also why it is important to only have transition set on ng-animate).

Alternatively to the example you could also do a transform: scaleY(0) on the deleted item and only transform: translateY() the siblings.

Caveat

Note that this snippet has trouble when multiple elements are removed in quick succession (before the previous animation has completed).

This can be fixed by having an animation time faster than the time a user takes to delete another item or by doing some more work on the animation (out of scope of this answer).

Finally some code

Note: apparently SO breaks the demo with multiple deletes - check out the fiddle to see it in work.

angular.module('app', ['ngAnimate'])
  .controller('testCtrl', ['$scope', function($scope) {
    var self = this;
    self.items = [];
    var i = 65;
    for(; i < 72; i++)
    {
    	self.items.push({ value: String.fromCharCode(i) });
    }
    self.addItem = function()
    {
    	self.items.push({ value: String.fromCharCode(i) });
      i++;
    }
    self.removeItemAt = function(index)
    {
    	self.items.splice(index, 1);
    }
  }])
li
{
  height: 48px;
  width: 300px;
  border: 1px solid lightgrey;
  background-color: white;
  position: relative;
  list-style: none;
}
li.ng-enter,
li.ng-enter ~ li {
  transform: translateY(-100%);
}
li.ng-enter.ng-enter-active,
li.ng-enter.ng-enter-active ~ li {
  transform: translateY(0);
}
li.ng-animate {
  z-index: -1;
}
li.ng-animate,
li.ng-animate ~ li {
  transition: transform 0.6s;
}
li.ng-leave,
li.ng-leave ~ li {
  transform: translateY(0);
}
li.ng-leave.ng-leave-active,
li.ng-leave.ng-leave-active ~ li {
  transform: translateY(-100%);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.23/angular-animate.js"></script>
<div ng-app="app" ng-controller="testCtrl as ctrl">
  <ul>
    <li ng-repeat="item in ctrl.items" ng-bind="item.value">
    </li>
  </ul>
  <button ng-click="ctrl.addItem()">
  Add
  </button>
  <button ng-click="ctrl.removeItemAt(5)">
  Remove at 5
  </button>
</div>




Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs