如何创建AngularJS指令以在数据更改时为元素设置动画?

时间:2014-06-12 16:49:31

标签: angularjs animation angularjs-directive ng-animate

在我的应用程序中我有一个标题显示在屏幕上。它绑定到我的范围内的属性。我正在使用ng-bind-html来简化我构建标题字符串的方式。 HTML看起来像这样:

<div class="headline slide-and-fade-up" animate-on-change="headline" ng-bind-html="headline"></div>

animate-change是我试图编写的指令,会导致标题在更改时生成动画。请求的动画用于旧动画在屏幕上移动和移出,而新动画从底部向上移动到位。我已经构建了一个与名为ng-animate的{​​{1}}兼容的动画,它可以完成我想要它做的事情。因为我需要在新值出现时为旧值设置动画,所以我需要复制元素,并在DOM中使用旧值和一个新值,然后让旧值离开。

到目前为止,我的指令看起来像这样:

slide-and-fade-up

这有几个问题:

  1. 克隆元素似乎没有&#34; live&#34;关于它的指令,即它存在于DOM中,但实际上并没有触发这个代码,这意味着动画只会发射一次,然后再也不会再发生
  2. 输入的元素中包含旧值,而离开的输入中包含新值。我不知道为什么会这样,但它基本上是完全落后的
  3. 如何完成此指令以使其达到我想要的效果?我确实通过使用angular.module('myApp.directives').directive('animateOnChange', ['$animate', function($animate) { return { restrict: 'A', link: function(scope, element, attrs) { scope.$watch(attrs.animateOnChange, function(newValue, oldValue) { if (newValue !== undefined && oldValue !== undefined && newValue !== oldValue) { $animate.enter(element.clone(), element.parent(), element); $animate.leave(element); } }); } } } ]); 并观察数据更改并切换布尔值,等待一小段时间然后再将其切换为开始。这是有效的,但与使用ng-if钩子的自包含指令相比,对我来说似乎过于苛刻。谢谢你的帮助。

2 个答案:

答案 0 :(得分:4)

编译您传入$animate.enter的克隆内容,然后将原始元素的innerHTML设置为&#34; oldValue&#34;在致电$watch之前作为$animate.leave的参数提供:

.directive('animateOnChange', function($animate, $compile) {
  var watchers = {};
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      // deregister `$watch`er if one already exists
      watchers[scope.$id] && watchers[scope.$id]();  
      watchers[scope.$id] = scope.$watch(attrs.animateOnChange, function(newValue, oldValue) {          
        if (newValue !== oldValue) {
          $animate.enter($compile(element.clone())(scope), element.parent(), element);
          element.html(oldValue);
          $animate.leave(element); 
        }
      });
    }
  }
})

注意:由于每次满足$watch表达式更改测试时都会重新编译和链接指令,因此您需要考虑将创建手表的事实每次都是。

在我的示例中,我将注销函数存储在任何已编译的指令实例中可用的对象中,并在创建除第一个之外的任何新观察者之前调用它。如果你不这样做,每次指令编译时你会看到数字观察者的两倍......显然不是一件好事。

Fork of your JS Bin

答案 1 :(得分:1)

链接功能在元素最初放置在页面上时,在编译阶段运行。

$animate.leave()完成后,将从DOM中删除leave元素,这是您将行为附加到的元素。这可能是为什么它只发生一次。

对于初学者,您需要引入一个变量来存储“当前”元素,而不是在每次转换时管理它。

现在的问题是调用element.clone()是有问题的。所有的角度善良都没有被魔法所附加。这是在监视阶段运行,这意味着范围(数据模型)已更改但DOM可能尚未更新。因此,在Angular更新之前克隆元素,克隆变为“愚蠢”。那是你看到的旧价值的那个。

与此同时,在开始动画后的瞬间,Angular赶上并将元素(现在正在出路)更新为新值,就在它被销毁之前。

您可能需要承担渲染内容的全部责任,而不是使用ng-bind-html。只需获取您正在创建的元素并调用element.html(newValue),这是您在观看该表达式时已经拥有的元素。或者你可以让克隆(哑)元素离开,并允许Angularified元素进入,假设当你开始动画时值将“很快就会改变”。