AngularJS: Best way to watch dimensions?

后端 未结 2 1297
太阳男子
太阳男子 2021-02-04 01:30

So, I\'ve come up with a few solutions to this and I\'m still not quite sure what is best. First for reference there is one similar question I could find, though it\'s a bit ol

2条回答
  •  深忆病人
    2021-02-04 01:46

    Unfortunately this question didn't get as much attention as I would have liked... And I don't have time to write a detailed explanation with background, profiler screenshots, etc. But I did find a solution, and I hope this helps someone else dealing with the same issue.


    Conclusion

    The $digest loop in any realistic medium-large application will not be able to handle a refresh of 100ms.

    Given the requirement of setInterval I simply bypassed the loop entirely, instead opting to broadcast a state change only when differing dimensions are detected (using the offsetWidth/Height native properties).

    Running at a 100ms interval with a single $root scope manifest is giving the best results of anything I've tested, with ~10.2% active-tab CPU on my 2.4Ghz i7 2013 MBP--acceptable compared to the ~84% with $interval.

    Any comments and critiques are welcome, otherwise, here is the self-contained directive! Obviously you can get creative with the scope and/or attributes to customize the watchers, but in the interest of staying on topic I've tried to omit any superfluous code.

    It will monitor & bind size changes on an element to a scope property of your choosing, for N elements with a linear complexity. I can't guarantee it's the fastest/best way to do this, but it's the fastest implementation I could develop quickly that tracks state-agnostic DOM-level dimension changes:

    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);
    
        }
      };
    }]);
    

提交回复
热议问题