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

第20回トグルスイッチのアニメーション

今回は、動きそのものはあまり凝りようのないトグルスイッチがお題だ(サンプル1⁠)⁠。とはいえ、視覚的な表現に細かい工夫がある。「⁠switch」のデザインと表現スイッチとして表示するをもとに、HTMLとCSSの組み立てはわかりやすく改めた。コード行数も絞り込んである。アニメーションの動きより、陰影やグラデーションなどの組み合わせが鍵だ。

サンプル1 CSS3: Image Accordion

インタラクションを加える前のトグルスイッチの表現

インタラクションを加える前の静的な組み立てから確かめる。まず<body>要素はつぎのように簡素だ。チェックボックスの<input>要素(type属性"checkbox")で、スイッチをオン・オフする(class属性"switch"⁠)⁠。もっとも、要素はCSSのdisplayプロパティで隠す(none⁠)⁠。スイッチとして表示する<label>要素(class属性"toggle")にfor属性でチェックボックスのid属性("toggle")を与えているので、クリックすればオン・オフが切り替わる。いつもどおり、-prefix-freeもCDNから読み込んでおく(第16回の「水平に並べた要素に静的なスタイルを割り当てる」参照⁠)⁠。

<body>要素
<div class="container">
    <input class="switch" type="checkbox" id="toggle" name="toggle">
    <label class="toggle" for="toggle"></label>
</div>

トグルスイッチの目に見える要素は3つだ(図1⁠)⁠。ひとつはスイッチが動く領域で、<label>要素(class属性"toggle")からつくられる。残るふたつのインジケータとつまみは、つぎのようにともに擬似要素(::beforeと::after)としてCSSで加えた。立体感はおもにグラデーション(linear-gradient()関数)の塗りと陰影(box-shadowプロパティ)で与えている。linear-gradient()関数の引数には方向を渡していないので、デフォルトの下向き(to bottom)だ。box-shadowは影を広げるだけでなく、内側(inset)に明暗を加えることで凹凸が表せる。

