實現頁面加載等待動畫很簡單嗎?


在手機上打開頁面時,經常會因為網絡不好導致需要較長的加載時間,如果這段時間內只是顯示一個“白板”,用戶體驗非常不好。通常的解決方案就是完整打開頁面前給用戶展示一個加載動畫,讓用戶能夠看到頁面還活着呢。本以為是個很簡單的活,真做起來才發現【有不少學問】!

一、首先要搞清楚的問題是,為什么加載過程中會出現”白板“,下面用Chrome開發者工具的Timeline分析面板做了幾個實驗。

1、不加載資源文件
圖片描述
順序:加載、解析、渲染、繪制

2、加載CSS
圖片描述
加載外部的CSS文件會阻止渲染,不論link的標簽放在什么位置上。
順序:加載、解析、加載(阻塞了渲染)、渲染、繪制

3、加載JS
a、在body前加載外部的JS文件會阻止渲染
圖片描述
順序:加載、解析、渲染(?)、加載(阻塞)、解析(JS執行完多了個解析)、渲染、繪制

b、在body后加載外部的JS文件不會阻止渲染
圖片描述
順序:加載、解析、渲染、繪制、加載、解析、渲染、繪制

4、在<script>標簽中動態創建link標簽加載CSS
圖片描述
加載CSS中斷了頁面的渲染和繪制

5、在<script>標簽中通過setTimeout函數動態創建link標簽加載CSS
圖片描述
加載CSS沒有中斷。我理解這是因為JS是單線程的,放在timeout里創建的link去排隊,瀏覽器就先不管它了。

二、基於上面的測試,實現頁面加載動畫理想的方式是什么?

先定好目標:盡快讓用戶看到變化,不要讓用戶以為頁面已經不響應,再逐步加載內容。
最快的方式就是做一個空的頁面,不加載任何外部資源(包括:CSS和JS)。頁面上方加載動畫的CSS定義和頁面元素,提供異步加載頁面元素、CSS和JS文件的JS。通過JS加載各類資源成功后關閉動畫效果,清除不必要的內容。

這樣就來個新問題,如何實現動態加載問題?

外部文件的動態加載問題很多文章都深入分析過了,簡單說,就是用異步加載,但是要考慮到各個JS文件的依賴關系問題。綜合比較了一下,決定requirejs實現動態加載。因為最近一直用angular,所以下面的代碼是require.js+angular。

HTML

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta content="width=device-width,user-scalable=no,initial-scale=1.0" name="viewport">
        <base href='/'>
        <title>loading</title>
        <style id='loadingStyle' type="text/css">
            /*省略了,這里是loading元素轉圈的樣式,為了快應該壓縮了再放里面*/
        </style>
    </head>
    <body>
        <div id="content" ng-app="app" ng-controller="ctrl">
            <ul class='list-group'>
                <li class='list-group-item' ng-repeat="d in data"><i ng-bind="d"></i></li>
            </ul>
        </div>
        <div class="loading"><div class='loading-indicator'><i></i></div></div>
        <script src="static/js/require.js" defer async data-main="test/loading/loader.js?_=11"></script>
    </body>
</html>

loader.js
因為angular不是AMD,所以用shim引用成angular全局變量

require.config({
    paths: {
        "angular": "/static/js/angular.min",
    },
    shim: {
        "angular": {
            exports: "angular"
        },
    },
    deps: ['/test/loading/app.js?_=10']
});

app.js
如果真的要動態添加樣式,建議先獲得數據,把數據展現出來,在加載樣式,這樣就能讓用戶今早看到變化。

define(["angular"], function(angular) {
    'use strict';
    angular.module('app', []).controller('ctrl', ['$scope', '$timeout', function($scope, $timeout) {
        $scope.data = [];
        // 模擬長時間獲得數據
        $timeout(function() {
            for (var i = 0; i < 100; i++) {
                $scope.data.push('data:' + i);
            }
            // 模擬長時間獲得樣式
            $timeout(function() {
                var link, head;
                link = document.createElement('link');
                link.href = "/test/loading/app.css?_=" + (new Date()).getTime();
                link.rel = 'stylesheet';
                link.onload = function() {
                    var eleLoading, eleStyle;
                    eleLoading = document.querySelector('.loading');
                    eleLoading.parentNode.removeChild(eleLoading);
                    eleStyle = document.querySelector('#loadingStyle');
                    eleStyle.parentNode.removeChild(eleStyle);
                };
                head = document.querySelector('head');
                head.appendChild(link);
            }, 2000);
        }, 2000);
    }]);
});

app.css

#content{color:red;}

三、還可以做什么?

可以考慮頁面的布局也動態加載,這樣用戶可以先看見頁面的框架,然后再獲取數據填到框架中。但是還沒有想到成熟的解決方案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM