AngularJS:观看尺寸的最佳方式?

时间:2014-08-21 23:08:01

标签: javascript css angularjs dom angularjs-directive

所以,我已经提出了一些解决方案,我仍然不太确定什么是最好的。首先参考我可以找到一个类似的问题,虽然它有点旧。这是以后阅读此内容的任何人:Watching dimension changes in Angular

目标

我的应用程序部分需要响应高度元素。我想要在UI /指令层执行此操作的最快,最具视觉吸引力的方式,而无需显式广播更改事件。

选项一:指令

一个简单的指令可以记录每个摘要循环的维度(和调整大小事件)

return {
  scope: {
    size: '=ngSize'
  },
  link: function($scope, element, attrs) {

    var windowEl = angular.element($window),
        handler  = function() {
          if ($scope.size &&
              element.outerWidth() === $scope.size.width &&
              element.outerHeight() === $scope.size.height) return false;
          $scope.size = {
            width: element.outerWidth(),
            height: element.outerHeight()
          };
        };

    windowEl.on('resize', function() {
      $scope.$apply(handler);
    });

    $root.$watch(function() { return [element.outerWidth(), element.outerHeight()] }, handler, true);

  }
};

问题:更改传播速度不够快,视觉滞后很明显。

解决方案:使用间隔调用


选项二:$ interval

我尝试使用$ interval调用同样的事情并且它工作正常,但CPU使用率却出乎意料地高,即使在我反转控制并跟踪由值监视的简单根集合中的元素(避免多个实例生成的并发计时器)指令)。

除了我环境中与GC相关的一些问题(我目前没有看到任何暗示这一点),是否有更好的方法来创建这种流畅的布局?

提出的解决方案/问题

我的第一个想法是创建一个并发的$ digest循环,以便有效地监视整个DOM以进行任何视觉变化。是否有可能有效地迭代所有计算的样式,例如,生成可以按值观察的廉价哈希?每次添加和/或更改相关的计算样式时,可以相对便宜地触发的东西?

在我构建和描述它之前,有人可以评论它是否真实,或者是否首先抽象/重构调整大小触发器更有意义?

关于在 1.2.9 中实现此目标的首选方法的任何其他想法?

[edit] 一个替代的,也许更简单的问题:甚至可以通过Javascript以计算效率的方式提供1-200毫秒的真实刷新率?如果是这样,那会是Angular的$ interval,还是'VanillaJS'实现更有效?

2 个答案:

答案 0 :(得分:10)

不幸的是,这个问题没有得到我所希望的那么多的关注......而且我没有时间用背景,剖析器截图等写出详细的解释。但我确实找到了解决方案,我希望这有助于其他人处理同样的问题。


结论

任何现实的大中型应用程序中的$ digest循环都无法处理100ms的刷新。

鉴于setInterval的要求,我只是完全绕过了循环,而只是在检测到不同维度时选择广播状态更改(使用offsetWidth/Height原生属性)。

使用单个$root范围清单以100毫秒的间隔运行,可以为我测试的任何内容提供最佳结果,我的2.4Ghz i7 2013 MBP上有~10.2%的活动标签CPU - 可接受与使用$interval的~84%进行比较。

欢迎任何评论和批评,否则,这是一个独立的指令!显然,你可以通过范围和/或属性来创造性地定制观察者,但为了保持主题,我试图省略任何多余的代码。

它将监控&对于具有线性复杂度的N个元素,将元素的大小更改绑定到您选择的范围属性。我不能保证它是最快/最好的方法,但它是我能够快速开发的最快的实现,跟踪与状态无关的DOM级维度更改:

app.directive('ngSize', ['$rootScope', function($root) {
  return {
    scope: {
        size: '=ngSize'
    },
    link: function($scope, element, attrs) {

        $root.ngSizeDimensions  = (angular.isArray($root.ngSizeDimensions)) ? $root.ngSizeDimensions : [];
        $root.ngSizeWatch       = (angular.isArray($root.ngSizeWatch)) ? $root.ngSizeWatch : [];

        var handler = function() {
            angular.forEach($root.ngSizeWatch, function(el, i) {
                // Dimensions Not Equal?
                if ($root.ngSizeDimensions[i][0] != el.offsetWidth || $root.ngSizeDimensions[i][1] != el.offsetHeight) {
                    // Update Them
                    $root.ngSizeDimensions[i] = [el.offsetWidth, el.offsetHeight];
                    // Update Scope?
                    $root.$broadcast('size::changed', i);
                }
            });
        };

        // Add Element to Chain?
        var exists = false;
        angular.forEach($root.ngSizeWatch, function(el, i) { if (el === element[0]) exists = i });

        // Ok.
        if (exists === false) {
            $root.ngSizeWatch.push(element[0]);
            $root.ngSizeDimensions.push([element[0].offsetWidth, element[0].offsetHeight]);
            exists = $root.ngSizeWatch.length-1;
        }

        // Update Scope?
        $scope.$on('size::changed', function(event, i) {
            // Relevant to the element attached to *this* directive
            if (i === exists) {
                $scope.size = {
                    width: $root.ngSizeDimensions[i][0],
                    height: $root.ngSizeDimensions[i][1]
                };
            }
        });

        // Refresh: 100ms
        if (!window.ngSizeHandler) window.ngSizeHandler = setInterval(handler, 100);

        // Window Resize?
        // angular.element(window).on('resize', handler);

    }
  };
}]);

答案 1 :(得分:0)

查看http://snapjay.github.io/angularjs-breakpoint/

我认为你需要做很多事情。您可以观看窗口宽度等。我不确定它是否也适用于高度,但你可以扩展它。