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

第3回選択項目以外がぼけるメニュー

今回のお題は、サンプル1のように項目を並べたメニューだ。マウスポインタをメニュー項目に重ねると、他の項目のテキストがぼける。また、ポインタを重ねたメニュー項目は浮き上がったり、マウスボタンを押すと凹むようにアニメーションする。CodePenに公開されたBlurry Menuを参考に、HTMLとCSSのコードの構成を改め、簡潔にした。

サンプル1 CSS3: Blurry menu

メニューのもとになる静的スタイル

まず、<body>要素に書くコードの構成だ。メニューは、つぎのようにclass属性が"menu"の<ul>要素でリストとして組み立てる。メニュー項目をclass属性"item"の<li>要素とし、その中の<a>要素にテキストを加えた。そして、メニューの<ul>要素全体を、class属性"container"の<div>要素で包んでいる。

<div class="container">
<ul class="menu">
    <li class="item"><a href="#">HTML</a></li>
    <li class="item"><a href="#">CSS</a></li>
    <li class="item"><a href="#">JavaScript</a></li>
    <li class="item"><a href="#">XML</a></li>
</ul>
</div>

この<body>要素の記述に対して、つぎのような<style>要素を加える。今のところ、メニューは動きのないただのバーで図1⁠、マウスインタラクションも加えられていない。なお、CSSにベンダープレフィックスをつけなくて済むように、<script>要素で-prefix-freeを読み込んだ(第1回のベンダープレフィックスと-prefix-freeの項参照⁠⁠。

<script src="lib/prefixfree.min.js"></script>
<style>
body {
    font-family: Arial, Helvetica, sans-serif;
    background: dimgray;
}
.container {
    height: 50px;
    margin: 40px 0;
    padding: 50px 0;
    text-align: center;
}
.menu {    
    display: inline-block;
    list-style: none;
    margin: 0;
    overflow: hidden;
    padding: 0;
    position: relative;
    background: linear-gradient(ghostwhite, gainsboro);
}
.item {
    float: left;
    font-size: 14px;
    line-height: 50px;
    padding: 0 35px;
    cursor: pointer;
}
.item a {
    color: gray;
    display: block;
    text-decoration: none;
    text-shadow: 0 1px 0 white;
}
</style>
図1 メニューのもととなるスタイル
図1 メニューのもととなるスタイル

メニューの背景色とテキストの影

今回のお題は、さほど目新しい機能は使っていない。細かい使い方の発想や、それらの組み合わせで表現を組み立てている。しかし言われないと気づかないところも少なくない。前掲の<style>要素の定めでは、メニューバーclass属性"menu")backgroundプロパティに、linear-gradient()関数で線形のグラデーションを加えている。おそらく気づきにくいだろうから、つぎのようにはっきりとした白から黒のグラデーションに変えてみよう図2⁠。実は、少し丸く膨らんだような表現にしてあったのだ。

.menu {    

    /* background: linear-gradient(ghostwhite, gainsboro); */
    background: linear-gradient(white, black);

}

.item a {

    text-shadow: 0 1px 0 white;

}
図2 メニュー背景のグラデーションと項目テキストの影
図2 メニュー背景のグラデーションと項目テキストの影

