Эта проблема
Добиться желаемого может быть немного сложно.
Распространенной попыткой является использование ng-style
для вычисления позиции элемента на основе его индекса в списке:
<div ng-repeat="c in countries | orderBy:q" ng-style="{ 'top': $index * 20 + 'px' }">
Демо: http://plnkr.co/edit/anv4fIrMxVDWuov6K3sw?p=preview
Проблема в том, что анимируются только некоторые элементы, и только ближе к низу.
Почему это?
Рассмотрим следующий список, отсортированный по имени (похожий на тот, что в демо выше):
- 2 - Дания
- 3 - Норвегия
- 1 - Швеция
Когда вы сортируете этот список по идентификатору, перемещается только один элемент — Швеция снизу вверх. Что на самом деле происходит, так это то, что элемент Швеции удаляется из DOM и снова вставляется в новую позицию. Однако, когда элемент вставляется в DOM, связанные с ним переходы CSS обычно не происходят (я говорю «нормально», поскольку в конечном итоге это зависит от того, как реализован рассматриваемый браузер).
Два других элемента остаются в DOM, получают новые top
позиции, а их переходы анимируются.
Таким образом, с этой стратегией переходы анимируются только для элементов, которые на самом деле не перемещались в DOM.
Другая стратегия заключается в том, чтобы включить модуль ngAnimate и использовать класс CSS ng-move
. Почти все примеры анимированных ng-repeats используют это.
Однако это не сработает по двум причинам:
Класс ng-move
будет применяться только к движущимся элементам (то есть только к элементу Швеции в приведенном выше примере).
Класс ng-move
применяется к элементу после того, как он был вставлен в новую позицию в DOM. У вас может быть CSS, который говорит «анимировать от непрозрачности от 0 до 1», но вы не можете «анимировать от старой позиции к новой», поскольку старая позиция неизвестна, и каждый элемент должен будет перемещаться на разное расстояние.
Решение
Решение, которое я использовал в прошлом, состоит в том, чтобы использовать ng-repeat
для отображения списка, но никогда не использовать базовые данные. Таким образом, все элементы DOM останутся в DOM и могут быть анимированы. Чтобы правильно отображать элементы, используйте ng-style
и пользовательское свойство, например:
ng-style="{ 'top': country.position * 20 + 'px' }"
Чтобы обновить свойство position
, выполните следующие действия:
Создайте копию базовых данных
Вы можете использовать angular.copy
для копирования всего массива, но с большими массивами это не будет хорошо для производительности. Это также было бы ненужным, поскольку каждому объекту в скопированном массиве потребуется только уникальное свойство и свойство для сортировки:
var tempArray = countries.map(function(country) {
var obj = {
id: country.id
};
obj[property] = country[property];
return obj;
});
В приведенном выше примере id
— это уникальное свойство, а property
— это переменная, содержащая имя свойства для сортировки, например name
.
Сортировать копию
Для сортировки массива используйте Array.prototype.sort()
с функцией сравнения:
tempArray.sort(function(a, b) {
if (a[property] > b[property])
return 1;
if (a[property] < b[property])
return -1;
return 0;
});
Установить позицию на индекс элемента в отсортированной копии
countries.forEach(function(country) {
country.position = getNewPosition(country.id);
});
function getNewPosition(countryId) {
for (var i = 0, length = tempArray.length; i < length; i++) {
if (tempArray[i].id === countryId) return i;
}
}
Есть возможности для улучшения, но это основа.
Демо: http://plnkr.co/edit/2Ramkg3sMW9pds9ZF1oc?p=preview
Я реализовал версию, в которой использовалось смещение, но это выглядело немного странно, поскольку элементы на мгновение накладывались друг на друга.
person
tasseKATT
schedule
23.12.2014