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

时间:2014-12-16 15:19:03

标签: angularjs ng-animate

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

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

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

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

使用交错(过渡延迟)的例子是一个奖励。

2 个答案:

答案 0 :(得分:9)

问题

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

一种常见的尝试是使用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 - 瑞典

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

另外两个元素保留在DOM中,获得新的top位置,并且它们的过渡是动画的。

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

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

然而,由于两个原因,这不起作用:

  1. ng-move类仅适用于移动的元素(因此仅适用于上例中的Sweden元素)

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

  3. 解决方案

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

    ng-style="{ 'top': country.position * 20 + 'px' }"
    

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

    1. 创建基础数据的副本

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

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

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

    2. 对副本进行排序

      要使用比较函数对数组进行排序,请使用Array.prototype.sort()

      tempArray.sort(function(a, b) {
        if (a[property] > b[property])
          return 1;
        if (a[property] < b[property])
          return -1;
        return 0;
      });
      
    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;
        }
      }
      
    4. 还有改进的余地,但这是它的基础。

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

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

答案 1 :(得分:0)

看看这个,非常简单:   CSS:

  .animate-enter, 
    .animate-leave
    { 
        -webkit-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
        -moz-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
        -ms-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
        -o-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
        transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
        position: relative;
        display: block;
    } 

    .animate-enter.animate-enter-active, 
    .animate-leave {
        opacity: 1;
        top: 0;
        height: 30px;
    }

    .animate-leave.animate-leave-active,
    .animate-enter {
        opacity: 0;
        top: -50px;
        height: 0px;
    }

HTML:

    <!doctype html>
<html ng-app>
<head>
  <meta charset="utf-8">
  <title>Top Animation</title>
  <script>document.write('<base href="' + document.location + '" />');</script>
  <link rel="stylesheet" href="style.css">
  <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
  <script src="http://code.angularjs.org/1.1.5/angular.js"></script>
</head>
<body ng-init="names=['Igor Minar', 'Brad Green', 'Dave Geddes', 'Naomi Black', 'Greg Weber', 'Dean Sofer', 'Wes Alvaro', 'John Scott', 'Daniel Nadasi'];">
  <div class="well" style="margin-top: 30px; width: 200px; overflow: hidden;">

        <ul class="nav nav-pills nav-stacked">
          <li ng-animate="'animate'" ng-repeat="name in names">
            <a href="#"> {{name}} </a>
          </li> 
      </ul>
    </form>
  </div>
</body>
</html>
相关问题