用于平滑分选ng-repeat的ngAnimate示例?

dan*_*ani 3 angularjs ng-animate

我想看一个使用angular-animate(1.2x)对列表进行排序的功能示例.(我只是在互联网上遇到破碎的小提琴等):

给定数组[A,B,C]和后来的[C,B,A]的ng-repeat应该:

  • 将A移至底部
  • 将C移至顶部
  • 保持B的位置

(使用CSS绝对顶部定位或类似.)

使用交错(转换延迟)的示例是奖励.

tas*_*ATT 9

问题

实现你想要的东西可能有点棘手.

一种常见的尝试是使用ng-style根据列表中的索引计算元素的位置:

<div ng-repeat="c in countries | orderBy:q" ng-style="{ 'top': $index * 20 + 'px' }">
Run Code Online (Sandbox Code Playgroud)

演示: http ://plnkr.co/edit/anv4fIrMxVDWuov6K3sw?p = preview

问题是只有一些元素是动画的,而且只是底部.

这是为什么?

考虑按名称排序的以下列表(类似于上面演示中的列表):

  • 2 - 丹麦
  • 3 - 挪威
  • 1 - 瑞典

当您按ID对此列表进行排序时,只会移动一个元素 - 瑞典从下到上.实际发生的是,瑞典元素从DOM中删除并再次插入其新位置.但是,当一个元素插入到DOM中时,通常不会发生相关的CSS转换(我通常会说,因为它最终取决于所讨论的浏览器是如何实现的).

其他两个元素保留在DOM中,获取新top位置并对其过渡进行动画处理.

因此,使用此策略,转换仅针对实际上未在DOM中移动的元素进行动画处理.

另一种策略是包含ngAnimate模块并使用该CSS类ng-move.几乎所有动画ng-repeats的例子都使用它.

但是,由于两个原因,这不起作用:

  1. ng-move类只能够应用于移动(因此只对瑞典元件在上面的例子)中的元素

  2. ng-move将元素插入DOM中的新位置后,该类将应用于该元素.您可以拥有"从不透明度0到1的动画"的CSS,但是您不能将"从旧位置动画到新位置",因为旧位置未知且每个元素必须移动不同的距离.

一个办法

我过去使用的解决方案是ng-repeat用于呈现列表但从未实际使用基础数据.这样,所有DOM元素都将保留在DOM中并可以设置动画.要正确使用元素ng-style和自定义属性,例如:

ng-style="{ 'top': country.position * 20 + 'px' }"
Run Code Online (Sandbox Code Playgroud)

要更新position属性,请执行以下操作:

  1. 创建基础数据的副本

    您可以使用angular.copy复制整个阵列,但对于大型阵列,这对性能不利.它也是不必要的,因为复制数组中的每个对象只需要一个唯一的属性和要按以下类型排序的属性:

    var tempArray = countries.map(function(country) {
      var obj = {
        id: country.id
      };
      obj[property] = country[property];
      return obj;
    });
    
    Run Code Online (Sandbox Code Playgroud)

    在上面的示例中id是unique属性,并且property是一个变量,包含要排序的属性的名称,例如name.

  2. 对副本进行排序

    要使用Array.prototype.sort()compare函数对数组进行排序:

    tempArray.sort(function(a, b) {
      if (a[property] > b[property])
        return 1;
      if (a[property] < b[property])
        return -1;
      return 0;
    });
    
    Run Code Online (Sandbox Code Playgroud)
  3. 将位置设置为已排序副本中元素的索引

    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;
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)

还有改进的余地,但这是它的基础.

演示: http ://plnkr.co/edit/2Ramkg3sMW9pds9ZF1oc?p=preview

我实现了一个使用惊人的版本,但它看起来有点奇怪,因为元素会暂时相互重叠.