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

第21回スライドする立方体

今回のお題は、3つの立方体のマウスインタラクションだ。マウスポインタを重ねた立方体の色が変わり、手前にスライドするサンプル1⁠。実のところ、3次元の操作は加えていない。2次元の変換で立体風に見せているだけだ。3D cubes built and animated with CSSのデザインと表現をもとに、HTMLとCSSの組み立てはわかりやすく改めた。

サンプル1 CSS3: 3D Cubes

インタラクションを加える前の立方体の表現

まずは、インタラクションを加える前の静的な組み立てだ。<body>要素にはつぎのように3つの立方体の<div>要素class属性"cube")を加える。立方体は3つとも同じように上class属性"face-top")と左右class属性"face-left"と"face-right")の3面をもつ。ただ、上面class属性"face-top")だけ<div>要素を入れ子にしている。左右の面は傾斜の変換だけ加えれば済むのに対し、上面はさらに回転しなければならないからだ。

<body>要素
<div class="container">
    <div class="cube">
        <div class="face-top"><div>  <!-- 入れ子要素 -->
            <h2>Top face</h2>
            <p>The top face is nested in an extra div tag to give correct rotation</p>
        </div></div>
        <div class="face-left">
            <h2>Left face</h2>
            <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum</p>
        </div>
        <div class="face-right">
            <h2>Right face</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod</p>
        </div>
    </div>
    <div class="cube two">
        <!-- 同上 -->
    </div>
    <div class="cube three">
        <!-- 同上 -->
    </div>
</div>

transformプロパティによる変換を加える前、3つの立方体の面の要素はつぎの図1のように位置や大きさ、色などが決めてある(後掲コード1参照⁠⁠。

図1 変形を加える前の3つの立方体の面
図1 変形を加える前の3つの立方体の面

立方体それぞれの3つの面は、つぎのようにtransformプロパティで変換する。基本は傾斜のskewY()関数だ。さらに、上面はscaleY()関数により縦横比を整えたうえで、前述のとおりrotate()関数で親要素class属性"face-top")を回転した。これで、2次元の変換により、3つの面が立方体として表現できる図2⁠。回転や傾斜が組み合わさる上面のような場合は、こうして要素を入れ子にすると、分けて調整がしやすい。

<style>要素
.face-left {
    transform: skewY(30deg);

}
.face-right {
    transform: skewY(-30deg);

}
.face-top div {
    transform: skewY(-30deg) scaleY(1.16);

}
.face-top {
    transform: rotate(60deg);

}
図2 3面の要素を変換して組み上がった3つの立方体
図2 3面の要素を変換して組み上がった3つの立方体

インタラクションを加える前の<body>要素とCSSの定めは、つぎのコード1にまとめた。実際につくってみると、細かく位置や大きさを合わせるのに少しばかり手間がかかる。面は左から、立方体は右奥から決めていった。コードそのものはとくにむずかしいところはないだろう。なお、いつものように-prefix-freeをCDNから読み込んである(第16回の水平に並べた要素に静的なスタイルを割り当てる参照)⁠。

コード1 インタラクションを加える前の立方体のHTMLコードとスタイル
<body>要素

<div class="container">
    <div class="cube">
        <div class="face-top"><div>
            <h2>Top face</h2>
            <p>The top face is nested in an extra div tag to give correct rotation</p>
        </div></div>
        <div class="face-left">
            <h2>Left face</h2>
            <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum</p>
        </div>
        <div class="face-right">
            <h2>Right face</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod</p>
        </div>
    </div>
    <div class="cube two">
        <div class="face-top"><div>
            <h2>Top face</h2>
            <p>The top face is nested in an extra div tag to give correct rotation</p>
        </div></div>
        <div class="face-left">
            <h2>Left face</h2>
            <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum</p>
        </div>
        <div class="face-right">
            <h2>Right face</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod</p>
        </div>
    </div>
    <div class="cube three">
        <div class="face-top"><div>
            <h2>Top face</h2>
            <p>The top face is nested in an extra div tag to give correct rotation</p>
        </div></div>
        <div class="face-left">
            <h2>Left face</h2>
            <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum</p>
        </div>
        <div class="face-right">
            <h2>Right face</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod</p>
        </div>
    </div>
