AngularJS: Лучший способ посмотреть размеры?

23

Итак, я придумал несколько решений для этого, и я все еще не совсем уверен, что лучше. Сначала для справки есть один подобный вопрос, который я мог найти, хотя он немного старый. Здесь это для тех, кто читает это позже: Просмотр изменений размеров в Angular

Цель

У меня есть часть моего приложения, в котором требуются элементы повышенной чувствительности. Я хочу, чтобы самый быстрый, наиболее привлекательный способ сделать это на уровне интерфейса/указателя без необходимости явно транслируемых событий изменения.

Вариант один: директива

Простая директива может записывать размеры в каждый цикл дайджеста (и изменять размер)

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, и это сработало, но использование процессора было неожиданно высоким даже после того, как я инвертировал управление и отслеживал элементы в простой корневой коллекции, наблюдаемой по значению (избегая одновременных таймеров, созданных несколькими экземплярами директивы).

Помимо некоторой проблемы, связанной с GC, в моей среде (я не вижу ничего, что говорит об этом в настоящее время), может быть, есть лучший подход к созданию такого рода компоновки жидкости?

Предлагаемое решение/вопрос

Моя первая мысль - это способ создать параллельный цикл $digest, чтобы эффективно отслеживать DOM в целом для любых визуальных изменений. Можно ли эффективно выполнять итерацию по всем вычисленным стилям, например, для получения дешевого хэша, который можно наблюдать по значению? Что-то, что может быть вызвано относительно недорогим каждый раз, когда соответствующий вычисленный стиль добавляется и/или изменяется?

Прежде чем я буду создавать и профилировать его, может ли кто-нибудь прокомментировать, насколько он реалистичен или просто имеет смысл абстрагироваться/реорганизовывать триггеры изменения размера в первую очередь?

Любые другие идеи относительно предпочтительного способа выполнить это в 1.2.9?

[править] Альтернативный, возможно, более простой вопрос: можно ли обеспечить реалистичную частоту обновления 1-200 мс с помощью Javascript с помощью вычислительной эффективности? Если это так, будет ли это путь Angular $или может быть эффективнее реализация "VanillaJS"?

  • 0
    Имея двойной слушатель ( $watch и window.onresize ), не возникает ли проблем с циклом параллелизма / зацикливания между этими двумя? Спасибо
Теги:
dom
angularjs-directive

2 ответа

10
Лучший ответ

К сожалению, этот вопрос не получил такого большого внимания, как мне бы хотелось... И у меня нет времени, чтобы написать подробное объяснение с фоном, скриншотами профайлера и т.д. Но я нашел решение, и я надеюсь, что это поможет кому-то другому заниматься одной и той же проблемой.


Заключение

Цикл $digest в любом реалистичном среднесрочном приложении не сможет обрабатывать обновление 100 мс.

Учитывая требование setInterval, я просто полностью обходил цикл, вместо этого предпочитая транслировать изменение состояния только при обнаружении различных измерений (используя собственные свойства offsetWidth/Height).

Запуск с интервалом 100 мс с единственным манифестом области $root дает наилучшие результаты всего, что я тестировал, с ~ 10,2% активной вкладкой CPU на моем 2.4Ghz i7 2013 MBP - приемлемым по сравнению с ~ 84% при $interval.

Любые комментарии и критические замечания приветствуются, в противном случае - это автономная директива! Очевидно, что вы можете получить креатив с областью действия и/или атрибутами для настройки наблюдателей, но в интересах оставаться на теме я попытался опустить лишний код.

Он будет контролировать и связывать изменения размера элемента в свойстве области по вашему выбору, для 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);

    }
  };
}]);
  • 0
    Эй, просто быстро, как это используется в HTML <div ng-size = "??"> </ div>, чтобы не знать, что поместить в цитаты
0

Отъезд http://snapjay.github.io/angularjs-breakpoint/

который делает много чего вам нужно, я думаю. Вы можете посмотреть ширину окна и так далее. Я не уверен, что он работает на высоте, но вы можете расширить его.

  • 0
    Но это не делает ничего, кроме базовой отзывчивости (размер по горизонтали / размеру окна), что, честно говоря, проще реализовать только с помощью медиазапросов Bootstrap и CSS ... Цель состоит в том, чтобы отслеживать размеры динамически расположенных элементов в режиме реального времени с помощью наибольшая вычислительная эффективность (скажем, визуализированная группа из ~ 10 - 200 узлов)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню