如何相对于导致更改的单击事件为 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 项目设置动画的主要内容,如果未能解决你的问题,请参考以下文章

在 ng-repeat 中更改一个按钮(元素)

当类更改时,ng-class 会在 AngularJs 视图中更新 ng-repeat 中的所有项目

如何向当前元素的相对位置的元素发送事件

Excel VBA复选框单击和更改事件相同吗?

当您单击它时,IE 不会在不确定的复选框上触发“更改”事件

Angular / Javascript - 重新加载ng-repeat数据后更改类