1

我正在构建一个包含动画的应用程序,我需要它更好地工作。我希望能够以指定的动画间隔浏览一系列 div 并一次显示一个。我希望每个系列都有自己的速度和自己的 div。

这是我到目前为止所拥有的(也在下面复制):http: //jsfiddle.net/ollerac/shkq7/

基本上,我正在寻找一种将 setInterval 放在 animatedBox 的属性上的方法,这样我就可以使用自定义属性创建一个新的 animatedBox 。但是每次我尝试这样做时,它都会中断。

HTML

<div ng-app ng-controller="BoxController">

    <div class="layer" ng-repeat="layer in animatedBox.layers" ng-style="{ 'backgroundColor': layer.color}" ng-show="layer == animatedBox.selectedLayer"></div>

</div>


JAVASCRIPT

function buildBox () {
    return {
        color: '#' + (Math.random() * 0xFFFFFF << 0).toString(16)
    }
}

function BoxController ($scope) {
    $scope.animatedBox = {
        layers: [],
        selectedLayer: null
    };

    for (var i = 0; i < 5; i++) {
        $scope.animatedBox.layers.push(buildBox());
    }

    var i = -1;
    setInterval(function () {
        $scope.$apply(function() {
            i++;

            if (i < $scope.animatedBox.layers.length) {
                $scope.animatedBox.displayThisLayer = $scope.animatedBox.layers[i];
            } else {
              i = 0;
              $scope.animatedBox.selectedLayer = $scope.animatedBox.layers[i];
            }
        });
    }, 500);
}


CSS

.layer {
    width: 30px;
    height: 30px;
    position: absolute;
}

*更新*

这里有更多关于我想做的事情:

更新 jsFiddle:http: //jsfiddle.net/ollerac/shkq7/2/

function buildBox () {
    return {
        color: '#' + (Math.random() * 0xFFFFFF << 0).toString(16)
    }
}

function BoxController ($scope) {
    $scope.animatedBox = {
        layers: [],
        selectedLayer: null,
        selectedLayerIndex: -1,
        updateSelectedLayer: function () {
            var self = this;

            if (self.layers.length) {
                $scope.$apply(function() {
                    self.selectedLayerIndex++;

                    if (self.selectedLayerIndex < self.layers.length) {
                        self.selectedLayer = self.layers[self.selectedLayerIndex];
                    } else {
                        self.selectedLayerIndex = 0;
                        self.selectedLayer = self.layers[self.selectedLayerIndex];
                    }
                });
            }
        }
    };

    for (var i = 0; i < 5; i++) {
        $scope.animatedBox.layers.push(buildBox());
    }

    setInterval(function () {
        $scope.animatedBox.updateSelectedLayer();
    }, 500);    
}

所以现在对象更新了它自己的selectedLayer属性。但是我仍然需要调用单独调用更新的 setInterval 以使其更新。但我希望对象能够自我更新并完全独立。你能想出一个好方法来做到这一点,因为我真的很挣扎......

我想这更像是一个一般的 javascript 问题,但我认为可能有一种 Angular 方法来处理这种情况,比如使用指令或其他合适的方法。

任何建议将不胜感激。

4

1 回答 1

2

你是对的,我相信指令是正确的解决方案。(顺便说一句,这是一个有趣的工作。:)

当处理这样的问题时,我通常首先编写我希望我能编写的 HTML 和控制器,如果一切正常的话。对于这个例子,这就是我最终得到的。

<div ng-controller="BoxController">
  <div animated-boxes="colors"></div>
</div>
app.value('randomColor', function() {
  var red   = Math.floor(Math.random() * 255);
  var green = Math.floor(Math.random() * 255);
  var blue  = Math.floor(Math.random() * 255);
  return "rgb(" + red + "," + green + "," + blue + ")";
});

app.controller('BoxController', function($scope, randomColor) {
  $scope.colors = [ randomColor(), randomColor() ];
});

在这里,控制器负责设置示波器上的一些基本数据——一组颜色;DOM非常简单,只需将该数组传递给名为animated-boxes. randomColor已移至服务中,因此可以更轻松地重用和测试。(我也对其进行了一些更改,因此它不会导致错误的十六进制值。)

