CSS3アニメーションでつくるインターフェイス表現

第16回並んで弾けるアニメーション

今回のお題は、水平に並べた要素に時間差で波紋のような弾けるアニメーションを加えるサンプル1⁠。Smooth Pulseのデザインとアニメーションをもとに、わかりやすく組み立て直した。アニメーションの工夫があるものの、技術的にはむずかしくない。ただ、要素の数だけアニメーションのコードが増える。jsdo.itのスペースも考え合わせて、要素の数は4つに減らした。

サンプル1 CSS3: Smooth Pulse

水平に並べた要素に静的なスタイルを割り当てる

アニメーションを加える前の、4つの要素の組み立てと静的なスタイルは、後に掲げるコード1のとおりだ。border-radiusプロパティの値を50%にして、要素は円形にした。そして、色は緑系(#33ffff)から青系(#3399ff)まで、段階的に変えている図1⁠。アニメーションは、円形の<a>要素class属性"circle")に加えてゆく。なお、おなじみの-prefix-freeは、CDNがあったのでそれを用いた。

<head>

<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
</head>
図1 水平に並べた要素の静的スタイル
図1 水平に並べた要素の静的スタイル

コード1 並べた要素に割り当てた静的なスタイル
<body>要素

<div id="container">
    <a href="#" class="circle"></a>
    <a href="#" class="circle"></a>
    <a href="#" class="circle"></a>
    <a href="#" class="circle"></a>
</div>
<style>要素
html {
    background: #333;
}
#container {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -160px;
}
.circle {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    cursor: pointer;
    float: left;
}
.circle:nth-child(n+2) {
    margin-left: 80px;
}
.circle:nth-child(1) {
    background: #33ffff;
}
.circle:nth-child(2) {
    background: #33ddff;
}
.circle:nth-child(3) {
    background: #33bbff;
}
.circle:nth-child(4) {
    background: #3399ff;
}

並べた要素に弾けるようなアニメーションを加える

円形の<a>要素class属性"circle")に加えるアニメーションは、4つで少しずつ異なる。それらをすべて羅列したのでは、共通する基本部分がわかりにくい。そこで、違うところは以下のように変数を使って示すことにした。変数nは要素の順番(1~4)だ。redgreenおよびblueは、要素ごとの色backgroundによって決まる。そして、delayは要素によって変えるアニメーション開始の時間差だ。あとは、すべて共通となる。

@keyframes規則の構成は大枠として、3つの影box-shadowプロパティ)を3つのキーフレーム(0%と10%および100%)でアニメーションanimationプロパティ)させることになる。ひとつめと3つめの影は同じ色で、それぞれの要素と同系色にする。ひとつめの影は要素から広がる光彩で、透明から輝きを増し、透明に戻ることで、瞬くように見せる。3つめは、大きさを広げながら強さを増し、さらに広がりつつ消えてゆく。隠し味は、ふたつめの背景色の影だ。こちらは不透明なまま、3つめの影に少し遅れて広がる。この影がふたつの間を埋めることによって、波紋のような弾けるアニメーションになった図2⁠。ここまでのCSSの定めを、以下のコード2にまとめた。