<style>要素
.toggle {

    background: linear-gradient(#121823, #161d2b);
    box-shadow:
        inset 0 3px 8px 1px rgba(0, 0, 0, 0.5),
        inset 0 1px 0 rgba(0, 0, 0, 0.5),
        0 1px 0 rgba(255, 255, 255, 0.2);
}
.toggle::before {

    background: linear-gradient(#36455b, #283446);
    box-shadow:
        inset 0 1px 0 rgba(0, 0, 0, 0.2),
        0 1px 0 rgba(255, 255, 255, 0.1),
        0 0 10px rgba(185, 231, 253, 0),
        inset 0 0 8px rgba(0, 0, 0, 0.9),
        inset 0 -2px 5px rgba(0, 0, 0, 0.3),
        inset 0 -5px 5px rgba(0, 0, 0, 0.5);
}
.toggle::after {

    background: linear-gradient(#36455b, #283446);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.2),
        0 0 8px rgba(0, 0, 0, 0.3),
        0 12px 12px rgba(0, 0, 0, 0.4);
}
図1 トグルスイッチの3つの視覚的要素
図1 トグルスイッチの3つの視覚的要素

これでインタラクションを加える前のトグルスイッチのオフの表現ができた。ここまでのCSSの定めをつぎのコード1に掲げる。前掲の<body>要素の記述も、ご参考までにここにまとめておこう。<body>の要素については、このあと書き替えることはない。

コード1 インタラクションを加える前のトグルスイッチのHTMLコードとスタイル
<body>要素

<div class="container">
    <input class="switch" type="checkbox" id="toggle" name="toggle">
    <label class="toggle" for="toggle"></label>
</div>
<style>要素
body {
    background: #202838;
}
.container {
    width: 180px;
    height: 55px;
    position: relative;
    margin: 100px auto;
}
.switch {
    display: none;
    position: absolute;
    width: 100%;
    height: 100%;
}
.toggle {
    display: block;
    width: 80%;
    height: 100%;
    position: relative;
    cursor: pointer;
    border-radius: 30px;
    background: linear-gradient(#121823, #161d2b);
    box-shadow:
        inset 0 3px 8px 1px rgba(0, 0, 0, 0.5),
        inset 0 1px 0 rgba(0, 0, 0, 0.5),
        0 1px 0 rgba(255, 255, 255, 0.2);
}
.toggle::before {
    content: "";
    display: inline-block;
    position: absolute;
    right: -36px;
    top: 17px;
    width: 18px;
    height: 18px;
    border-radius: 10px;
    background: linear-gradient(#36455b, #283446);
    box-shadow:
        inset 0 1px 0 rgba(0, 0, 0, 0.2),
        0 1px 0 rgba(255, 255, 255, 0.1),
        0 0 10px rgba(185, 231, 253, 0),
        inset 0 0 8px rgba(0, 0, 0, 0.9),
        inset 0 -2px 5px rgba(0, 0, 0, 0.3),
        inset 0 -5px 5px rgba(0, 0, 0, 0.5);
}
.toggle::after {
    content: "";
    height: 51px;
    width: 51px;
    position: absolute;
    left: 2px;
    top: 2px;
    border-radius: inherit;
    background: linear-gradient(#36455b, #283446);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.2),
        0 0 8px rgba(0, 0, 0, 0.3),
        0 12px 12px rgba(0, 0, 0, 0.4);
}

トグルスイッチをスライドする

アニメーションする要素はふたつある。そのうちのつまみの擬似要素(::after)を先に動かそう。チェックボックス(class属性"switch")がチェックされたら(:checked擬似クラス⁠)⁠、以下のように右にスライドさせる(図2⁠)⁠。transitionプロパティにはアニメーションの時間のみ与えた。タイミング関数は、デフォルトのeaseが用いられることになる。

これで、動きは済むはずだ。だが、box-shadowプロパティも少し変えている。内側(inset)右に青白さを加えたので、明かりに照らされたようになった。この表現の意味は、つぎの項で明らかになる。

<style>要素
.toggle::after {

    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.2),
        0 0 8px rgba(0, 0, 0, 0.3),
        0 12px 12px rgba(0, 0, 0, 0.4);
    transition: 0.5s;
}
.switch:checked ~ .toggle::after {
    left: 63%;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.2),  /* 変化なし */
        0 0 8px rgba(0, 0, 0, 0.3),  /* 変化なし */
        0 8px 8px rgba(0, 0, 0, 0.3),  /* 影のぼけが弱まる */
        inset -1px 0 1px #b9f3fe;  /* 青みがかった白が内側右に加わる */
}
図2 トグルスイッチがクリックでスライドする
図2 トグルスイッチがクリックでスライドする

インジケータを光らせる

アニメーションさせるふたつめのインジケータの擬似要素(::before)を光らせよう。オフは暗く、オンで青みがかった白に変える。陰影(box-shadow)の設定は、つぎのように少し細かい。あまり変わらない影もあるものの、青みのかった白い光彩が透明から不透明になって表れたり、色が黒から青みがかった白に移ったりしている。背景色(background)のグラデーション(linear-gradient())も白を強めた。transitionプロパティの定めは、前項のつまみと同じだ。

<style>要素
.toggle::before {

    background: linear-gradient(#36455b, #283446);
    box-shadow:
        inset 0 1px 0 rgba(0, 0, 0, 0.2),
        0 1px 0 rgba(255, 255, 255, 0.1),
        0 0 10px rgba(185, 231, 253, 0),
        inset 0 0 8px rgba(0, 0, 0, 0.9),
        inset 0 -2px 5px rgba(0, 0, 0, 0.3),
        inset 0 -5px 5px rgba(0, 0, 0, 0.5);
    transition: 0.5s;
}

.switch:checked ~ .toggle::before {
    background: linear-gradient(#ffffff, #77a1b9);
    box-shadow:
        inset 0 1px 0 rgba(0, 0, 0, 0.1),  /* 黒い影が薄まる */
        0 1px 0 rgba(255, 255, 255, 0.1),  /* 変化なし */
        0 0 10px rgba(100, 231, 253, 1),  /* 青みがかった白が表れる */
        inset 0 0 8px rgba(61, 157, 247, 0.8),  /* 黒から青みがかった白に */
        inset 0 -2px 5px rgba(185, 231, 253, 0.3),  /* 黒から青みがかった白に */
        inset 0 -3px 8px rgba(185, 231, 253, 0.5);  /* 黒から青みがかった白が広がる */
 }

これでトグルスイッチをクリックすると、つまみが左右にスライドするとともにインジケータも点滅するようになった。つまみが右に移ったときインジケータは光るので、つまみの右が照らされる(図3⁠)⁠。前項でつまみに加えた内側右が明るくなる変化は、これを表現したかったわけだ。冒頭のサンプル1を改めてたしかめてほしい。スイッチの現実感が増しているだろう。書き上がったCSSの定めは、以下のコード2にまとめたとおりだ。

図3 インジケーターが点灯してつまみを照らす
図3 インジケーターが点灯してつまみを照らす
コード2 トグルスイッチのスライドするつまみとインジケータ
body {
    background: #202838;
}
.container {
    width: 180px;
    height: 55px;
    position: relative;
    margin: 100px auto;
}
.switch {
    display: none;
    position: absolute;
    width: 100%;
    height: 100%;
}
.toggle {
    display: block;
    width: 80%;
    height: 100%;
    position: relative;
    cursor: pointer;
    border-radius: 30px;
    background: linear-gradient(#121823, #161d2b);
    box-shadow:
        inset 0 3px 8px 1px rgba(0, 0, 0, 0.5),
        inset 0 1px 0 rgba(0, 0, 0, 0.5),
        0 1px 0 rgba(255, 255, 255, 0.2);
}
.toggle::before {
    content: "";
    display: inline-block;
    position: absolute;
    right: -36px;
    top: 17px;
    width: 18px;
    height: 18px;
    border-radius: 10px;
    background: linear-gradient(#36455b, #283446);
    box-shadow:
        inset 0 1px 0 rgba(0, 0, 0, 0.2),
        0 1px 0 rgba(255, 255, 255, 0.1),
        0 0 10px rgba(185, 231, 253, 0),
        inset 0 0 8px rgba(0, 0, 0, 0.9),
        inset 0 -2px 5px rgba(0, 0, 0, 0.3),
        inset 0 -5px 5px rgba(0, 0, 0, 0.5);
    transition: 0.5s;
}
.toggle::after {
    content: "";
    height: 51px;
    width: 51px;
    position: absolute;
    left: 2px;
    top: 2px;
    border-radius: inherit;
    background: linear-gradient(#36455b, #283446);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.2),
        0 0 8px rgba(0, 0, 0, 0.3),
        0 12px 12px rgba(0, 0, 0, 0.4);
    transition: 0.5s;
}
.switch:checked ~ .toggle::before {
    background: linear-gradient(#ffffff, #77a1b9);
    box-shadow:
        inset 0 1px 0 rgba(0, 0, 0, 0.1),
        0 1px 0 rgba(255, 255, 255, 0.1),
        0 0 10px rgba(100, 231, 253, 1),
        inset 0 0 8px rgba(61, 157, 247, 0.8),
        inset 0 -2px 5px rgba(185, 231, 253, 0.3),
        inset 0 -3px 8px rgba(185, 231, 253, 0.5);
 }
.switch:checked ~ .toggle::after {
    left: 63%;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.2),
        0 0 8px rgba(0, 0, 0, 0.3),
        0 8px 8px rgba(0, 0, 0, 0.3),
        inset -1px 0 1px #b9f3fe;
}

おすすめ記事

記事・ニュース一覧