Итак, я придумал несколько решений для этого, и я все еще не совсем уверен, что лучше. Сначала для справки есть один подобный вопрос, который я мог найти, хотя он немного старый. Здесь это для тех, кто читает это позже: Просмотр изменений размеров в 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"?
К сожалению, этот вопрос не получил такого большого внимания, как мне бы хотелось... И у меня нет времени, чтобы написать подробное объяснение с фоном, скриншотами профайлера и т.д. Но я нашел решение, и я надеюсь, что это поможет кому-то другому заниматься одной и той же проблемой.
Цикл $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);
}
};
}]);
Отъезд http://snapjay.github.io/angularjs-breakpoint/
который делает много чего вам нужно, я думаю. Вы можете посмотреть ширину окна и так далее. Я не уверен, что он работает на высоте, но вы можете расширить его.
$watch
иwindow.onresize
), не возникает ли проблем с циклом параллелизма / зацикливания между этими двумя? Спасибо