.circle:nth-child(n) {

    animation: pulse_n 2s delay ease-out infinite;
}
@keyframes pulse_n {
    0% {
        box-shadow: 0 0 8px 6px rgba(red, green, blue, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(red, green, blue, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(red, green, blue, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(red, green, blue, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(red, green, blue, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(red, green, blue, 0);
    }
}
図2 並んだ要素に順に波紋のような弾けるアニメーションが加わった
図2 並んだ要素に順に波紋のような弾けるアニメーションが加わった
コード2 要素を順に弾けるようにアニメーションさせる
html {
    background: #333;
}
#container {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -160px;
}
.circle {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    cursor: pointer;
    float: left;
}
.circle:nth-child(n+2) {
    margin-left: 80px;
}
.circle:nth-child(1) {
    background: #33ffff;
    animation: pulse_1 2s 0s ease-out infinite;
}
@keyframes pulse_1 {
    0% {
        box-shadow: 0 0 8px 6px rgba(26, 255, 255, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(26, 255, 255, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(26, 255, 255, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(26, 255, 255, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(26, 255, 255, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(26, 255, 255, 0);
    }
}
.circle:nth-child(2) {
    background: #33ddff;
    animation: pulse_2 2s 0.25s ease-out infinite;
}
@keyframes pulse_2 {
    0% {
        box-shadow: 0 0 8px 6px rgba(26, 217, 255, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(26, 217, 255, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(26, 217, 255, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(26, 217, 255, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(26, 217, 255, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(26, 217, 255, 0);
    }
}
.circle:nth-child(3) {
    background: #33bbff;
    animation: pulse_3 2s 0.5s ease-out infinite;
}
@keyframes pulse_3 {
    0% {
        box-shadow: 0 0 8px 6px rgba(26, 179, 255, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(26, 179, 255, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(26, 179, 255, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(26, 179, 255, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(26, 179, 255, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(26, 179, 255, 0);
    }
}
.circle:nth-child(4) {
    background: #3399ff;
    animation: pulse_4 2s 0.75s ease-out infinite;
}
@keyframes pulse_4 {
    0% {
        box-shadow: 0 0 8px 6px rgba(26, 140, 255, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(26, 140, 255, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(26, 140, 255, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(26, 140, 255, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(26, 140, 255, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(26, 140, 255, 0);
    }
}

ポインタが重なったときのアニメーションを加える

要素にマウスポインタが重なったときには、アニメーションを変えよう。基本は前項で加えた弾けるアニメーションと同じだ。それも、つぎのように少し簡潔になる。ポインタが重なった要素のみに与えるアニメーションだから、時間差は変えない。@keyframes規則のキーフレームはふたつで済ませている。要素に応じて色を定めた3つの影のアニメーションは、前項と同じ考え方だ。これで、マウスポインタを重ねた要素の色とアニメーションが変わる図3⁠。

.circle:nth-child(n):hover {

    animation: hover_n 0.5s 0.4s ease-out infinite;
}

@keyframes hover_n {
    from {
        box-shadow: 0 0 8px 6px rgba(red, green, blue, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(red, green, blue, 1);
    }
    to {
        box-shadow: 0 0 8px 6px rgba(red, green, blue, 0),
            0 0 4px 40px #333,
            0 0 4px 41px rgba(red, green, blue, 0);
    }
}
図3 マウスポインタを重ねるとアニメーションが変わる
図3 マウスポインタを重ねるとアニメーションが変わる

細かいけれど、要素にはtransitionプロパティも加えたので、マウスポインタを重ねたときの色は滑らかに切り替わる。なお、アニメーションの時間差は、要素同士の同期はとっていない。そのため、マウスポインタを重ねてアニメーションが切り替わると、他の要素とのタイミングはずれてしまう。できあがったCSSの定めは、以下のコード3にまとめた。要素の数に加え、それぞれの:hover擬似クラスにもアニメーションを定めたことにより、コードの行数は増えてしまった。だが、これまで解説した基本の考え方がわかれば、むずかしいことはないだろう。

.circle {

    transition: 0.5s;
}
コード3 ポインタが重なったときの動きも加えた波紋のように弾むアニメーション
html {
    background: #333;
}
#container {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -160px;
}
.circle {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    cursor: pointer;
    float: left;
    transition: 0.5s;
}
.circle:nth-child(n+2) {
    margin-left: 80px;
}
.circle:nth-child(1) {
    background: #33ffff;
    animation: pulse_1 2s 0s ease-out infinite;
}
.circle:nth-child(1):hover {
    background: #ff3333;
    animation: hover_1 0.5s 0.4s ease-out infinite;
}
@keyframes pulse_1 {
    0% {
        box-shadow: 0 0 8px 6px rgba(26, 255, 255, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(26, 255, 255, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(26, 255, 255, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(26, 255, 255, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(26, 255, 255, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(26, 255, 255, 0);
    }
}
@keyframes hover_1 {
    from {
        box-shadow: 0 0 8px 6px rgba(255, 26, 26, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(255, 26, 26, 1);
    }
    to {
        box-shadow: 0 0 8px 6px rgba(255, 26, 26, 0),
            0 0 4px 40px #333,
            0 0 4px 41px rgba(255, 26, 26, 0);
    }
}
.circle:nth-child(2) {
    background: #33ddff;
    animation: pulse_2 2s 0.25s ease-out infinite;
}
.circle:nth-child(2):hover {
    background: #ff4733;
    animation: hover_2 0.5s 0.4s ease-out infinite;
}
@keyframes pulse_2 {
    0% {
        box-shadow: 0 0 8px 6px rgba(26, 217, 255, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(26, 217, 255, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(26, 217, 255, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(26, 217, 255, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(26, 217, 255, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(26, 217, 255, 0);
    }
}
@keyframes hover_2 {
    from {
        box-shadow: 0 0 8px 6px rgba(255, 48, 26, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(255, 48, 26, 1);
    }
    to {
        box-shadow: 0 0 8px 6px rgba(255, 48, 26, 0),
            0 0 4px 40px #333,
            0 0 4px 41px rgba(255, 48, 26, 0);
    }
}
.circle:nth-child(3) {
    background: #33bbff;
    animation: pulse_3 2s 0.5s ease-out infinite;
}
.circle:nth-child(3):hover {
    background: #ff5c33;
    animation: hover_3 0.5s 0.4s ease-out infinite;
}
@keyframes pulse_3 {
    0% {
        box-shadow: 0 0 8px 6px rgba(26, 179, 255, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(26, 179, 255, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(26, 179, 255, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(26, 179, 255, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(26, 179, 255, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(26, 179, 255, 0);
    }
}
@keyframes hover_3 {
    from {
        box-shadow: 0 0 8px 6px rgba(255, 71, 26, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(255, 71, 26, 1);
    }
    to {
        box-shadow: 0 0 8px 6px rgba(255, 71, 26, 0),
            0 0 4px 40px #333,
            0 0 4px 41px rgba(255, 71, 26, 0);
    }
}
.circle:nth-child(4) {
    background: #3399ff;
    animation: pulse_4 2s 0.75s ease-out infinite;
}
.circle:nth-child(4):hover {
    background: #ff7033;
    animation: hover_4 0.5s 0.4s ease-out infinite;
}
@keyframes pulse_4 {
    0% {
        box-shadow: 0 0 8px 6px rgba(26, 140, 255, 0),
            0 0 0px 0px #333,
            0 0 0px 0px rgba(26, 140, 255, 0);
    }
    10% {
        box-shadow: 0 0 8px 6px rgba(26, 140, 255, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(26, 140, 255, 1);
    }
    100% {
        box-shadow: 0 0 8px 6px rgba(26, 140, 255, 0),
            0 0 0px 40px #333,
            0 0 0px 40px rgba(26, 140, 255, 0);
    }
}
@keyframes hover_4 {
    from {
        box-shadow: 0 0 8px 6px rgba(255, 94, 26, 1),
            0 0 12px 10px #333,
            0 0 12px 14px rgba(255, 94, 26, 1);
    }
    to {
        box-shadow: 0 0 8px 6px rgba(255, 94, 26, 0),
            0 0 4px 40px #333,
            0 0 4px 41px rgba(255, 94, 26, 0);
    }
}

おすすめ記事

記事・ニュース一覧