如何相对于导致更改的单击事件为 ng-repeat 项目设置动画
Posted
技术标签:
【中文标题】如何相对于导致更改的单击事件为 ng-repeat 项目设置动画【英文标题】:How to animate ng-repeat items relative to click events causing the change 【发布时间】:2016-04-24 20:47:20 【问题描述】:我正在尝试为用户从不同的项目集中选择项目设置动画。 该项目应该从被点击的集合动画到它在选定项目列表中的新位置。
在下面的演示中,将粉色框视为可用项目,将带边框的框视为选定项目的列表(蓝色框)。用户可以通过单击任一粉红色框来选择一个项目:
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>
嗯,它有点工作 - 但我正在使用 jQuery-ui 来找出起始位置(粉红色框的位置在响应式设计中会很谨慎)和 jquery animate 方法来为元素设置动画。
此外,我还必须将点击的方向存储在范围内,并在 enter
事件侦听器中设置初始位置和动画到结束位置。
我一直在阅读和尝试很多内置动画钩子的角度,但无法找到从相对/动态位置为元素设置动画的正确方法。
有没有更好的方法来以 Angular js 方式实现相同的用户体验..?
【问题讨论】:
您是否尝试在不使用任何 jquery ui 的情况下实现相同的行为? @kiran 使用 jQuery UI 查找位置很好。我认为会有更好的方法来构造它的角度,比如不必在控制器范围内存储点击的方向......我也不认为在$animate.on('enter'
中做整个事情是正确的方式
查看 ng-animate-ref 可能会有所帮助,因为它可以根据需要在应用程序区域中为元素设置动画。我认为它通常意味着一对一的关系,所以如果你的元素可以像你的演示一样产生多个实例,那么你可能需要挂钩动画的结尾并在完成后拉出引用,以便下一次迭代不会为多个元素设置动画。文档:docs.angularjs.org/api/ngAnimate
@ColtMcCormack 这仅在视图之间转换时有效...
看起来不错,您可能只想将模型附加到 Div,这样您就可以循环购买,然后您将在其中拥有模型和数据集以进行相应的遍历和处理。跨度>
【参考方案1】:
如果我正确理解了您的问题(如果没有,请告诉我);我认为解决问题的一种方法是这样的:
同时假设您的产品的尺寸(宽度)是恒定的 - 设置为 50 像素或其他东西 - ;您可以将粉色元素的位置设置为绝对;然后对粉红色元素使用 ng-repeat,在 html 中添加一个简短的 ng-style 属性,如下所示:
<div ng-repeat="item in products" ng-style="'left': $index*50 + 'px'" ng-click="add-to-purchased($index)"></div>
关于购买的产品:不要在“purchased”数组上使用 ng-repeat,在“add-to-purchased”函数中,将产品推送到“purchased”数组后,您可以简单地对产品进行动画处理"top: '到边界元素的高度距离'" 和 "left" 等于 $scope.purchased.length*50 + 'px'。然后使用 ng-class(带有切换)添加一个用于着色和其他 css 内容的类...(您也可以考虑颜色变化的过渡。您可能知道)
我还认为您可以使用 ng-class 来处理不同的高度和顶部问题(以防产品数量超过一条生产线的容量),该 ng-class 根据以下条件添加具有新“顶部”值的类:($index > some-number), 另一个 ng-class 用于上层元素(边界元素顶部的元素),改变它的高度 ...
希望对你有帮助
更新:
不幸的是,我没有很好地理解这个问题。但是现在看这个问题,我认为有一种更动态的方法。
在$scope.purchase
函数中,您可以使用$broadcast
向您的指令发送消息,并像这样传递被点击的元素(对于库存中的任何元素,无论是否使用ng-repeat 创建):
<div class="stock" ng-click="purchase($event)"></div>
和:
$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()
来获取元素相对于视口的位置(.top
,.left
,...),而不是 jquery-ui 的.position
,如果你想...
是否更接近解决方案?
【讨论】:
你错过了很多有问题的东西。 1)这是一个响应式设计,所以没有什么是不变的 2)不使用 ng-repeat 不是一个选项(其他外部指令,如ui-sortable
依赖它),这就是为什么我特别问“如何为 ng-repeat 项目设置动画” .如果一切都是不变的,我可以简单地定义 css 类中的像素位置并切换它们 3)检测点击的鼠标位置
"您可以简单地将产品设置为“顶部:'到边框元素的高度距离'”和“左侧”等于 $scope.purchased.length*50 + ' px'" - 如何以角度方式做到这一点,同时使用ng-repeat
和响应式设计是个问题。假设我们可以使用 jquery ui position()
方法找到股票和产品的当前位置,但为此我们应该首先检测点击的股票。然后如何将这些动态信息用于角度 ng-repeat 中的动画,比我已经完成的方式更好,这就是我要问的
我已经拥有的东西比你的建议更好:1)不假设任何东西都是恒定的 2)检测点击的位置(通过将其存储在控制器范围内,我认为这是一种糟糕的做法) 3) 与 ng-repeat 一起工作。我认为有一个更好的方法可以在 Angular 中做到这一点,所以我会问 Angular 专家并学习一些新东西
我想我刚刚明白了你的问题!所以让我再问一个细节。产品库存是否总是这种两种元素的形式,还是为了举例?我的意思是在真正的代码中,你也会对它们使用 ng-repeat 吗?因为我想到了解决问题的另一种方法。
在我的具体情况下,只有两个这样的。但一般的解决方案可能会在未来对其他人有所帮助【参考方案2】:
此解决方案是一项改进,它消除了在购买功能中向范围添加信息的需要,并且通过使用“源”指令并将来源信息存储在控制器属性中来避免混合模型数据和 UI 细节。这个例子被简化了,当然可以改进。关键是管理流程所需的数据永远不会通过范围公开。
如果目标元素要从 DOM 中删除(即它是 ng-repeat 的一部分,则必须稍微修改此解决方案以计算动画开始位置并将其存储为 monitor.click 处理程序的一部分而不是存储目标元素本身。
动画的方法在我看来是任意的。此示例使用 OP 的 jqueryUI 动画方法,但它也可以与 css 过渡或使用 $animate 一起工作。
一个完整的例子是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;
;
);
【讨论】:
我看到的不同之处在于,最终将属性添加到控制器本身而不是$scope
以及额外的指令、事件处理程序等......这看起来像是我所拥有的复杂形式。 .. 无论如何,我要为付出的时间和精力付出代价:)以上是关于如何相对于导致更改的单击事件为 ng-repeat 项目设置动画的主要内容,如果未能解决你的问题,请参考以下文章