LIFULL Creators Blog

「株式会社LIFULL(ライフル)」の社員によるブログです。

Sassで寿司を回す、たった一つの冴えたやりかた

f:id:homes_designers:20151229142750j:plain
こんにちは、ウェッブデザイナーのアズマです。
みなさん、Sassは好きですか。僕は好きです。
そして、寿司は好きですか。僕は好きです。

Sassがあれば何でもできる!
Sassがあれば、回転寿司も回せる!
というわけで、今日はSass(CSS)を使って回転寿司のように要素を無限ループさせてみましょう。

何ができるか

f:id:homes_designers:20151229111432g:plain
こうです。寿司が無限ループしています。(本当はもっとなめらかに動きます)
僕の圧倒的な画力についての感想はここでは本質ではないのでご遠慮願います。ここで大事なのは寿司が無限ループすることです。

「画像が流れればそれでよい」ということであれば、1枚の横長背景画像にしてbackground-positionを動かすという手法や、いっそのこと全部Gifアニメにしちゃう(寿司ゆき超かわいい)という手法もありますが、
例えば「今後要素の数がちょくちょく増える予感がある」とか「できるだけセマンティックに作りたい」とか「なんか負けた気がする」などといった理由で背景画像にはしたくない、そんな夜もあるはずです。

ただ寿司が回ればいいのか?
否、回すなら美しく回さなければならない。そうでしょう?
回しましょう、今宵、美しい寿司を。 ====

完成形のコード

というわけでSassの出番です。
寿司の個数に合わせて数値を計算したり、CSSで要素ごとにアニメーションを設定しようとすると日が暮れてしまうので、Sassで一気に作っちゃいましょう。

まずHTMLはこうです。何の変哲もないリストですね。ちなみに回転寿司は英語でsushi-go-roundです。本当です。

<ul class="sushiGoRound">
    <li><img src="img/ebi.jpg" alt="えび"></li>
    <li><img src="img/ikura.jpg" alt="いくら"></li>
    <li><img src="img/maguro.jpg" alt="まぐろ"></li>
    <li><img src="img/shimesaba.jpg" alt="しめさば"></li>
    <li><img src="img/tako.jpg" alt="たこ"></li>
    <li><img src="img/tamago.jpg" alt="たまご"></li>
</ul>

そしてSassはこうです。

// 寿司を回すための変数を設定
$itemTotal: 6;  // 寿司の総数
$speed: 50; // 寿司の速度

$duration: ($itemTotal*100) / $speed;

.sushiGoRound {
    position: relative;
    overflow: hidden;
    width: 500px;
    height: 100px;
    padding: 0;
    background: url('img/bg.jpg');
    border: 1px solid #333;
    li {
        position: absolute;
        width: 100px;
        height: 100px;
        list-style: none;
        animation: slide #{$duration}s linear 0s infinite;
    }
    li img {
        width: 94px;
        height: 94px;
        border: 3px solid #fff;
        border-radius: 50%;
        box-shadow: 0 3px 6px rgba(0,0,0,0.1);
    }
    // 寿司の動き
    $offset: $itemTotal * 100%;
    @keyframes slide {
        0% {
            transform: translateX(0)
        }
        50% {
            transform: translateX(-$offset);
            visibility: hidden;
        }
        51% {
            transform: translateX($offset);
            visibility: hidden;
        }
        52% {
            transform: translateX($offset);
            visibility: visible;
        }
    }
    // 寿司ごとのずらし
    @for $i from 1 through ($itemTotal) {
        li:nth-of-type(#{$i}) {
            $delay: #{($i - 1 - $itemTotal) * ($duration / $itemTotal)}s;
            animation-delay: $delay;
        }
    }
}

$itemTotal(寿司の総数)はお手元の寿司の数に合わせて各々入れてください。
$speed(速度)は50〜100ぐらいが適当です。あまりに遅いといわゆる「寿司が止まって見える」状態になります。
f:id:homes_designers:20151229114311g:plain
へへっ…俺には寿司が止まって見えるぜ…

何をしているのか

大まかな流れとしては、

  1. 寿司を全部同じ位置に配置する
  2. 寿司をループさせて動かす
  3. 寿司の動きにズレを加える

となります。だんだん寿司とは何か、わからなくなってきましたね。哲学的な問いです。

1. 寿司を全部同じ位置に配置する

今回はul.sushiGoRoundが寿司のレーン、その中のli要素がそれぞれの皿にあたります。
まずはみんなお馴染みposition: absoluteで、li要素を全部重ねてul要素の端に配置してしまいます。

.sushiGoRound {
    position: relative;
    overflow: hidden;
    width: 500px;
    height: 100px;
    padding: 0;
    background: url('img/bg.jpg');
    border: 1px solid #333;
    li {
        position: absolute;
        width: 100px;
        height: 100px;
        list-style: none;
    }
    li img {
        width: 94px;
        height: 94px;
        border: 3px solid #fff;
        border-radius: 50%;
        box-shadow: 0 3px 6px rgba(0,0,0,0.1);
    }
}
2. 寿司をループさせて動かす

準備は整いました。さっそく寿司を回しましょう。
…と、その前に、寿司の総数と速度を設定する変数をそこら辺に入れておきます。

// 寿司を回すための変数を設定
$itemTotal: 6;  // 寿司の総数
$speed: 50; // 寿司の速度
$duration: ($itemTotal*100) / $speed;

li要素に

        animation: slide #{$duration}s linear 0s infinite;

を指定し、適当なところに以下を追記しましょう。

// 寿司の動き
$offset: $itemTotal * 100%;
@keyframes slide {
    0% {
        transform: translateX(0)
    }
    49% {
        transform: translateX(-$offset);
        visibility: hidden;
    }
    50% {
        transform: translateX($offset);
        visibility: hidden;
    }
    51% {
        transform: translateX($offset);
        visibility: visible;
    }
}

ここで何をしているのかというと、こういうことです。
f:id:homes_designers:20151229115125g:plain

要は、6つの寿司をぶつからないように、かつ変な間が空かないようにぴったりループさせるには、
1 寿司を初期地点から6つ分左に動かす(0%〜49%)
2 寿司を初期地点から6つ分右に瞬間移動させる(49%〜50%)
3 寿司を初期地点に戻す(51%〜100%(0%))
というアニメーションを絶え間なく続ける必要があります。これを行うためのコードです。

ここでのポイントは、1の動きと3の動きが同じ距離であることです。
これによって回転寿司のレーンのような美しい等速直線運動が生み出されます。

3. 寿司の動きにズレを加える
// 寿司ごとのずらし
@for $i from 1 through ($itemTotal) {
    li:nth-of-type(#{$i}) {
        $delay: #{($i - 1 - $itemTotal) * ($duration / $itemTotal)}s;
        animation-delay: $delay;
    }
}

以上のコードでは寿司ごとにアニメーション開始タイミングのズレを発生させています。
ここでのポイントは、delayにマイナスの値を入れていることです。
animation-delayがプラスの値だと寿司が動き出すまでに時間がかかりますが、マイナスの値を入れることによって、「既にx秒動いた状態」から開始されることになります。
これによって、結果的に各寿司があるべき場所に配置され、なめらかに動き出すというわけです。

何の役に立つのか

いかがでしたでしょうか?
「Sassで寿司なんて回して一体何の役に立つんだ、働け」という声が聞こえてくるようですが、
f:id:homes_designers:20151230172640g:plain
先日HOME'Sからリリースされた新サービス「暮らし図鑑」(スマホ用)のトップ画面では、かわいいキャラクターをこの回転寿司メソッドでくるくる動かしてディスプレイしています。(残念ながらイラストを書いたのは僕ではありません。)

「狭い画面で、たくさんの要素を、邪魔にならないように見てもらう」という目的に対して使える手法ではないでしょうか。

ちなみにこの「暮らし図鑑」、約200人から集めたライフスタイルやリアルな生活事情をもとに人々をタイプ分けして、自分と似たタイプの人の引っ越しを見られることで、ユーザーがじゃぶじゃぶ引っ越したくなるような住み替え心を煽るサービスとなっておりますので、ぜひお試しください。そして引っ越したくなってください。
www.homes.co.jp

 

弱点

「たった一つの」とか言っておいてアレなんですが、この回転寿司メソッドにはまだ「寿司の間隔調整が難しい」「寿司の数が変わるたびに変数を手で差し替えないといけない」などの弱点があります。
「俺ならもっと美しく寿司を回してみせるぜ!」という方はコメント欄などからアドバイスいただけると嬉しいです!それでは、よき寿司を!