Как я могу оживить движение оставшихся элементов ng-repeat при удалении?

У меня есть динамический список элементов, использующий ng-repeat. Когда что-то происходит, элемент может исчезнуть. Я обработал плавное оживление удаления этих элементов, используя ng-animate, но после того, как они ушли, остальные предметы просто привязаны к их новой позиции. Как я могу оживить это движение плавно?

Я пробовал применить "все" переход к повторному классу и использовать ng-move без успеха.

Ответ 1

Вы можете добиться этого, оживив свойство max-height. Проверьте этот образец:

http://jsfiddle.net/k4sR3/8/

Вам нужно будет выбрать достаточно высокое значение для max-height (в моем примере я использовал 90px). Когда элемент изначально добавляется, вы хотите, чтобы он начинался с 0 height (я также анимировал left, чтобы предмет перемещался слева, а также opacity, но вы можете удалить их, если они не поддавайтесь тому, что вы делаете):

.repeated-item.ng-enter {
    -webkit-transition:0.5s linear all;
    -moz-transition:0.5s linear all;
    -o-transition:0.5s linear all;
    transition:0.5s linear all;
    max-height: 0;
    opacity: 0;
    left: -50px;
}

Затем вы устанавливаете окончательные значения для этих свойств в правиле ng-enter-active:

.repeated-item.ng-enter.ng-enter-active {
    max-height: 90px;
    opacity: 1;
    left: 0;
}

Удаление элементов немного сложнее, так как вам нужно будет использовать анимацию на основе ключевых кадров. Опять же, вы хотите оживить max-height, но на этот раз вы хотите начать с 90px и уменьшить его до 0. По мере того как анимация запускается, элемент будет уменьшаться, и все следующие элементы будут плавно продвигаться.

Сначала определите анимацию, которую вы будете использовать:

@keyframes my_animation {
  from {
    max-height: 90px;
    opacity: 1;
    left: 0;
  }
  to {
    max-height: 0;
    opacity: 0;
    left: -50px;
  }
}

(Для краткости я не указываю определения, относящиеся к конкретному поставщику, @-webkit-keyframes, @-moz-keyframes и т.д. - проверьте приведенный выше jsfiddle для полного образца.)

Затем объявите, что вы будете использовать эту анимацию для ng-leave следующим образом:

.repeated-item.ng-leave {
  -webkit-animation:0.5s my_animation;
  -moz-animation:0.5s my_animation;
  -o-animation:0.5s my_animation;
  animation:0.5s my_animation;
}

Основы

В случае, если кто-то борется с выяснением того, как заставить анимацию AngularJS вообще работать, здесь сокращенное руководство.

Во-первых, чтобы включить поддержку анимации, вам нужно будет добавить дополнительный файл angular-animate.js после загрузки angular.js. Например:.

<script type="text/javascript" src="angular-1.2/angular.js"></script>
<script type="text/javascript" src="angular-1.2/angular-animate.js"></script>

Затем вам нужно загрузить ngAnimate, добавив его в список зависимостей вашего модуля (во втором параметре):

var myApp = angular.module('myApp', ['ngAnimate']);

Затем назначьте класс вашему элементу ng-repeat. Вы будете использовать это имя класса для назначения анимации. В моем примере я использовал repeated-item как имя:

<li ng-repeat="item in items" class="repeated-item">

Затем вы определяете свои анимации в CSS с помощью класса repeated-item, а также специальных классов ng-enter, ng-leave и ng-move, которые Angular добавляет к элементу, когда он добавляется, удалены или перемещены.

Официальная документация для анимации AngularJS находится здесь:

http://docs.angularjs.org/guide/animations

Ответ 2

TL;DR: Jank плох, делайте анимацию с преобразованием. Проверьте эту скрипту для css и демонстрации.


Объяснение

Обратите внимание, что анимация height, max-height, top,... действительно плохая производительность, потому что они вызывают перекосы и, следовательно, jank (дополнительная информация о HTML5Rocks | высокопроизводительные-анимации).

Однако существует способ получения этого типа анимации с использованием только преобразований, используя селектор sibling.

Когда элементы добавляются, есть один переплав из-за нового элемента, все элементы ниже преобразуются, поэтому они остаются в одном и том же положении, а затем преобразование удаляется для плавного вставки.

В обратном случае, когда элементы удаляются, они преобразуются в новое положение для плавного выдвижения, и когда элемент окончательно удаляется, происходит повторное отражение, и преобразование немедленно удаляется, поэтому они остаются в своем положении (это также почему важно установить transition на ng-animate).

В качестве альтернативы к примеру вы также можете сделать transform: scaleY(0) в удаленном элементе и только transform: translateY() братьев и сестер.

Caveat

Обратите внимание, что этот фрагмент имеет проблемы при удалении нескольких элементов с быстрой последовательностью (до завершения предыдущей анимации).

Это можно устранить, если время анимации будет быстрее, чем время, которое пользователь предпримет, чтобы удалить другой элемент или выполнить еще одну работу над анимацией (вне сферы действия этого ответа).

Наконец, код

Примечание. По-видимому, SO разрывает демо с несколькими удалениями - посмотрите fiddle, чтобы увидеть его в работе.

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>