</div>
<style>要素
body {
    line-height: 1.5em;
    color: #666;
    background: #f1ebe2;
    font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
    font-weight: 200;
}
p, h2 {
    margin: 0;
    font-size: 100%;
}
.container {
    min-height: 500px;
}
.cube {
    position: absolute;
    left: 50%;
    top: 150px;
}
.cube p {
    line-height: 14px;
    font-size: 12px;
    margin-bottom: 24px;
}
.cube h2 {
    font-weight: bold;
}
.cube.two {
    top: 213px;
    left: 48%;
    margin-left: -100px;
}
.cube.three {
    top: 276px;
    left: 46%;
    margin-left: -200px;
}
.face-right,
.face-left,
.face-top div {
    padding: 10px;
    width: 90px;
    height: 90px;
}
.face-right,
.face-left,
.face-top {
    position: absolute;
}
.face-left {
    transform: skewY(30deg);
    background-color: #ccc;
}
.face-right {
    transform: skewY(-30deg);
    background-color: #ddd;
    left: 109px;
}
.face-top div {
    transform: skewY(-30deg) scaleY(1.16);
    background-color: #eee;
    font-size: 0.862em;
}
.face-top {
    transform: rotate(60deg);
    top: -86px;
    left: 55px;
}

マウスポインタが重なった立方体の色を変える

立方体にマウスポインタが重なったら:hover擬似クラス⁠⁠、3つの面の色をつぎのように変える。さらに、ポインタの乗った面は色を濃くしている図3⁠。

<style>要素
.cube:hover .face-right,
.cube:hover .face-left,
.cube:hover .face-top div {
    background-color: #ffc;
}
.cube:hover .face-right:hover,
.cube:hover .face-left:hover,
.cube:hover .face-top:hover div {
    background-color: #ffa;
}
図3 立方体にマウスポインタを重ねると色が変わる
図3 立方体にマウスポインタを重ねると色が変わる

マウスポインタが重なった立方体をスライドさせる

マウスポインタが重なった立方体はスライドさせよう。CSSは、つぎのようにtransformプロパティにtranslate()関数を定めるだけだ。とはいえ、これもピクセル単位で細かく調整しなければならない。transitionプロパティには、パラメータとして変化させたいプロパティtransformを加えた。いつものように省けば、デフォルトはすべてallとされるので、それでも差し支えはない。タイミング関数は、機械的な線形のlinearを選んだ。

<style>要素
.cube {
    transition: transform 1s linear;
}
.cube:hover {
    transform: translate(108px, 64px);
}

これで3つの立方体が組み立てられ、マウスポインタを重ねると色が変わってスライドする図4⁠。CSSの定めは、以下のコード2にまとめた。はじめの見た目より簡単に感じたのではないだろうか。3次元でなく2次元の変換を使ったためだ。だが、大きさや角度を変えたりすると、ピクセル単位の調整に少し手間どるかもしれない。さて、昨年1月からはじまった本連載は、今回で幕を閉じる。JavaScriptは使わず、CSSだけでさまざまなインターフェイス表現をつくってきた。その可能性を少しでも感じていただけたなら幸いである。

図4 立方体にマウスポインタを重ねた立方体の色が変わってスライドする
図4 立方体にマウスポインタを重ねた立方体の色が変わってスライドする

コード2 ポインタを重ねた立方体の色が変わってスライドする
<style>要素

body {
    line-height: 1.5em;
    color: #666;
    background: #f1ebe2;
    font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
    font-weight: 200;
}
p, h2 {
    margin: 0;
    font-size: 100%;
}
.container {
    min-height: 500px;
}
.cube {
    position: absolute;
    left: 50%;
    top: 150px;
}
.cube p {
    line-height: 14px;
    font-size: 12px;
    margin-bottom: 24px;
}
.cube h2 {
    font-weight: bold;
}
.cube.two {
    top: 213px;
    left: 48%;
    margin-left: -100px;
}
.cube.three {
    top: 276px;
    left: 46%;
    margin-left: -200px;
}
.face-right,
.face-left,
.face-top div {
    padding: 10px;
    width: 90px;
    height: 90px;
}
.face-right,
.face-left,
.face-top {
    position: absolute;
}
.cube:hover .face-right,
.cube:hover .face-left,
.cube:hover .face-top div {
    background-color: #ffc;
}
.cube:hover .face-right:hover,
.cube:hover .face-left:hover,
.cube:hover .face-top:hover div {
    background-color: #ffa;
}
.face-left {
    transform: skewY(30deg);
    background-color: #ccc;
}
.face-right {
    transform: skewY(-30deg);
    background-color: #ddd;
    left: 109px;
}
.face-top div {
    transform: skewY(-30deg) scaleY(1.16);
    background-color: #eee;
    font-size: 0.862em;
}
.face-top {
    transform: rotate(60deg);
    top: -86px;
    left: 55px;
}
.cube {
    transition: transform 1s linear;
}
.cube:hover {
    transform: translate(108px, 64px);
}

おすすめ記事

記事・ニュース一覧