使用AngularJS显示慢速脚本的加载动画?

Hor*_*ren 31 javascript angularjs

在angularjs 1.2中,像过滤ng-repeat多行(> 2,000行)的操作可能变得非常慢(> 1秒).
我知道我可以使用limitTo分页,自定义过滤器等来优化执行时间,但我仍然有兴趣知道在浏览器忙于运行长脚本时是否可以显示加载动画.

在角度的情况下,我认为可以在$digest运行时调用,因为这似乎是占用大部分时间的主要功能,并且可能被多次调用.

在一个相关的问题中,没有给出有用的答案.任何帮助非常感谢!

Nik*_*los 20

问题是只要Javascript正在执行,UI就没有机会更新.即使您过滤之前呈现微调器,只要Angular忙,它就会显示为"冻结".

克服这种情况的一种方法是过滤块,如果有更多数据可用,则在小块后再次过滤$timeout.超时使UI线程有机会运行并显示更改和动画.

这里展示了这个原则的小提琴.

它不使用Angular的过滤器(它们是同步的).相反,它data使用以下函数过滤数组:

function filter() {
    var i=0, filtered = [];
    innerFilter();

    function innerFilter() {
        var counter;
        for( counter=0; i < $scope.data.length && counter < 5; counter++, i++ ) {
            /////////////////////////////////////////////////////////
            // REAL FILTER LOGIC; BETTER SPLIT TO ANOTHER FUNCTION //
            if( $scope.data[i].indexOf($scope.filter) >= 0 ) {
                filtered.push($scope.data[i]);
            }
            /////////////////////////////////////////////////////////
        }
        if( i === $scope.data.length ) {
            $scope.filteredData = filtered;
            $scope.filtering = false;
        }
        else {
            $timeout(innerFilter, 10);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它需要2个支持变量:$scope.filteringtrue当过滤器处于活动状态,给我们以显示微调和禁止输入的机会; $scope.filteredData接收过滤器的结果.

有3个隐藏参数:

  • 块大小(counter < 5)的目的很小,以证明效果
  • 超时延迟($timeout(innerFilter, 10))应该很小,但足以给UI线程一些时间来响应
  • 实际的过滤逻辑,应该是现实生活场景中的回调.

这只是概念的证明; 我建议重构它(可能是指令)以供实际使用.


Onu*_*rım 7

以下是步骤:

  1. 首先,您应该使用CSS动画.没有JS驱动的动画和GIF应该在繁重的过程中使用.单线程限制.动画将冻结.CSS动画与UI线程分开,IE 10+和所有主流浏览器都支持它们.
  2. 写一个指令并将它放在你ng-viewfixed定位之外.
  3. 使用一些特殊标志将其绑定到您的app控制器.
  4. 在长/重过程之前和之后切换此指令的可见性.(您甚至可以将文本消息绑定到指令以向用户显示一些有用的信息). - 在一个繁重的过程循环中直接与这个或任何其他东西进行交互将花费更长的时间来完成.这对用户来说很糟糕!

指令模板:

<div class="activity-box" ng-show="!!message">
    <img src="img/load.png" width="40" height="40" />
    <span>{{ message }}</span>
</div>
Run Code Online (Sandbox Code Playgroud)

activity 指示:

具有单个属性的简单指令message.请注意ng-show上面模板中的指令.在message既用来切换活动的指标,并设置用户的信息文本.

app.directive('activity', [
    function () {
        return {
            restrict: 'EA',
            templateUrl: '/templates/activity.html',
            replace: true,
            scope: {
                message: '@'
            },
            link: function (scope, element, attrs) {}
        };
    }
]);
Run Code Online (Sandbox Code Playgroud)

SPA HTML:

<body ng-controller="appController">
    <div ng-view id="content-view">...</div>
    <div activity message="{{ activityMessage }}"></div>
</body>
Run Code Online (Sandbox Code Playgroud)

注意,activity指令放在外面ng-view.它将在您的单页面应用的每个部分提供.

APP控制器:

app.controller('appController',
    function ($scope, $timeout) {
        // You would better place these two methods below, inside a 
        // service or factory; so you can inject that service anywhere
        // within the app and toggle the activity indicator on/off where needed
        $scope.showActivity = function (msg) {
            $timeout(function () {
                $scope.activityMessage = msg;
            });
        };
        $scope.hideActivity = function () {
            $timeout(function () {
                $scope.activityMessage = '';
            }, 1000); // message will be visible at least 1 sec
        };
        // So here is how we do it:
        // "Before" the process, we set the message and the activity indicator is shown
        $scope.showActivity('Loading items...');
        var i;
        for (i = 0; i < 10000; i += 1) {
            // here goes some heavy process
        }
        // "After" the process completes, we hide the activity indicator.
        $scope.hideActivity();
    });
Run Code Online (Sandbox Code Playgroud)

当然,你也可以在其他地方使用它.例如,你可以$scope.hideActivity();在承诺解决时打电话.或切换上的活动requestresponsehttpInterceptor是一个好主意了.

CSS示例:

.activity-box {
    display: block;
    position: fixed; /* fixed position so it doesn't scroll */
    z-index: 9999; /* on top of everything else */
    width: 250px;
    margin-left: -125px; /* horizontally centered */
    left: 50%;
    top: 10px; /* displayed on the top of the page, just like Gmail's yellow info-box */
    height: 40px;
    padding: 10px;
    background-color: #f3e9b5;
    border-radius: 4px;
}

/* styles for the activity text */
.activity-box span {
    display: block;
    position: relative;
    margin-left: 60px;
    margin-top: 10px;
    font-family: arial;
    font-size: 15px;
}

/* animating a static image */
.activity-box img {
    display: block;
    position: absolute;
    width: 40px;
    height: 40px;
    /* Below is the key for the rotating animation */
    -webkit-animation: spin 1s infinite linear;
    -moz-animation: spin 1s infinite linear;
    -o-animation: spin 1s infinite linear;
    animation: spin 1s infinite linear;
}

/* keyframe animation defined for various browsers */
@-moz-keyframes spin {
    0% { -moz-transform: rotate(0deg); }
    100% { -moz-transform: rotate(359deg); }
}
@-webkit-keyframes spin {
    0% { -webkit-transform: rotate(0deg); }
    100% { -webkit-transform: rotate(359deg); }
}
@-o-keyframes spin {
    0% { -o-transform: rotate(0deg); }
    100% { -o-transform: rotate(359deg); }
}
@-ms-keyframes spin {
    0% { -ms-transform: rotate(0deg); }
    100% { -ms-transform: rotate(359deg); }
}
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(359deg); }
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.


Sir*_*How 5

使用spin.js和网站http://fgnass.github.com/spin.js/显示了非常简单的步骤.加载动画在CSS中,与UI线程分开,因此可以顺利加载.