javascript - change - AngularJS: ¿La mejor manera de ver las dimensiones?
page title angular 4 (2)
Desafortunadamente, esta pregunta no recibió tanta atención como me hubiera gustado ... Y no tengo tiempo para escribir una explicación detallada con antecedentes, capturas de pantalla del generador de perfiles, etc. Pero encontré una solución, y espero que esto ayude alguien más lidiando con el mismo problema.
Conclusión
El bucle $ digest en cualquier aplicación mediana-grande realista no podrá manejar una actualización de 100 ms.
Dado el requisito de setInterval
, simplemente pasé por alto el bucle por completo, en lugar de optar por transmitir un cambio de estado solo cuando se detectan diferentes dimensiones (utilizando las propiedades nativas offsetWidth/Height
).
La ejecución en un intervalo de 100 ms con un solo $root
manifiesto de alcance de $root
está dando los mejores resultados de todo lo que he probado, con un ~ 10.2% de CPU de tabulación activa en mi 2.4 GHz i7 2013 MBP: aceptable comparado con el ~ 84% con $interval
Cualquier comentario y crítica son bienvenidos, de lo contrario, ¡aquí está la directiva independiente! Obviamente, puede ser creativo con el alcance y / o los atributos para personalizar a los observadores, pero con el interés de permanecer en el tema, he tratado de omitir cualquier código superfluo.
Supervisará y vinculará los cambios de tamaño en un elemento a una propiedad de alcance de su elección, para N elementos con una complejidad lineal. No puedo garantizar que sea la forma más rápida / mejor de hacer esto, pero es la implementación más rápida que pude desarrollar rápidamente lo que hace un seguimiento de los cambios de dimensión a nivel de DOM agnósticos:
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);
}
};
}]);
Entonces, he encontrado algunas soluciones para esto y todavía no estoy seguro de cuál es la mejor. Primero, para referencia, hay una pregunta similar que pude encontrar, aunque es un poco vieja. Aquí está para cualquiera que lea esto más tarde: Observando cambios de dimensión en Angular
La meta
Tengo partes de mi aplicación en las que se necesitan elementos de altura sensibles. Quiero la forma más rápida y visualmente atractiva de hacer esto en la capa UI / directiva sin la necesidad de eventos de cambio emitidos explícitamente.
Opción uno: Directiva
Una directiva simple puede registrar las dimensiones en cada bucle de resumen (y cambiar el tamaño del evento)
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);
}
};
Problema: el cambio no se propaga lo suficientemente rápido y se observa un retraso visual.
Solución: usando una llamada de intervalo
Opción Dos: $ intervalo
Intenté lo mismo con una llamada de intervalo $ y funcionó, pero el uso de la CPU fue sorprendentemente alto, incluso después de invertir el control y hacer un seguimiento de los elementos en una colección raíz simple observada por valor (evitando temporizadores concurrentes producidos por múltiples instancias de la directiva ).
Aparte de algún problema relacionado con GC en mi entorno (no veo nada que sugiera esto actualmente), ¿podría haber un mejor enfoque para crear este tipo de diseño fluido?
Solución propuesta / Pregunta
Mi primer pensamiento sería una forma de crear un bucle de digestión de $ concurrentes, para monitorear de manera eficiente el DOM como un todo para cualquier cambio visual. ¿Es posible iterar de manera eficiente a través de todos los estilos computados, por ejemplo, para producir un hash barato que se puede observar por valor? ¿Algo que se puede activar de forma relativamente económica cada vez que se agrega y / o cambia un estilo computarizado relevante?
Antes de construirlo y crear un perfil, ¿podría alguien comentar si es incluso realista, o si simplemente tiene más sentido abstraer / refactorizar los desencadenantes de cambio de tamaño en primer lugar?
¿Alguna otra idea sobre la forma preferida de lograr esto en 1.2.9 ?
[editar] Una pregunta alternativa, tal vez más simple: ¿ es incluso posible proporcionar una tasa de actualización realista de 1-200 ms a través de Javascript de una manera computacionalmente eficiente? Si es así, ¿sería el intervalo de $ de Angular o podría ser más eficiente una implementación ''VanillaJS''?
Echa un vistazo a http://snapjay.github.io/angularjs-breakpoint/
que hace mucho de lo que necesitas, creo. Puedes ver el ancho de la ventana y así sucesivamente. No estoy seguro de que funcione también en la altura, pero se podría extender.