javascript - How to animate ng-repeat items relative to click events causing the change - Stack Overflow

I'm trying to animate a user selecting items from different sets of items.The item should animate

I'm trying to animate a user selecting items from different sets of items. The item should animate from the clicked set to it's new position in list of selected items.

In the below demo, consider the pink boxes as available items and the bordered box as the list of selected items (blue boxes). User can select an item by clicking on either of the pink boxes:

angular.module('test', ['ngAnimate'])
  .controller('testCtrl', function($scope) {
    $scope.products = [{}, {}, {}, {}];
    $scope.purchased = [{}];
    $scope.purchase = function(dir) {
      $scope.direction = dir
      $scope.purchased.push($scope.products.pop());
    };
  })
  .directive('testDir', function($animate) {
    return {
      link: function(scope, element) {
        $animate.on('enter', element, function(element, phase) {
          $target = scope.direction == 'left' ? $('.stock:first') : $('.stock:last');
          element.position({
            my: 'center',
            at: 'center',
            of: $target,
            using: function(pos, data) {
              $(this).css(pos);
              $(this).animate({
                top: 0,
                left: 0
              });
            }
          });
        });
      }
    };
  });
.stock {
  display: inline-block;
  width: 50px;
  height: 50px;
  background: hotpink;
}
.stock.right {
  margin-left: 100px;
}
.product {
  height: 50px;
  width: 50px;
  border: 1px solid;
}
.purchased {
  height: 60px;
  margin-top: 100px;
  border: 2px dotted;
}
.purchased .product {
  display: inline-block;
  margin: 5px;
  background: dodgerblue;
}
<script src=".1.3.min.js"></script>
<script src=".11.2/jquery-ui.js"></script>
<script src=".4.8/angular.js"></script>
<script src=".4.8/angular-animate.js"></script>
<div ng-app="test" ng-controller="testCtrl">
  <div class="stock" ng-click="purchase('left')"></div>
  <div class="stock right" ng-click="purchase('right')"></div>
  <div class="purchased clearfix">
    <div class="product" ng-repeat="product in purchased" data-test-dir>
    </div>
  </div>
</div>

I'm trying to animate a user selecting items from different sets of items. The item should animate from the clicked set to it's new position in list of selected items.

In the below demo, consider the pink boxes as available items and the bordered box as the list of selected items (blue boxes). User can select an item by clicking on either of the pink boxes:

angular.module('test', ['ngAnimate'])
  .controller('testCtrl', function($scope) {
    $scope.products = [{}, {}, {}, {}];
    $scope.purchased = [{}];
    $scope.purchase = function(dir) {
      $scope.direction = dir
      $scope.purchased.push($scope.products.pop());
    };
  })
  .directive('testDir', function($animate) {
    return {
      link: function(scope, element) {
        $animate.on('enter', element, function(element, phase) {
          $target = scope.direction == 'left' ? $('.stock:first') : $('.stock:last');
          element.position({
            my: 'center',
            at: 'center',
            of: $target,
            using: function(pos, data) {
              $(this).css(pos);
              $(this).animate({
                top: 0,
                left: 0
              });
            }
          });
        });
      }
    };
  });
.stock {
  display: inline-block;
  width: 50px;
  height: 50px;
  background: hotpink;
}
.stock.right {
  margin-left: 100px;
}
.product {
  height: 50px;
  width: 50px;
  border: 1px solid;
}
.purchased {
  height: 60px;
  margin-top: 100px;
  border: 2px dotted;
}
.purchased .product {
  display: inline-block;
  margin: 5px;
  background: dodgerblue;
}
<script src="https://code.jquery./jquery-2.1.3.min.js"></script>
<script src="https://code.jquery./ui/1.11.2/jquery-ui.js"></script>
<script src="https://code.angularjs/1.4.8/angular.js"></script>
<script src="https://code.angularjs/1.4.8/angular-animate.js"></script>
<div ng-app="test" ng-controller="testCtrl">
  <div class="stock" ng-click="purchase('left')"></div>
  <div class="stock right" ng-click="purchase('right')"></div>
  <div class="purchased clearfix">
    <div class="product" ng-repeat="product in purchased" data-test-dir>
    </div>
  </div>
</div>


Well, it kind of works - but I'm using jQuery-ui to find out the starting position (The position of pink boxes will wary in a responsive design) and jquery animate method to animate the element.

Also I have to store the clicked direction in scope and I'm setting both the initial position and animating to end position in the enter event listener.

I have been reading and experimenting a lot with built in animation hooks in angular, but couldn't figure out a proper way to animate elements from relative/dynamic positions.

Is there a better way to achieve the same user experience in angular js way..?