现在,唯一不起作用的部分是这个叫做animated-boxes. 任何时候你想与 DOM 交互,或者在使用 HTML 属性时触发一些行为,我们都会转向指令。

我们将定义我们的指令,注入$timeout服务,因为我们知道我们想做基于计时器的东西。该指令的结果将只是一个对象。

app.directive('animatedBoxes', function($timeout) {
  return {
  };
});

由于我们希望指令是自包含的并且不会弄乱它所包含的外部范围,我们将给它一个隔离范围(有关更多信息,请参阅指令文档,但基本上这只是意味着我们有一个范围不是附加到指令所在的范围,除了通过我们指定的变量。)

由于我们希望访问通过 HTML 属性传递给指令的值,因此我们将在该值上设置双向作用域绑定;我们会调用它colors

app.directive('animatedBoxes', function($timeout) {
  return {
    scope: {
      colors: '=animatedBoxes'
    }
  };
});

我们将给它一个简单的模板,该模板循环并为每种颜色colors输出一个我们的 s。divOurng-show表示只有在范围值等于div时才应该显示,这是循环当前迭代的数组索引。selected$indexng-repeat

app.directive('animatedBoxes', function($timeout) {
  return {
    scope: {
      colors: '=animatedBoxes'
    },
    template: "<div><div class='layer' ng-repeat='color in colors' " +
      "ng-style='{backgroundColor: color}' ng-show='selected == $index'>" +
      "</div></div>"
  };
});

现在是链接函数——处理指令逻辑的函数。首先,我们要跟踪我们展示的是哪个盒子;在我们的 中ng-show,我们用于selected此。我们还想跟踪我们有多少盒子;我们将使用$watch我们指令的范围来跟上这一点。

link: function(scope, elem, attrs) {
  scope.selected = 0;
  var count = 0;

  scope.$watch('colors', function(value) {
    // whenever the value of `colors`, which is the array
    // of colors passed into the directive, changes, update
    // our internal count of colors
    if (value) count = value.length;
    else count = 0; // if `colors` is falsy, set count to 0
  }, true); // `true` ensures we watch the values in the array,
            // not just the object reference
}

最后,我们需要不时地循环浏览每个盒子。我们将使用 来执行此操作$timeout,这是一个setTimeout包含范围$apply调用的版本(它还做了一些其他的事情,但我们现在不关心这个)。

var nextBox = function() {
  if (scope.selected >= count - 1) scope.selected = 0;
  else scope.selected++;
  // recursively use `$timeout` instead of `setInterval`
  $timeout(nextBox, 500);
};

// kick off the directive by launching the first `nextBox`
nextBox();

如果您将到目前为止的整个指令放在一起,您将得到以下代码(已删除注释):

app.directive('animatedBoxes', function($timeout) {
  return {
    scope: {
      colors: '=animatedBoxes'
    },
    template: "<div><div class='layer' ng-repeat='color in colors' " +
      "ng-style='{backgroundColor: color}' ng-show='selected == $index'>" +
      "</div></div>",
    link: function(scope, elem, attrs) {
      scope.selected = 0;
      var count = 0;

      scope.$watch('colors', function(value) {
        if (value) count = value.length;
        else count = 0;
      }, true);

      var nextBox = function() {
        if (scope.selected >= count - 1) scope.selected = 0;
        else scope.selected++;
        $timeout(nextBox, 500);
      };

      nextBox();
    }
  };
});

一个完整的工作示例,包括注释和一个小调试区域,您可以在其中查看它的值colors并与之交互(因此您可以看到指令如何响应控制器中的更改)可以在这里找到:http: //jsfiddle.net /BinaryMuse/g6A6Y/

现在你有了这个,考虑尝试应用这些知识,通过 DOM 传递指令来允许指令具有可变速度,就像我们对colors. 这是我的结果:http: //jsfiddle.net/BinaryMuse/cHHKn/

于 2013-07-06T17:25:39.703 回答