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.com/jquery-2.1.3.min.js"></script> <script src="https://code.jquery.com/ui/1.11.2/jquery-ui.js"></script> <script src="https://code.angularjs.org/1.4.8/angular.js"></script> <script src="https://code.angularjs.org/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..?
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 becomes 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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With