2

我正在使用 ng-repeat 指令生成一些<rect>带有<animate>孩子的 s。

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

以下代码片段演示了这种行为:

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

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

加载示例后,<rect>将出现 4,动画从底部到顶部。但是,当按下“再一个”按钮时,<rect>将添加新的而不带动画(在 Firefox 35 和 Chrome 38 上测试的行为)。

如何触发 new <rect>s 的动画?

4

1 回答 1

6

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

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

编辑添加:

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

让动画在添加后立即开始的技巧是将其链接到很少使用的DOM 突变事件之一,特别是DOMNodeInserted. 这样,当您将动画节点添加为 的子节点<rect>时,或者将 添加<rect>到 SVG 时,将立即触发事件和动画。

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

这是 vanilla JS(作为小提琴)的概念证明

这是您修改后的代码段;JS代码是一样的,只是角度模板发生了变化:

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

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

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

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

于 2014-10-28T22:19:35.760 回答