SVG ng-repeat元素上的SMIL动画仅在页面加载时发生

时间:2014-10-28 17:30:32

标签: angularjs animation svg smil

我使用ng-repeat指令为<rect>个孩子生成了一些<animate>

在页面加载时,动画被正确触发,一切都按预期发生。 但是,当我添加新的<rect>时,动画不会发生。

以下代码段演示了此行为:

&#13;
&#13;
function Controller($scope) {
  $scope.rects = [];
  var spacing = 5;
  var width = 10;
  var height = 100;
  var x = 10;
  var y = 10;

  $scope.newRect = function() {
    $scope.rects.push({
      x: x,
      y: y,
      width: width,
      height: height
    });
    x += spacing + width;
  };

  for (var i = 0; i < 5; i++) {
    $scope.newRect();
  }

}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app ng-controller="Controller">
  <svg width="1000" height="200">
    <rect ng-repeat="rect in rects" x="{{rect.x}}" y="{{rect.y}}" height="{{rect.height}}" width="{{rect.width}}">
      <animate attributeName="y" from="{{rect.height}}" to="{{rect.y}}" dur="1s" repeatCount="1" />
      <animate attributeName="height" from="0" to="{{rect.height}}" dur="1s" repeatCount="1" />
    </rect>
  </svg>
  <button ng-click="newRect()">One more</button>
</div>
&#13;
&#13;
&#13;

加载示例后,将显示4 <rect>,从底部到顶部进行动画制作。然而,当按下&#34;再多一次&#34;按钮,新<rect>将添加没有动画(在Firefox 35和Chrome 38上测试行为)。

如何触发新<rect>的动画?

1 个答案:

答案 0 :(得分:4)

动画元素的默认开始时间(相当于begin="0s")始终相对于SVG加载时间进行测量。即使您在页面加载后动态创建动画元素,也是如此。

如果您想要任何其他开始时间,您需要(a)为begin属性显式设置不同的值,或者(b)使用动画元素DOM的beginElement() or beginElementAt(offsetTime)方法对象。由于您使用脚本创建元素并希望它们在插入后立即启动,因此beginElement()方法可能是最简单的方法。

编辑添加:

如果beginElement不起作用,因为angular-js不能直接访问创建的元素,您可以使用begin属性的事件格式。如果begin属性包含DOM事件的名称(或以分号分隔的时间和事件名称列表),则动画将在该事件发生时开始。默认情况下,将在动画将影响的元素上侦听事件 - 在您的情况下为矩形。 (您可以使用elementID.eventName格式将动画绑定到其他元素。)

添加动画后立即启动动画的技巧是将动画链接到很少使用的DOM-mutation events之一,特别是DOMNodeInserted。这样,当您将动画节点添加为<rect>的子节点时,或者将<rect>添加到SVG时,将立即触发事件和动画。

如果您希望在插入元素和触发动画之间有延迟,请使用eventName + timeOffset格式。

Here is the proof of concept with vanilla JS (as a fiddle)

这是您修改后的代码段; JS代码是相同的,只有角度模板已经改变:

&#13;
&#13;
function Controller($scope) {
  $scope.rects = [];
  var spacing = 5;
  var width = 10;
  var height = 100;
  var x = 10;
  var y = 10;

  $scope.newRect = function() {
    $scope.rects.push({
      x: x,
      y: y,
      width: width,
      height: height
    });
    x += spacing + width;
  };

  for (var i = 0; i < 5; i++) {
    $scope.newRect();
  }

}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app ng-controller="Controller">
  <svg width="1000" height="120">
    <rect ng-repeat="rect in rects" x="{{rect.x}}" y="{{rect.y}}" 
          height="{{rect.height}}" width="{{rect.width}}">
      <animate attributeName="y" from="{{rect.height}}" to="{{rect.y}}" 
               dur="1s" repeatCount="1" begin="DOMNodeInserted"/>
      <animate attributeName="height" from="0" to="{{rect.height}}" 
               dur="1s" repeatCount="1" begin="DOMNodeInserted"/>
    </rect>
  </svg>
  <button ng-click="newRect()">One more</button>
</div>
&#13;
&#13;
&#13;

我不确定你是否有意让条形图从底线开始偏移10px。如果这是偶然的,您可以通过将第一个动画的from值设置为rect.height + rect.y来修复它。

我已经测试了最新Chrome和Firefox中的小提琴。如果您需要支持旧版浏览器,特别是如果您使用SMIL polyfill支持旧版IE,那么您需要进行测试以确保正确抛出DOM突变事件。