[Dd]enzow(ill)? with DB and Python

DBとか資格とかPythonとかの話をつらつらと

CSSを使用したローディング画面を実装したかった

CSSでローディング画面を追加しないといけなくなって色々調べて悩んだので、まとめておきます。こういうのです。

https://gyazo.com/740eca7d44cfdeac1275982a9bbe7c18

つまり

いいたいことは以下のHTMLで全てです。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ローディングサンプル</title>
    <style>
    /* ローディングアイコンを含んだ全体の領域 */
    #base-area{
        position: relative; /* これを指定することでローディングアイコンの起点にする */
        display: flex; /*ここから下はただレイアウト整えるだけ必須ではない*/
        justify-content: center;
        flex-direction: column;
    }

    /* ローディングアイコン */
    #loading {
        position: absolute; /* 親要素からの相対位置になるようにする */
        z-index: 99; /* z軸を上げる */
        top: 0;  /* marginまでセットでいい感じに親要素の真ん中になる */
        bottom: 0;
        left: 0;
        right: 0;
        margin: auto;
        width: 50px; /* ローディングアイコンのサイズになる */
        height: 50px;
        border-radius: 50%; /* 50% 丸めるとまるくなる */
        border: 8px solid #00FF00;  /* 先の太さや色になる */
        border-right-color: transparent;  /* 右側を透過することで1/4だけ空いた円になる */
        animation: spin 1s linear infinite;  /* 1秒ごとのアニメーションの指定 */
    }

    /* アニメーションの設定 */
    @keyframes spin {
    0% {
        transform: rotate(0deg); /* 0度回す */
    }
    50% {
        transform: rotate(180deg);  /* 180度回す */
    }
    100% {
        transform: rotate(360deg);  /* 360度回す */
    }
    }

    #loading-button {
        height: 30px;
    }

    </style>
</head>
<body>
<h1 style="text-align:center;">サンプルローディング画面</h1>

<div id='base-area'>
    <!-- 初期時点ではローディングアイコンは非表示にする -->
    <div id='loading' style='display:none;'></div>

    <!-- このボタンを押したらロードして入力欄が更新されることにする -->
    <button id='loading-button' type='button'>リロード</button>

    <!-- 更新される入力欄 -->
    <textarea id='input-area' rows=20 cols=30 placeholder="サンプル入力欄"></textarea>
</div>

<script>
    // ボタンを押した際の処理を追加
    var buttonNode = document.querySelector('#loading-button');
    buttonNode.addEventListener('click', function(e){
        // ボタン押された時点でローディングを表示
        showLoding();
        // 本来はここでWEB APIを呼び出して結果をまつ
        fetch('/')
            .then(function(res){
                // 結果を得たらローディングを消す
                // ローディングが見えるように今回は1秒ずらしている
                setTimeout(hideLoading, 1000);
            });
    }, false);

    function showLoding(){
        var loadingNode = document.querySelector('#loading');
        loadingNode.style.display = '';
    }

    function hideLoading(){
        var loadingNode = document.querySelector('#loading');
        loadingNode.style.display = 'none';
    }
</script>
</body>
</html>

コメントみれば分かる話ですが、もう少し順をおってみていきます。

ローディングアイコン

CSSでローディングアイコンを作るには、まず丸いノードを作ります。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ローディングサンプル</title>
    <style>
    /* ローディングアイコン */
    #loading {
        width: 50px; /* ローディングアイコンのサイズになる */
        height: 50px;
        border: 8px solid #00FF00;  /* 先の太さや色になる */
    }
    </style>
</head>
<body>
    <div id='loading'></div>
</body>
</html>

こうすると、<div id='loading'></div>の部分が以下の様に太枠の四角になります。

f:id:denzow:20180124073812p:plain

続いてborder-right-color: transparent;を追加すると右辺が消えます。

f:id:denzow:20180124073844p:plain

さらにborder-radius: 50%;で一片の空いた丸ができます。

f:id:denzow:20180124073857p:plain

これでローディング時に回すアイコンができました。次は回しましょう。animation: spin 1s linear infinite;をまず追加します。1s部分はアニメーションのサイクルを何秒で回すか、linearは処理の速度を指定するもので、一定の速度でアニメーションを行うというものです。またinfiniteはアニメーションを無限にループするようにする指定です。

この指定に基づきspinというアニメーションが適用されます。合わせて指定すると以下のようになります。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ローディングサンプル</title>
    <style>
    /* ローディングアイコン */
    #loading {
        width: 50px; /* ローディングアイコンのサイズになる */
        height: 50px;
        border: 8px solid #00FF00;  /* 先の太さや色になる */
        border-right-color: transparent;
        border-radius: 50%;
        animation: spin 1s linear infinite;  /* 1秒ごとのアニメーションの指定 */
    }
    /* アニメーションの設定 */
    @keyframes spin {
    0% {
        transform: rotate(0deg); /* 0度回す */
    }
    50% {
        transform: rotate(180deg);  /* 180度回す */
    }
    100% {
        transform: rotate(360deg);  /* 360度回す */
    }
    }
    </style>
</head>
<body>
    <div id='loading'></div>
</body>
</html>

これでぐるぐる回る円ができました。あとはこれをタイミングに合わせて表示、非表示を切り替えるだけでローディング画面が実現できます。

ローディングのタイミング

大体は外部APIに処理を投げてその結果を待っている間に表示することが多いと思います。そのため、以下のようにボタンが押された時点でまずローディングアイコンを表示し、APIからレスポンスが戻った際のコールバックの中でローディングアイコンを非表示にすれば良いわけです。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ローディングサンプル</title>
    <style>
    /* ローディングアイコン */
    #loading {
        width: 50px; /* ローディングアイコンのサイズになる */
        height: 50px;
        border: 8px solid #00FF00;  /* 先の太さや色になる */
        border-right-color: transparent;
        border-radius: 50%;
        animation: spin 1s linear infinite;  /* 1秒ごとのアニメーションの指定 */
    }

    /* アニメーションの設定 */
    @keyframes spin {
    0% {
        transform: rotate(0deg); /* 0度回す */
    }
    50% {
        transform: rotate(180deg);  /* 180度回す */
    }
    100% {
        transform: rotate(360deg);  /* 360度回す */
    }
    }
    </style>
</head>
<body>
    
    <button id='loading-button' type='button'>リロード</button>
    <div id='loading' style="display:none"></div>
    
    <script>
            // ボタンを押した際の処理を追加
            var buttonNode = document.querySelector('#loading-button');
            buttonNode.addEventListener('click', function(e){
                // ボタン押された時点でローディングを表示
                showLoding();
                // 本来はここでWEB APIを呼び出して結果をまつ
                fetch('/')
                    .then(function(res){
                        // 結果を得たらローディングを消す
                        // ローディングが見えるように今回は1秒ずらしている
                        setTimeout(hideLoading, 1000);
                    });
            }, false);
            function showLoding(){
                var loadingNode = document.querySelector('#loading');
                loadingNode.style.display = '';
            }
        
            function hideLoading(){
                var loadingNode = document.querySelector('#loading');
                loadingNode.style.display = 'none';
            }
        </script>
</body>
</html>

https://gyazo.com/85706052a98f8f8cdf286df12a045179

うまく動いています。あとはローディングアイコンの位置等を調整するだけでいいでしょう。