如何针对导致更改的单击事件设置ng-repeat项目的动画

我正在尝试为用户设置动画,从不同的项目集中选择项目。 该项目应从所单击的集合设置为所选项目列表中的新位置。

在下面的演示中,将粉红色框视为可用项目,将边框视为所选项目列表(蓝色框)。 用户可以通过单击任一粉红色框来选择项目:

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; } 
     


嗯,它有点工作 – 但我使用jQuery-ui找出起始位置(粉红色框的位置在响应式设计中会保持警惕)和jquery动画方法来动画元素。

此外,我必须将单击的方向存储在范围内,并且我将初始位置和动画设置为enter事件侦听器中的结束位置。

我一直在阅读和试验很多内置动画钩子的角度,但无法找出一种从相对/动态位置动画元素的正确方法。

有没有更好的方法来实现角js方式相同的用户体验..?

如果我正确理解你的问题(如果没有,请告诉我); 我认为解决问题的一种方法是这样的:

假设您的产品的尺寸(宽度)恒定 – 设置为50px或其他 – ; 你可以将粉红元素的位置设置为绝对; 然后使用ng-repeat为粉红色元素,在html中使用简短的ng-style属性,如下所示:

 

以及购买的产品:在“购买”arrays中使用ng-repeat而不是在“添加购买”function中使用ng-repeat,在将产品推送到“已购买”arrays后,您可以简单地将产品设置为“顶部” :’与边界元素’和“左”的高度距离等于{$ scope.purchased.length * 50 +’px’}。 然后使用ng-class(带有切换)为着色和其他css内容添加一个类…(你也可以考虑转换颜色变化。你可能知道)

我还认为你可以处理不同的高度和顶部问题(如果产品的数量超过一行的容量),使用ng-class,根据以下内容添加具有新“顶部”值的类:($ index> some-数字),以及上部元素的另一个ng-class(位于边界元素顶部的元素),改变它的高度……

我希望这可以帮到你


更新:

不幸的是,我没有理解这个问题。 但现在看问题,我认为有一种方法可以更加动态地做到这一点。

$scope.purchase函数中,你可以使用$broadcast你的指令发送消息,并像这样传递被点击的元素(对于库存中的任何元素,无论是否使用ng-repeat创建):

 

和:

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

在你的指令中,放入事件监听器:

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

我想你也可以使用target.getBoundingClientRect()来获取元素相对于视口( .left.left ,…)的位置,而不是jquery-ui的.position如果你想…

它离解决方案更近了吗?

此解决方案的一个改进是,它消除了在Purchase函数中向作用域添加信息的需要,并且它避免了通过使用“source”指令和将源信息存储在控制器属性中来混合模型数据和UI细节。 这个例子很简单,当然可以改进。 关键是管理流程所需的数据永远不会通过范围公开。

如果目标元素被从DOM中删除(即它是ng-repeat的一部分,那么必须稍微修改此解决方案以计算并存储动画起始位置作为monitor.click处理程序的一部分而不是存储目标元素本身。

动画的方法在我看来是任意的。 这个例子使用了OP的jqueryUI动画方法,但是它可以与css过渡或使用$ animate一样好用。

这里有一个完整的例子

 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; } } }; });