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

在手機上打開頁面時,常常會由於網絡很差致使須要較長的加載時間,若是這段時間內只是顯示一個「白板」,用戶體驗很是很差。一般的解決方案就是完整打開頁面前給用戶展現一個加載動畫,讓用戶可以看到頁面還活着呢。本覺得是個很簡單的活,真作起來才發現【有很多學問】!css

1、首先要搞清楚的問題是,爲何加載過程當中會出現」白板「,下面用Chrome開發者工具的Timeline分析面板作了幾個實驗。

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

二、加載CSS
圖片描述
加載外部的CSS文件會阻止渲染,不論link的標籤放在什麼位置上。
順序:加載、解析、加載(阻塞了渲染)、渲染、繪製瀏覽器

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

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

四、在<script>標籤中動態建立link標籤加載CSS
圖片描述
加載CSS中斷了頁面的渲染和繪製框架

五、在<script>標籤中經過setTimeout函數動態建立link標籤加載CSS
圖片描述
加載CSS沒有中斷。我理解這是由於JS是單線程的,放在timeout裏建立的link去排隊,瀏覽器就先無論它了。異步

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

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

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

外部文件的動態加載問題不少文章都深刻分析過了,簡單說,就是用異步加載,可是要考慮到各個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;}

3、還能夠作什麼?

能夠考慮頁面的佈局也動態加載,這樣用戶能夠先看見頁面的框架,而後再獲取數據填到框架中。可是尚未想到成熟的解決方案。

相關文章
相關標籤/搜索