CSSだけでボールが回転するloadingを作る方法 Safari対応版

Loadingアイコンメーカーはじめました(2015年9月3日)

前回記事にしたボールが回転するloadingですが、safariで動作しないことが分かりましたので、別のアプローチで再作成しました。
今回は、「内部のボールが回転」「全体が回転」の2種類に、内部のボール数によって2つのバリエーションを用意し、計4タイプを作成しました。

6月16日追記)CSSに一部誤りがあったため修正しました。

HTMLは中にspanを1つ入れて、
<span class="loading"><span></span></span>
です。
左のパターンのCSSは次のようになります。

span.loading, span.loading:after {
    position: relative;
    display: inline-block;
    width: 50px; /*50px=loading全体のサイズの直径*/
    height: 50px; /*50px=loading全体のサイズの直径*/
    margin: 0 10px;
    background-repeat: no-repeat;

    background-image:
         -webkit-gradient(radial,4 center,0,4 center,4,from(#ccc),color-stop(0.5,#ccc),color-stop(0.9,transparent),to(transparent)), /*4=ボール(背景)の直径、#ccc=ボール(背景)の色*/
        -webkit-gradient(radial,center 4,0,center 4,4,from(#ccc),color-stop(0.5,#ccc),color-stop(0.9,transparent),to(transparent)), /*同上*/
        -webkit-gradient(radial,46 center,0,46 center,4,from(#ccc),color-stop(0.5,#ccc),color-stop(0.9,transparent),to(transparent)), /*4=ボール(背景)の直径、46=全体の直径-ボール(背景)の直径、#ccc=ボール(背景)の色*/
        -webkit-gradient(radial,center 46,0,center 46,4,from(#ccc),color-stop(0.5,#ccc),color-stop(0.9,transparent),to(transparent)); /*同上*/


    background-image:
          -webkit-radial-gradient(10% 50%, 4px 4px, #ccc, #ccc 80%, transparent 95%, transparent), /*4px=ボール(背景)の直径、#ccc=ボール(背景)の色*/
        -webkit-radial-gradient(50% 10%, 4px 4px, #ccc, #ccc 80%, transparent 95%, transparent), /*同上*/
        -webkit-radial-gradient(90% 50%, 4px 4px, #ccc, #ccc 80%, transparent 95%, transparent), /*同上*/
        -webkit-radial-gradient(50% 90%, 4px 4px, #ccc, #ccc 80%, transparent 95%, transparent); /*同上*/


    background-image:
        radial-gradient(4px 4px at 10% 50%, #ccc, #ccc 80%, transparent), /*同上*/
        radial-gradient(4px 4px at 50% 10%, #ccc, #ccc 80%, transparent), /*同上*/
        radial-gradient(4px 4px at 90% 50%, #ccc, #ccc 80%, transparent), /*同上*/
        radial-gradient(4px 4px at 50% 90%, #ccc, #ccc 80%, transparent); /*同上*/
}

span.loading:after {
    position: absolute;
    content: " ";
    z-index: -1;
    left: 0;
    top: 0;
    margin: 0;

    -webkit-transform: rotate(45deg);
    transform: rotate(45deg);
}

span.loading span {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    margin: 0;

    background-image:
         -webkit-gradient(radial,5 center,0,5 center,5,from(#000),color-stop(0.4,#000),color-stop(0.9,transparent),to(transparent)); /*5=ボール(回転)の直径、#ccc=ボール(回転)の色*/

    background-image:
        -webkit-radial-gradient(10% 50%, 5px 5px, #000, #000 50%, transparent 95%, transparent); /*5px=ボール(回転)の直径、#000=ボール(回転)の色*/

    background-image:
        radial-gradient(5px 5px at 10% 50%, #000, #000 50%, transparent); /*同上*/

    -webkit-animation: rotating 1s linear infinite; /*1s=回転速度(秒)*/
    animation: rotating 1s linear infinite; /*同上*/
}

/*回転*/
@-webkit-keyframes rotating {
    0% {
        -webkit-transform: rotate(0deg);
    }
    12.4% {
        -webkit-transform: rotate(0deg);
    }
    12.5% {
        -webkit-transform: rotate(45deg);
    }
    24.9% {
        -webkit-transform: rotate(45deg);
    }
    25% {
        -webkit-transform: rotate(90deg);
    }
    37.4% {
        -webkit-transform: rotate(90deg);
    }
    37.5% {
        -webkit-transform: rotate(135deg);
    }
    49.9% {
        -webkit-transform: rotate(135deg);
    }
    50% {
        -webkit-transform: rotate(180deg);
    }
    62.4% {
        -webkit-transform: rotate(180deg);
    }
    62.5% {
        -webkit-transform: rotate(225deg);
    }
    74.9% {
        -webkit-transform: rotate(225deg);
    }
    75% {
        -webkit-transform: rotate(270deg);
    }
    87.4% {
        -webkit-transform: rotate(270deg);
    }
    87.5% {
        -webkit-transform: rotate(315deg);
    }
    99.9% {
        -webkit-transform: rotate(315deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
    }
}
@keyframes rotating {
    0% {
        transform: rotate(0deg);
    }
    12.4% {
        transform: rotate(0deg);
    }
    12.5% {
        transform: rotate(45deg);
    }
    24.9% {
        transform: rotate(45deg);
    }
    25% {
        transform: rotate(90deg);
    }
    37.4% {
        transform: rotate(90deg);
    }
    37.5% {
        transform: rotate(135deg);
    }
    49.9% {
        transform: rotate(135deg);
    }
    50% {
        transform: rotate(180deg);
    }
    62.4% {
        transform: rotate(180deg);
    }
    62.5% {
        transform: rotate(225deg);
    }
    74.9% {
        transform: rotate(225deg);
    }
    75% {
        transform: rotate(270deg);
    }
    87.4% {
        transform: rotate(270deg);
    }
    87.5% {
        transform: rotate(315deg);
    }
    99.9% {
        transform: rotate(315deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

radial-gradientを利用して内部のボールを描写しています。
radial-gradientでボールを描写する方法がかなり難解なため、ここでは解説しませんが、知りたい方は上記のコードを色々といじってみて学ぶのが手っ取り早いかも知れません。
上下左右4つのボールを描写し、同様に疑似要素で描写した4つのボールを45度傾けています。
中のspanは、1つだけボールを描写し、これをanimationで回転させています。
animation部分では、100%を12.5%ずつの8つに分け、それぞれ12.4%不動→0.1%で移動、を繰り返しています。
今回、これまで紹介したベンダープレフィックス(-webkitなど)の記載方法を見直しました。

コメントアウト(/**/)に色やサイズの変更についてメモを載せましたので、そちらを参考にカスタマイズをお試しください。
尚、ボールのサイズは、全体の直径の1割が上限になっています。これを超える場合はボールが切れてしまうのでご注意ください。

バリエーション)ボールを増やす

CSSは下記のようになります。
afterのみ使用していた疑似要素にbeforeを加え、それぞれを30度・60度と回してボールを計12個にしています。
アニメーションは100%を8つに分けていたところを12に分割しています。

span.loading, span.loading:before, span.loading:after {
    position: relative;
    display: inline-block;
    width: 50px; /*50px=loading全体のサイズの直径*/
    height: 50px; /*50px=loading全体のサイズの直径*/
    margin: 0 10px;
    background-repeat: no-repeat;

    background-image:
         -webkit-gradient(radial,3 center,0,3 center,3,from(#ccc),color-stop(0.5,#ccc),color-stop(0.9,transparent),to(transparent)), /*3=ボール(背景)の直径、#ccc=ボール(背景)の色*/
        -webkit-gradient(radial,center 3,0,center 3,3,from(#ccc),color-stop(0.5,#ccc),color-stop(0.9,transparent),to(transparent)), /*同上*/
        -webkit-gradient(radial,47 center,0,47 center,3,from(#ccc),color-stop(0.5,#ccc),color-stop(0.9,transparent),to(transparent)), /*3=ボール(背景)の直径、47=全体の直径-ボール(背景)の直径、#ccc=ボール(背景)の色*/
        -webkit-gradient(radial,center 47,0,center 47,3,from(#ccc),color-stop(0.5,#ccc),color-stop(0.9,transparent),to(transparent)); /*同上*/


    background-image:
         -webkit-radial-gradient(10% 50%, 4px 4px, #ccc, #ccc 80%, transparent 95%, transparent), /*4px=ボール(背景)の直径、#ccc=ボール(背景)の色*/
        -webkit-radial-gradient(50% 10%, 4px 4px, #ccc, #ccc 80%, transparent 95%, transparent), /*同上*/
        -webkit-radial-gradient(90% 50%, 4px 4px, #ccc, #ccc 80%, transparent 95%, transparent), /*同上*/
        -webkit-radial-gradient(50% 90%, 4px 4px, #ccc, #ccc 80%, transparent 95%, transparent); /*同上*/


    background-image:
        radial-gradient(3px 3px at 10% 50%, #ccc, #ccc 80%, transparent), /*同上*/
        radial-gradient(3px 3px at 50% 10%, #ccc, #ccc 80%, transparent), /*同上*/
        radial-gradient(3px 3px at 90% 50%, #ccc, #ccc 80%, transparent), /*同上*/
        radial-gradient(3px 3px at 50% 90%, #ccc, #ccc 80%, transparent); /*同上*/
}

span.loading:before, span.loading:after {
    position: absolute;
    content: " ";
    z-index: -1;
    left: 0;
    top: 0;
    margin: 0;
}
    span.loading:before {
        -webkit-transform: rotate(30deg);
        transform: rotate(30deg);
    }
    span.loading:after {
        -webkit-transform: rotate(60deg);
        transform: rotate(60deg);
    }

span.loading span {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    margin: 0;

    background-image:
         -webkit-gradient(radial,4 center,0,4 center,4,from(#000),color-stop(0.5,#000),color-stop(0.9,transparent),to(transparent)); /*4=ボール(回転)の直径、#ccc=ボール(回転)の色*/

    background-image:
         -webkit-radial-gradient(10% 50%, 4px 4px, #000, #000 50%, transparent 95%, transparent); /*4px=ボール(回転)の直径、#000=ボール(回転)の色*/

    background-image:
        radial-gradient(4px 4px at 10% 50%, #000, #000 50%, transparent); /*同上*/

    -webkit-animation: rotating 1s linear infinite; /*1s=回転速度(秒)*/
    animation: rotating 1s linear infinite; /*同上*/
}

/*回転*/
@-webkit-keyframes rotating {
    0% {
        -webkit-transform: rotate(0deg);
    }
    8.2% {
        -webkit-transform: rotate(0deg);
    }
    8.3% {
        -webkit-transform: rotate(30deg);
    }
    16.6% {
        -webkit-transform: rotate(30deg);
    }
    16.7% {
        -webkit-transform: rotate(60deg);
    }
    24.9% {
        -webkit-transform: rotate(60deg);
    }
    25% {
        -webkit-transform: rotate(90deg);
    }
    33.2% {
        -webkit-transform: rotate(90deg);
    }
    33.3% {
        -webkit-transform: rotate(120deg);
    }
    41.6% {
        -webkit-transform: rotate(120deg);
    }
    41.7% {
        -webkit-transform: rotate(150deg);
    }
    49.9% {
        -webkit-transform: rotate(150deg);
    }
    50% {
        -webkit-transform: rotate(180deg);
    }
    58.2% {
        -webkit-transform: rotate(180deg);
    }
    58.3% {
        -webkit-transform: rotate(210deg);
    }
    66.6% {
        -webkit-transform: rotate(210deg);
    }
    66.7% {
        -webkit-transform: rotate(240deg);
    }
    74.9% {
        -webkit-transform: rotate(240deg);
    }
    75% {
        -webkit-transform: rotate(270deg);
    }
    83.2% {
        -webkit-transform: rotate(270deg);
    }
    83.3% {
        -webkit-transform: rotate(300deg);
    }
    91.6% {
        -webkit-transform: rotate(300deg);
    }
    91.7% {
        -webkit-transform: rotate(330deg);
    }
    99.9% {
        -webkit-transform: rotate(330deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
    }
}
@keyframes rotating {
    0% {
        transform: rotate(0deg);
    }
    8.2% {
        transform: rotate(0deg);
    }
    8.3% {
        transform: rotate(30deg);
    }
    16.6% {
        transform: rotate(30deg);
    }
    16.7% {
        transform: rotate(60deg);
    }
    24.9% {
        transform: rotate(60deg);
    }
    25% {
        transform: rotate(90deg);
    }
    33.2% {
        transform: rotate(90deg);
    }
    33.3% {
        transform: rotate(120deg);
    }
    41.6% {
        transform: rotate(120deg);
    }
    41.7% {
        transform: rotate(150deg);
    }
    49.9% {
        transform: rotate(150deg);
    }
    50% {
        transform: rotate(180deg);
    }
    58.2% {
        transform: rotate(180deg);
    }
    58.3% {
        transform: rotate(210deg);
    }
    66.6% {
        transform: rotate(210deg);
    }
    66.7% {
        transform: rotate(240deg);
    }
    74.9% {
        transform: rotate(240deg);
    }
    75% {
        transform: rotate(270deg);
    }
    83.2% {
        transform: rotate(270deg);
    }
    83.3% {
        transform: rotate(300deg);
    }
    91.6% {
        transform: rotate(300deg);
    }
    91.7% {
        transform: rotate(330deg);
    }
    99.9% {
        transform: rotate(330deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

バリエーション)全体を回す

HTMLは内部のspanが不要です。
CSSは、内部のspanを回転させていたものを、全体を回転させるように変更させるだけです。
尚、疑似要素にも回転のanimationを設定してしまうと、回転体の中で回転することになり、意図しない複雑な回転が発生してしまうのでご注意ください。

ボールが8つの場合のCSSは以下です。

span.loading, span.loading:after {
    position: relative;
    display: inline-block;
    width: 50px; /*50px=loading全体のサイズの直径*/
    height: 50px; /*50px=loading全体のサイズの直径*/
    margin: 0 10px;
    background-repeat: no-repeat;

    background-image:
        -webkit-gradient(radial,4 center,0,4 center,4,from(#999),color-stop(0.5,#999),color-stop(0.9,transparent),to(transparent)), /*4=ボールの直径、#999=ボールの色*/
        -webkit-gradient(radial,center 4,0,center 4,4,from(#999),color-stop(0.5,#999),color-stop(0.9,transparent),to(transparent)), /*同上*/
        -webkit-gradient(radial,46 center,0,46 center,4,from(#999),color-stop(0.5,#999),color-stop(0.9,transparent),to(transparent)),  /*4=ボールの直径、46=全体の直径-ボールの直径、#999=ボールの色*/
        -webkit-gradient(radial,center 46,0,center 46,4,from(#999),color-stop(0.5,#999),color-stop(0.9,transparent),to(transparent)); /*同上*/

    background-image:
         -webkit-radial-gradient(10% 50%, 4px 4px, #999, #999 80%, #999 95%, transparent), /*4px=ボールの直径、#999=ボールの色*/
        -webkit-radial-gradient(50% 10%, 4px 4px, #999, #999 80%, #999 95%, transparent), /*同上*/
        -webkit-radial-gradient(90% 50%, 4px 4px, #999, #999 80%, #999 95%, transparent), /*同上*/
        -webkit-radial-gradient(50% 90%, 4px 4px, #999, #999 80%, #999 95%, transparent); /*同上*/
                                                                                         
    background-image:
        radial-gradient(4px 4px at 10% 50%, #999, #999 80%, transparent), /*同上*/
        radial-gradient(4px 4px at 50% 10%, #999, #999 80%, transparent), /*同上*/
        radial-gradient(4px 4px at 90% 50%, #999, #999 80%, transparent), /*同上*/
        radial-gradient(4px 4px at 50% 90%, #999, #999 80%, transparent); /*同上*/
}

span.loading:after {
    position: absolute;
    content: " ";
    left: 0;
    top: 0;
    margin: 0;

    -webkit-transform: rotate(45deg);
    transform: rotate(45deg);
}

span.loading {
    -webkit-animation: rotating 3s linear infinite; /*1s=回転速度(秒)*/
    animation: rotating 3s linear infinite; /*同上*/
}

/*回転*/
@-webkit-keyframes rotating {
    0% {
        -webkit-transform: rotate(0deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
    }
}
@keyframes rotating {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

ボールが12個の場合のCSSは以下です。

span.loading, span.loading:before, span.loading:after {
    position: relative;
    display: inline-block;
    width: 50px; /*50px=loading全体のサイズの直径*/
    height: 50px; /*50px=loading全体のサイズの直径*/
    margin: 0 10px;
    background-repeat: no-repeat;

    background-image:
         -webkit-gradient(radial,3 center,0,3 center,3,from(#999),color-stop(0.5,#999),color-stop(0.9,transparent),to(transparent)), /*3=ボールの直径、#999=ボールの色*/
        -webkit-gradient(radial,center 3,0,center 3,3,from(#999),color-stop(0.5,#999),color-stop(0.9,transparent),to(transparent)), /*同上*/
        -webkit-gradient(radial,47 center,0,47 center,3,from(#999),color-stop(0.5,#999),color-stop(0.9,transparent),to(transparent)), /*3=ボールの直径、47=全体の直径-ボールの直径、#999=ボールの色*/
        -webkit-gradient(radial,center 47,0,center 47,3,from(#999),color-stop(0.5,#999),color-stop(0.9,transparent),to(transparent)); /*同上*/

    background-image:
         -webkit-radial-gradient(10% 50%, 4px 4px, #999, #999 80%, #999 95%, transparent), /*4px=ボールの直径、#999=ボールの色*/
        -webkit-radial-gradient(50% 10%, 4px 4px, #999, #999 80%, #999 95%, transparent), /*同上*/
        -webkit-radial-gradient(90% 50%, 4px 4px, #999, #999 80%, #999 95%, transparent), /*同上*/
        -webkit-radial-gradient(50% 90%, 4px 4px, #999, #999 80%, #999 95%, transparent); /*同上*/

    background-image:
          radial-gradient(3px 3px at 10% 50%, #999, #999 80%, transparent), /*同上*/
        radial-gradient(3px 3px at 50% 10%, #999, #999 80%, transparent), /*同上*/
        radial-gradient(3px 3px at 90% 50%, #999, #999 80%, transparent), /*同上*/
        radial-gradient(3px 3px at 50% 90%, #999, #999 80%, transparent); /*同上*/
}

span.loading:before, span.loading:after {
    position: absolute;
    content: " ";
    z-index: -1;
    left: 0;
    top: 0;
    margin: 0;
}
    span.loading:before {
        -webkit-transform: rotate(30deg);
        transform: rotate(30deg);
    }
    span.loading:after {
        -webkit-transform: rotate(60deg);
        transform: rotate(60deg);
    }

span.loading {
    -webkit-animation: rotating 3s linear infinite; /*1s=回転速度(秒)*/
    animation: rotating 3s linear infinite; /*同上*/
}

/*回転*/
@-webkit-keyframes rotating {
    0% {
        -webkit-transform: rotate(0deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
    }
}
@keyframes rotating {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

対応ブラウザとIE対策

Chrome・Firefox・Safari・IE11・IE10に対応しています。Android4・iOSの標準ブラウザでも利用できます。
IE9以下は、下記コードで「loading」の文字を表示させて対処します。

<!--[if lte IE 9 ]>
<style type="text/css">
    /*非表示化*/
    span.loading:before, span.loading span {
        display: none !important;
    }

    /*loading*/
    span.loading:after {
        content: "loading";
        left: 0;
        right: 0;
        top: 50%;
        width: auto;
        height: auto;
        margin-left: 0;
        background-color: transparent;
        text-align: center;
        line-height: 0;
        -webkit-transform: rotate(0);
        transform: rotate(0);
    }

</style>
<![endif]-->

2015/6/11