もうひとつ気づくのは、テキストが凹んで見えることだ(前掲図2⁠。そのためには、下方向に明るい影を入れればよい(第1回のマウスポインタを重ねたときのボタンの表現参照⁠⁠。テキストの影は、text-shadowプロパティに影の水平・垂直のずれとぼけ幅および色を与えて定める。前掲コードでは、下に1ピクセルのくっきりとした白い影を加えていた。

text-shadow: 水平のずれ 垂直のずれ ぼけ幅 カラー

なお、linear-gradient()関数の第1引数には、グラデーションの向きが与えられる。これを省くと、デフォルトで上から下のグラデーションto bottomになる。

linear-gradient([to 方向,] 始まりの色, 終わりの色)

メニュー項目の間に区切り線を入れる

メニューバーの項目class属性"item")の間には区切り線を入れる。ここでも小技を加えて立体的に見せたい。そのためには、プロパティborder-leftborder-rightで明暗の差をつければよい。まずは、わかりやすく白と黒で太めの線を加えてみた図3⁠。

.item {

    border-left: 4px solid white;
    border-right: 4px solid black;

}
図3 メニュー項目の左右に区切り線が入る
図3 メニュー項目の左右に区切り線が入る

効果が確かめられたところで、改めてつぎのように線は細くし、明暗差をグレーで和らげた。そして、両端の線は擬似クラス:first-child:last-childで指定してなくすnoneことにする図4⁠。

.item {

    border-left: 1px solid white;
    border-right: 1px solid gainsboro;

}

li.item:first-child {
    border-left: none;
}
li.item:last-child {
    border-right: none;
}
図4 区切り線により少し膨らんで見えるメニュー項目
図4 区切り線により少し膨らんで見えるメニュー項目

メニューバーに外枠を加える

メニューを動かす前に、もうひとつだけ飾りつけとして、メニューバーに外枠を加える。もちろん、borderプロパティを用いてもよい。けれど、今回はbox-shadowプロパティを活用してみよう。影は普通ぼかす。ぼかさないときは、ずらさなければ見えない(前述「メニューの背景色とテキストの影」参照⁠⁠。けれど、box-shadowプロパティには、つぎのように伸縮量が値に加えられる(デフォルト値0⁠⁠。正の値を与えれば、影のぼかしは変えることなく範囲が広がる。

box-shadow: [inset] 水平のずれ 垂直のずれ ぼけ幅 [伸縮量] カラー

メニューバーclass属性"menu")に、つぎのようにわかりやすくシアンの影を多めに拡張してみた。ぼけ幅は0なので、くっきりとした外枠が加わる図5

.menu {    

    box-shadow: 0 0 0 4px cyan;
}
図5 影でメニューバーに太めの外枠が加わる
図5 影でメニューバーに太めの外枠が加わる

改めて、白い細線でつぎのようにbox-shadowプロパティを定め直そう。メニューバーに、項目間の区切り線となじんだ細い外枠がついた図6⁠。

.menu {    

    box-shadow: 0 0 0 1px white;
}
図6 メニューバーに細い外枠が加わった
図6 メニューバーに細い外枠が加わった

メニュー項目にポインタが重なったとき他の項目のテキストをぼかす

いよいよ、メニューのアニメーションを加える。まず、メニューバーにマウスポインタが重なったとき、メニュー項目のテキストをぼかそう。文字をぼかすにも影を活用する。text-shadowプロパティで影をつくったうえで、文字の色はtransparentキーワードで透明にするのだ。メニューバーclass属性"menu"):hover擬似クラスで、<a>要素につぎのような定めを加えた。これで、マウスポインタをメニューバーに重ねると項目のテキストがぼける図7⁠。

.menu:hover a {
    color: transparent;
    text-shadow: 0 0 5px silver;
}
図7 マウスポインタをメニューバーに重ねると項目のテキストがぼける
図7 マウスポインタをメニューバーに重ねると項目のテキストがぼける

ポインタが重なったメニュー項目を浮き上がらせるアニメーション

つぎに、マウスポインタの重なったメニュー項目は、逆にくっきりと示したい。つぎのように、メニュー項目class属性"item"):hover擬似クラスで、背景は明るくし、<a>要素のテキストは暗くして際立たせる。また、テキストには白い影を光彩のように加えた(ほとんどわからないくらいに⁠⁠。これで、メニュー項目にマウスポインタを重ねたとき、他の項目の文字はぼけて、選んだ項目だけがハイライトする図8⁠。

.item:hover {
    background: ghostwhite;
}
.item:hover a {
    color: black;

    text-shadow: 0 1px 1px white;
}
図8 マウスポインタを重ねたメニュー項目がハイライトする
図8 マウスポインタを重ねたメニュー項目がハイライトする

さらに、マウスポインタを重ねたメニュー項目のテキストは、少し上にずらして拡大しよう。transformプロパティtranslateY()関数で垂直移動し、拡大はscale()関数の引数に比率を渡す。引数は2つだとそれぞれ水平と垂直、ひとつなら縦横同じ比率で伸縮する。つぎのように変化は控えめにしたので、静止画図9でわかりづらかったら、冒頭のサンプル1でアニメーションを確かめてほしい。

.item:hover a {

    transform: translateY(-2px) scale(1.02);

}
図9 マウスポインタを重ねたメニュー項目のテキストが浮かび上がる表現
図9 マウスポインタを重ねたメニュー項目のテキストが浮かび上がる表現

滑らかなアニメーションにするため、メニュー項目class属性"item")にはつぎのようにtransitionプロパティを定める。タイミング関数は、立ち上がりに加速するease-inを選んだ図10⁠。transitionプロパティは継承しないので、メニュー項目の<a>要素にも同じ定めを加えた。