Share Improve this question asked Jan 19, 2016 at 11:35 T JT J 43.2k13 gold badges86 silver badges142 bronze badges 8
  • Are you trying to achieve same behavior without using any jquery ui? – Kiran Shakya Commented Jan 25, 2016 at 6:37
  • @kiran Using jQuery UI to find position is fine. I thought there will be a better way to structure this in angular, like without having to store clicked directions in controller scope... I also don't think doing the whole thing in $animate.on('enter' is the right way – T J Commented Jan 25, 2016 at 6:40
  • It might be beneficial to check out ng-animate-ref as it is meant for animating an element across application areas like you want. I think it is typically meant for a 1-to-1 relationship, so if your element can spawn multiple instances like your demo, then you might need to hook the end of the animation and pull the ref out once plete so that the next iteration doesn't animate multiple elements. Docs: docs.angularjs/api/ngAnimate – Colton McCormack Commented Jan 25, 2016 at 16:55
  • @ColtMcCormack That only works while transitioning between views... – T J Commented Jan 26, 2016 at 14:05
  • It seems fine, you just might want to attach the Model to the Div so that way you can just loop over purchases and then you will have the Model and datasets therein to walk and process accordingly. – Fallenreaper Commented Jan 27, 2016 at 19:07
 |  Show 3 more ments

2 Answers 2

Reset to default 1

if I've understood your question correctly(tell me if not); i think one way to handle the problem, goes like this:

while assuming the size(width) of your products to be constant -set to 50px or something- ; you can set the pink elements' position to absolute; then use ng-repeat for pink elements, with a brief ng-style attribute inside the html like this:

<div ng-repeat="item in products" ng-style="{'left': $index*50 + 'px'}" ng-click="add-to-purchased($index)"></div>

and about the purchased products: instead of using ng-repeat on the "purchased" array, inside "add-to-purchased" function, after pushing the product to the "purchased" array, you can simply animate the product to the "top: 'the height distance to the bordered element'" and "left" equal to {$scope.purchased.length*50 + 'px'}. then add a class using ng-class (with a toggle) for coloring and other css stuff... (you can also consider transition for color changes. as you probably know)

i also think that you can handle different heights and tops problem(in case that the number of products bees more than one line's capacity) with an ng-class which adds classes with new "top" values based on: ($index > some-number), and another ng-class for the upper element(the element that's on top of the bordered element), changing it's height ...

i hope this was helpful


Update:

unfortunately i hadn't understood the question well. but looking at the problem now, i think there is a way of doing this more dynamically.

inside the $scope.purchase function, you can message your directive with $broadcast and passing the clicked element like this (for any element in stock, either it's created with ng-repeat or not):

<div class="stock" ng-click="purchase($event)"></div>

and:

$scope.purchase = function(event) {
  $scope.purchased.push($scope.products.pop());
  $scope.$broadcast('purchaseHappened', event.target);
};

and inside your directive, put the event listener:

scope.$on('purchaseHappened', function(event, target) {
     //catch target in here, and then use it's position to animate the new elements...
})

i think you can also use target.getBoundingClientRect() to get the element's position, relative to the viewport (.top , .left ,...) instead of jquery-ui's .position if you want...

is it closer to the solution?

This solution is an improvement in that it eliminates the need to add information to the scope in the Purchase function and it avoids mixing model data and UI details by using a "source" directive and storing origin information in a controller property. The example is simplified and can of course be improved. The key point is that the data required to manage the process is never exposed via the scope.

If the target element is subject to being removed from the DOM (i.e it's part of an ng-repeat, then this solution would have to be modified slightly to calculate and store the animation start positions as part of the monitor.click handler rather than store the target element itself.

The method of animation seems to me to be arbitrary. This example uses the OP's jqueryUI animation method, but it would work just as well with css transitions or using $animate.

A full example is here

angular.module('app', [])
.controller('main', function($scope) {
  $scope.products = [{},{}];
  $scope.purchased = [{}];
  $scope.Purchase = function() {
    $scope.purchased.push({});
  };
})
.directive('source', function(){
  return {
    controller: function($scope) {
    }
  };
})
.directive('originator', function(){
  return{
    require: '^source',
    priority: 1,
    link:{
      pre: function(scope, element, attr, ctrl){
        element.on('click', function(evt){
          ctrl.target = evt.target;    
        });
      } 
    }
  };
})
.directive('sink', function(){
  return {
    require: '^source',
    link: function(scope, element, attr, ctrl){
      var target = ctrl.target;
      if(target){
        var $target = $(target);
        //animate from target to current position
        element.position({
            my: 'center',
            at: 'center',
            of: $target,
            using: function(pos, data) {
              $(this).css(pos);
              $(this).animate({
                top: 0,
                left: 0
              });
            }
          });
        ctrl.target = undefined;
      }
    }
  };
});

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745419560a4626905.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信