如何使用ng-repeat和ng-animate对列表进行动画排序?

时间:2022-11-25 16:10:02

I'm rendering a list of objects using ng-repeat with an orderBy filter like this:

我使用这样的orderBy过滤器使用ng-repeat来呈现对象列表:

<li class="list-item" ng-repeat="item in items | orderBy:predicate:reverse">

  • My attempts to ng-animate a change in sorting of the list have proven frustrating and aren't worth sharing. I have seen the Yearofmoo example app here.

    我试图改变列表排序的方式,结果证明这是令人沮丧的,不值得分享。我在这里看到了Yearofmoo示例应用程序。

    Unfortunately this demonstration is not quite what I'm trying to achieve. I need to animate the X position of a given list item when it is placed in a new order after the orderBy definition changes. I have tried to accomplish this with css transitions and absolute positioning, but ng-repeat seems to recreate the list items on orderBy making animation a real challenge.

    不幸的是,这个演示并不是我想要实现的。当一个给定的列表项在orderBy定义更改后被放置到一个新顺序时,我需要对它的X位置进行动画处理。我试图通过css转换和绝对定位来实现这一点,但是ng-repeat似乎重新创建了orderBy上的列表项,使动画成为真正的挑战。

    1. Is this possible with ng-repeat | orderBy (with or without ng-animate)?
    2. 使用ng-repeat | orderBy(是否有ng-animate)是否可以这样做?
    3. Can you suggest an approach or provide an example?
    4. 你能提供一个方法或者一个例子吗?

    2 个解决方案

    #1


    41  

    So, even if @Alex Osborn has shown a way to do what you want in the comments, here is my attempt:

    所以,即使@Alex Osborn在评论中展示了一种方法来实现你的愿望,我还是想尝试一下:

    angular.module('StackApp', []).controller('MainCtrl', function($scope) {
      'use strict';
    
      $scope.reverse = 'false';
    
      $scope.myList = [{
        id: 0,
        text: 'HTML5 Boilerplate'
      }, {
        id: 1,
        text: 'AngularJS'
      }, {
        id: 2,
        text: 'Karma'
      }, {
        id: 3,
        text: 'Hello'
      }, {
        id: 4,
        text: 'World'
      }, {
        id: 5,
        text: 'How'
      }, {
        id: 6,
        text: 'Are'
      }, {
        id: 7,
        text: 'You'
      }, {
        id: 8,
        text: '?'
      }, {
        id: 9,
        text: 'I'
      }, {
        id: 10,
        text: 'write'
      }, {
        id: 11,
        text: 'more'
      }, {
        id: 12,
        text: 'to'
      }, {
        id: 13,
        text: 'make'
      }, {
        id: 14,
        text: 'the'
      }, {
        id: 15,
        text: 'list'
      }, {
        id: 16,
        text: 'longer'
      }];
    
      $scope.$watch('reverse', function() {
        $scope.setOrder();
      });
    
      $scope.setOrder = function() {
    
        if ($scope.reverse === 'random') {
    
          var t = [];
    
          for (var i = 0; i < $scope.myList.length; i++) {
            var r = Math.floor(Math.random() * $scope.myList.length);
            while (inArray(t, r)) {
              r = Math.floor(Math.random() * $scope.myList.length);
            }
            t.push(r);
            $scope.myList[i].order = r;
          }
    
        } else {
    
          for (var i = 0; i < $scope.myList.length; i++) {
            if ($scope.reverse === 'false') {
              $scope.myList[i].order = i;
            } else {
              $scope.myList[i].order = ($scope.myList.length - 1 - i);
            }
          }
        }
      };
    
      function inArray(a, value) {
        for (var i = 0; i < a.length; i++) {
          if (a[i] === value) {
            return true;
          }
        }
        return false;
      }
    
    });
    #list {
      /* Needed, otherwise items would be at top of the page (see below) */
      position: absolute;
      /* full width, or it would look strange */
      width: 100%;
    }
    #list li {
      position: absolute;
      /* Top: 0; this will be changed for every single list item by AngularJS */
      top: 0;
      /* Item height; hold this in sync with template file */
      height: 40px;
      /*  Simple transition */
      -webkit-transition: top 0.5s ease-in-out;
      -moz-transition: top 0.5s ease-in-out;
      transition: top 0.5s ease-in-out;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
    <div ng-app="StackApp">
      <div ng-controller="MainCtrl">
        <h1>Animate Order</h1>
        <form action="">
          <label for="reverse">reverse = true</label>
          <br>
          <input type="radio" value="true" name="reverse" ng-model="reverse">
          <br>
          <br>
          <label for="reverse">reverse = false</label>
          <br>
          <input type="radio" value="false" name="reverse" ng-model="reverse">
          <br>
          <br>
          <label for="reverse">reverse = random (click button below to shuffle again)</label>
          <br>
          <input type="radio" value="random" name="reverse" ng-model="reverse">
        </form>
        <br>
        <br>
        <input type="button" ng-click="reverse = 'random';setOrder()" value="setOrder()">
        <br>
        <br>
        <ul id="list" ng-style="{height: ((myList.length * 40) + 'px')}">
          <li ng-repeat="item in myList" ng-style="{top: ((item.order * 40) + 'px')}">{{$index}} - {{item.order}}. {{item.text}}</li>
        </ul>
      </div>
    </div>

    So, AngularJS doesn't order the items, but it changes the CSS attribute top (ng-style="{top: ...}"). AngularJS doesn't recreate the list and we get a nice animation. :)

    因此,AngularJS没有对项目排序,但是它更改了CSS属性top (ng-style="{top:…}")。AngularJS没有重新创建列表,我们得到一个很好的动画。:)

    #2


    11  

    I've expanded upon AndreM96's answer to allow displaying the list as a grid.

    我已经扩展了AndreM96的答案,以允许将列表显示为一个网格。

    angular.module('StackApp', []).config(function($routeProvider) {
    
      'use strict';
    
      $routeProvider
        .when('/', {
          template: '<h1>Animate Order</h1>' +
            '<form action="">' +
            '<input type="radio" value="true" name="order" ng-model="order">' +
            '<label for="order">reverse</label><br><br>' +
    
            '<input type="radio" value="false" name="order" ng-model="order">' +
            '<label for="order">normal</label><br><br>' +
    
            '<input type="radio" value="random" name="order" ng-model="order">' +
            '<label for="order">random (click button below to shuffle again)</label><br>' +
    
            '</form>' +
            '<input type="button" ng-click="order = \'random\';setOrder()" value="randomize">' +
            '<br><br>' +
            '<ul id="list" ng-style="{height: ((myList.length * 90) + \'px\')}">' +
            '<li ng-repeat="item in myList" ng-style="{top: ((item.row * 90) + \'px\'), left: ((item.column * 90) + \'px\')}">{{$index}} - {{item.order}}. {{item.text}}</li>' +
            '</ul>',
          controller: 'MainCtrl'
        })
        .otherwise({
          redirectTo: '/'
        });
    
    });
    
    angular.module('StackApp').controller('MainCtrl', function($scope) {
      'use strict';
    
      $scope.order = 'false';
    
      $scope.myList = [{
          id: 0,
          text: 'HTML5 Boilerplate'
        },
        {
          id: 1,
          text: 'AngularJS'
        },
        {
          id: 2,
          text: 'Karma'
        },
        {
          id: 3,
          text: 'Hello'
        },
        {
          id: 4,
          text: 'World'
        },
        {
          id: 5,
          text: 'How'
        },
        {
          id: 6,
          text: 'Are'
        },
        {
          id: 7,
          text: 'You'
        },
        {
          id: 8,
          text: '?'
        },
        {
          id: 9,
          text: 'I'
        },
        {
          id: 10,
          text: 'write'
        },
        {
          id: 11,
          text: 'more'
        },
        {
          id: 12,
          text: 'to'
        },
        {
          id: 13,
          text: 'make'
        },
        {
          id: 14,
          text: 'the'
        },
        {
          id: 15,
          text: 'list'
        },
        {
          id: 16,
          text: 'longer'
        }
      ];
    
      $scope.$watch('order', function() {
        $scope.setOrder();
      });
    
      $scope.setOrder = function() {
    
        var i;
    
        if ($scope.order === 'random') {
          var t = [];
          for (i = 0; i < $scope.myList.length; i++) {
            var r = Math.floor(Math.random() * $scope.myList.length);
            while (inArray(t, r)) {
              r = Math.floor(Math.random() * $scope.myList.length);
            }
            t.push(r);
            $scope.myList[i].order = r;
          }
        } else if ($scope.order === 'false') {
          for (i = 0; i < $scope.myList.length; i++) {
            $scope.myList[i].order = i;
          }
        } else {
          for (i = 0; i < $scope.myList.length; i++) {
            $scope.myList[i].order = ($scope.myList.length - 1 - i);
          }
        }
    
        calcGridPosition();
      };
    
      function inArray(a, value) {
        for (var i = 0; i < a.length; i++) {
          if (a[i] === value) {
            return true;
          }
        }
        return false;
      }
    
      function calcGridPosition() {
        for (var i = 0; i < $scope.myList.length; i++) {
          var item = $scope.myList[i];
    
          // columns, left-to-right, top-to-bottom
          var columns = 5;
          item.column = item.order % columns;
          item.row = Math.floor(item.order / columns);
    
          // rows, top-to-bottom, left-to-right
          // var rows = 3;
          // item.column = Math.floor(item.order/rows);
          // item.row = item.order%rows;
        }
      }
    
    });
    #list {
      position: absolute;
      width: 100%;
      list-style-type: none;
      padding-left: 0;
    }
    
    #list li {
      position: absolute;
      height: 70px;
      width: 70px;
      background: #ddd;
      -webkit-transition: all 2.5s ease-in-out;
      -moz-transition: all 2.5s ease-in-out;
      transition: all 2.5s ease-in-out;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
    <main ng-app="StackApp">
      <div class="container" ng-view></div>
    </main>

    JSBin Demo

    JSBin演示

    如何使用ng-repeat和ng-animate对列表进行动画排序?

    #1


    41  

    So, even if @Alex Osborn has shown a way to do what you want in the comments, here is my attempt:

    所以,即使@Alex Osborn在评论中展示了一种方法来实现你的愿望,我还是想尝试一下:

    angular.module('StackApp', []).controller('MainCtrl', function($scope) {
      'use strict';
    
      $scope.reverse = 'false';
    
      $scope.myList = [{
        id: 0,
        text: 'HTML5 Boilerplate'
      }, {
        id: 1,
        text: 'AngularJS'
      }, {
        id: 2,
        text: 'Karma'
      }, {
        id: 3,
        text: 'Hello'
      }, {
        id: 4,
        text: 'World'
      }, {
        id: 5,
        text: 'How'
      }, {
        id: 6,
        text: 'Are'
      }, {
        id: 7,
        text: 'You'
      }, {
        id: 8,
        text: '?'
      }, {
        id: 9,
        text: 'I'
      }, {
        id: 10,
        text: 'write'
      }, {
        id: 11,
        text: 'more'
      }, {
        id: 12,
        text: 'to'
      }, {
        id: 13,
        text: 'make'
      }, {
        id: 14,
        text: 'the'
      }, {
        id: 15,
        text: 'list'
      }, {
        id: 16,
        text: 'longer'
      }];
    
      $scope.$watch('reverse', function() {
        $scope.setOrder();
      });
    
      $scope.setOrder = function() {
    
        if ($scope.reverse === 'random') {
    
          var t = [];
    
          for (var i = 0; i < $scope.myList.length; i++) {
            var r = Math.floor(Math.random() * $scope.myList.length);
            while (inArray(t, r)) {
              r = Math.floor(Math.random() * $scope.myList.length);
            }
            t.push(r);
            $scope.myList[i].order = r;
          }
    
        } else {
    
          for (var i = 0; i < $scope.myList.length; i++) {
            if ($scope.reverse === 'false') {
              $scope.myList[i].order = i;
            } else {
              $scope.myList[i].order = ($scope.myList.length - 1 - i);
            }
          }
        }
      };
    
      function inArray(a, value) {
        for (var i = 0; i < a.length; i++) {
          if (a[i] === value) {
            return true;
          }
        }
        return false;
      }
    
    });
    #list {
      /* Needed, otherwise items would be at top of the page (see below) */
      position: absolute;
      /* full width, or it would look strange */
      width: 100%;
    }
    #list li {
      position: absolute;
      /* Top: 0; this will be changed for every single list item by AngularJS */
      top: 0;
      /* Item height; hold this in sync with template file */
      height: 40px;
      /*  Simple transition */
      -webkit-transition: top 0.5s ease-in-out;
      -moz-transition: top 0.5s ease-in-out;
      transition: top 0.5s ease-in-out;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
    <div ng-app="StackApp">
      <div ng-controller="MainCtrl">
        <h1>Animate Order</h1>
        <form action="">
          <label for="reverse">reverse = true</label>
          <br>
          <input type="radio" value="true" name="reverse" ng-model="reverse">
          <br>
          <br>
          <label for="reverse">reverse = false</label>
          <br>
          <input type="radio" value="false" name="reverse" ng-model="reverse">
          <br>
          <br>
          <label for="reverse">reverse = random (click button below to shuffle again)</label>
          <br>
          <input type="radio" value="random" name="reverse" ng-model="reverse">
        </form>
        <br>
        <br>
        <input type="button" ng-click="reverse = 'random';setOrder()" value="setOrder()">
        <br>
        <br>
        <ul id="list" ng-style="{height: ((myList.length * 40) + 'px')}">
          <li ng-repeat="item in myList" ng-style="{top: ((item.order * 40) + 'px')}">{{$index}} - {{item.order}}. {{item.text}}</li>
        </ul>
      </div>
    </div>

    So, AngularJS doesn't order the items, but it changes the CSS attribute top (ng-style="{top: ...}"). AngularJS doesn't recreate the list and we get a nice animation. :)

    因此,AngularJS没有对项目排序,但是它更改了CSS属性top (ng-style="{top:…}")。AngularJS没有重新创建列表,我们得到一个很好的动画。:)

    #2


    11  

    I've expanded upon AndreM96's answer to allow displaying the list as a grid.

    我已经扩展了AndreM96的答案,以允许将列表显示为一个网格。

    angular.module('StackApp', []).config(function($routeProvider) {
    
      'use strict';
    
      $routeProvider
        .when('/', {
          template: '<h1>Animate Order</h1>' +
            '<form action="">' +
            '<input type="radio" value="true" name="order" ng-model="order">' +
            '<label for="order">reverse</label><br><br>' +
    
            '<input type="radio" value="false" name="order" ng-model="order">' +
            '<label for="order">normal</label><br><br>' +
    
            '<input type="radio" value="random" name="order" ng-model="order">' +
            '<label for="order">random (click button below to shuffle again)</label><br>' +
    
            '</form>' +
            '<input type="button" ng-click="order = \'random\';setOrder()" value="randomize">' +
            '<br><br>' +
            '<ul id="list" ng-style="{height: ((myList.length * 90) + \'px\')}">' +
            '<li ng-repeat="item in myList" ng-style="{top: ((item.row * 90) + \'px\'), left: ((item.column * 90) + \'px\')}">{{$index}} - {{item.order}}. {{item.text}}</li>' +
            '</ul>',
          controller: 'MainCtrl'
        })
        .otherwise({
          redirectTo: '/'
        });
    
    });
    
    angular.module('StackApp').controller('MainCtrl', function($scope) {
      'use strict';
    
      $scope.order = 'false';
    
      $scope.myList = [{
          id: 0,
          text: 'HTML5 Boilerplate'
        },
        {
          id: 1,
          text: 'AngularJS'
        },
        {
          id: 2,
          text: 'Karma'
        },
        {
          id: 3,
          text: 'Hello'
        },
        {
          id: 4,
          text: 'World'
        },
        {
          id: 5,
          text: 'How'
        },
        {
          id: 6,
          text: 'Are'
        },
        {
          id: 7,
          text: 'You'
        },
        {
          id: 8,
          text: '?'
        },
        {
          id: 9,
          text: 'I'
        },
        {
          id: 10,
          text: 'write'
        },
        {
          id: 11,
          text: 'more'
        },
        {
          id: 12,
          text: 'to'
        },
        {
          id: 13,
          text: 'make'
        },
        {
          id: 14,
          text: 'the'
        },
        {
          id: 15,
          text: 'list'
        },
        {
          id: 16,
          text: 'longer'
        }
      ];
    
      $scope.$watch('order', function() {
        $scope.setOrder();
      });
    
      $scope.setOrder = function() {
    
        var i;
    
        if ($scope.order === 'random') {
          var t = [];
          for (i = 0; i < $scope.myList.length; i++) {
            var r = Math.floor(Math.random() * $scope.myList.length);
            while (inArray(t, r)) {
              r = Math.floor(Math.random() * $scope.myList.length);
            }
            t.push(r);
            $scope.myList[i].order = r;
          }
        } else if ($scope.order === 'false') {
          for (i = 0; i < $scope.myList.length; i++) {
            $scope.myList[i].order = i;
          }
        } else {
          for (i = 0; i < $scope.myList.length; i++) {
            $scope.myList[i].order = ($scope.myList.length - 1 - i);
          }
        }
    
        calcGridPosition();
      };
    
      function inArray(a, value) {
        for (var i = 0; i < a.length; i++) {
          if (a[i] === value) {
            return true;
          }
        }
        return false;
      }
    
      function calcGridPosition() {
        for (var i = 0; i < $scope.myList.length; i++) {
          var item = $scope.myList[i];
    
          // columns, left-to-right, top-to-bottom
          var columns = 5;
          item.column = item.order % columns;
          item.row = Math.floor(item.order / columns);
    
          // rows, top-to-bottom, left-to-right
          // var rows = 3;
          // item.column = Math.floor(item.order/rows);
          // item.row = item.order%rows;
        }
      }
    
    });
    #list {
      position: absolute;
      width: 100%;
      list-style-type: none;
      padding-left: 0;
    }
    
    #list li {
      position: absolute;
      height: 70px;
      width: 70px;
      background: #ddd;
      -webkit-transition: all 2.5s ease-in-out;
      -moz-transition: all 2.5s ease-in-out;
      transition: all 2.5s ease-in-out;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
    <main ng-app="StackApp">
      <div class="container" ng-view></div>
    </main>

    JSBin Demo

    JSBin演示

    如何使用ng-repeat和ng-animate对列表进行动画排序?