.item {

    transition: 0.2s ease-in;
}
.item a {

    transition: 0.2s ease-in;
}
図10 タイミング関数のease-in
図10 タイミング関数のease-in

この図Mozilla Contributorsによるもので、CC-BY-SA 2.5のもとで利用が許諾されています。

マウスボタンを押したメニュー項目を凹ませる

仕上げは、マウスボタンを押したメニュー項目の表現だ。メニュー項目class属性"item"):active擬似クラスに、つぎのようなコードを定める。背景は内側insetに影を加えた。例によって細かい設定として、左側の区切り線は少し暗くして、テキストの色をグレーに落とし、垂直位置は少し戻している図11⁠。そして、transitionプロパティの定めを取り消すことnoneで瞬時に切り替わるようにした。

.item:active {
    border-left-color: whitesmoke;    
    box-shadow: inset 0 0 22px lightgrey;
    transition: none;
}
.item:active a {
    color: dimgray;
    transform: translateY(-1px);
    transition: none;
}
図11 マウスボタンを押したメニュー項目が凹む表現
図11 マウスボタンを押したメニュー項目が凹む表現

これで選択項目以外がぼけるメニューのでき上がりだ。書き上げたCSSの定めは、つぎのコード1にすべてまとめた。説明されて初めて気づいた細かな設定も多かったろう。冒頭のサンプル1で改めて確かめてもらいたい。また、jsdo.itのサイトで開いて[FORK]ボタンを押せば、サンプルのコピーがつくられて、自由に書き替えできる。設定をもっとはっきりした値にしてみるなど、いろいろ試されるとよいだろう。

コード1 選択項目以外がぼけるメニュー
body {
    font-family: Arial, Helvetica, sans-serif;
    background: dimgray;
}
.container {
    height: 50px;
    margin: 40px 0;
    padding: 50px 0;
    text-align: center;
}
.menu {    
    display: inline-block;
    list-style: none;
    margin: 0;
    overflow: hidden;
    padding: 0;
    position: relative;
    background: linear-gradient(ghostwhite, gainsboro);
    box-shadow: 0 0 0 1px white;
}
.item {
    float: left;
    border-left: 1px solid white;
    border-right: 1px solid gainsboro;
    font-size: 14px;
    line-height: 50px;
    padding: 0 35px;
    cursor: pointer;
    transition: 0.2s ease-in;
}
.item a {
    color: gray;
    display: block;
    text-decoration: none;
    text-shadow: 0 1px 0 white;
    transition: 0.2s ease-in;
}
li.item:first-child {
    border-left: none;
}
li.item:last-child {
    border-right: none;
}
.menu:hover a {
    color: transparent;
    text-shadow: 0 0 5px silver;
}
.item:hover {
    background: ghostwhite;
}
.item:hover a {
    color: black;
    transform: translateY(-2px) scale(1.02);
    text-shadow: 0 1px 1px white;
}
.item:active {
    border-left-color: whitesmoke;    
    box-shadow: inset 0 0 22px lightgrey;
    transition: none;
}
.item:active a {
    color: dimgray;
    transform: translateY(-1px);
    transition: none;
}

おすすめ記事

記事・ニュース一覧