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

标签 angularjs animation svg smil

我正在生成一些<rect><animate> children 使用 ng-repeat 指令。

页面加载时,动画会正确触发,一切都会按预期发生。 但是,当我添加新的<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>

加载示例后,4 <rect>将会出现,从下到上有动画效果。然而,当按下“另一个”按钮时,新的 <rect>将在没有动画的情况下添加(在 Firefox 35 和 Chrome 38 上测试行为)。

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

最佳答案

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

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

编辑添加:

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

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

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

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

这是您修改后的代码片段; JS 代码是相同的,只是 Angular 模板发生了变化:

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>

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

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

关于angularjs - SVG ng-repeat'ed 元素上的 SMIL 动画仅在页面加载时发生,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26614750/

相关文章:

angularjs - HTML 未使用 $sce 和 ng-bind-html 在 Salesforce 上呈现

css - 在 CSS 伪元素中引用内联 SVG?

SVG animateTransform 翻译和缩放同时失败

javascript - $index 不存在于 ng-repeat 中 - 答案在我原来的帖子中

angularjs - 使用 Angular 选择时的条件 ng-options

javascript - 如何从 Angular Controller 中调用 ngDirective?

javascript - 从中心点开始的 SVG 路径动画

javascript - Nexus 5 网站 - 设备内的滚动 slider - 怎么做?

javascript - 动画元素,但保持容器居中

javascript - Base64 SVG 图像