SVGアニメーションでYouTubeのLikeボタンつくったよ

SVGアニメーションでYouTubeのLikeボタンつくったよ

お久しぶりです、あんずです。
今年は月に1記事書こうと思ってからもう3ヶ月経過しました。今月からが今年です。

さて、最近はインタラクション一つ一つが凝っていることが多くなってきましたね。3D化も進んでいるし、本当にそのうち仮想空間で暮らすことになりそうです。
キリトかなーやっぱw

そんなインタラクションですが、YouTubeのライクボタンがかなりいい味出してたので、つくってみました。
YouTubeは多分Adobe After Effectsで作って、アニメーションを書き出していますが、今回はCSSを自分で書いてアニメーションを作りました。

少しずつ動きのリアル感は損なわれていますが、軽めのインタラクションとしてはいい感じに仕上がったんじゃないかと思います。

こんな感じ

目次

1. アニメーションの動きをとらえる

2. SVG素材を用意する

3. アニメーションをつける

4. 押したらアニメーションするようにする

5. まとめ

1. アニメーションの動きをとらえる

まずは作りたいアニメーションのコマ割りをとらえてざっくりメモします。

メモから、なんとなく構造を理解して、CSSでは拡大縮小と回転、色の変化を使用することが主になりそうなど、CSSに落とし込むための準備とどんな素材が必要かを考察します。

素材は、指とはっちゃけるための線とぱやぱやして消えるための花火?みたいなやつが必要そうです。

2. SVG素材を用意する

1で必要素材がなんとなく理解できたので、次にSVGを用意します。

今回、SVGはAdobe Illustratorで作成していますが、Figmaなどの無料アプリでも書き出せるので好きなアプリで作成してください。

作った素材はこんな感じで、重なる部分はわかりやすいように色をつけています。

指はMaterial Symbolsから拝借しています。

はっちゃけた部分が、ぱやぱやして消えるところまで、1つのアニメーションにならなければいけないので、線の終わりに円の中心を置いて、アニメーションが続くように素材を作成します。

そこから、コマごとに素材を切り分けて書き出します。
1つにまとめて書き出しても問題はありませんが、今回は1素材ずつアニメーションをつけていく手法をとったので、コマごとに書き出しています。

3.  アニメーションをつける

SVGファイルをエディタで開いて、アニメーションをつけていきます。
まずは、手の部分のアニメーションから。

HTML

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="40" height="40">
  <path class="hand" d="m17.5,29.71c-.55,0-1.02-.2-1.41-.59-.39-.39-.59-.86-.59-1.41v-10.18c0-.27.05-.52.16-.76.11-.24.25-.45.44-.64l5.43-5.4c.25-.23.55-.38.89-.42.34-.05.67,0,.99.17.32.17.55.4.69.7.14.3.17.61.09.92l-1.12,4.6h5.45c.53,0,1,.2,1.4.6.4.4.6.87.6,1.4v2c0,.12-.01.24-.04.38-.02.13-.06.26-.11.38l-3,7.05c-.15.33-.4.62-.75.85-.35.23-.72.35-1.1.35h-8Zm-6,0c-.55,0-1.02-.2-1.41-.59-.39-.39-.59-.86-.59-1.41v-9c0-.55.2-1.02.59-1.41.39-.39.86-.59,1.41-.59s1.02.2,1.41.59c.39.39.59.86.59,1.41v9c0,.55-.2,1.02-.59,1.41-.39.39-.86.59-1.41.59Z"/> 
</svg>   

CSS

.hand {
    animation: hand 1.5s ease-in;
    fill: #fff;
    transform-origin: center;
    transform: scale(1) rotate(0deg);
}

@keyframes hand {
    0% {
        fill: #fff;
        transform: scale(.3) rotate(10deg);
    }
    5%{
        transform: scale(.5) rotate(10deg);
    }
    20% {
        fill: #cbfcc9;
        transform: scale(1.3) rotate(-15deg);
    }
    40% {
        fill: #ab71f7;
        transform: scale(1.3) rotate(-30deg);
    }
    55% {
        transform: scale(1.3) rotate(-30deg);
    }
    60% {
        fill: #ddd926;
        transform: scale(1) rotate(10deg);
    }
    66% {
        fill: #f28032;
        transform: scale(1) rotate(2deg);
    }
    70% {
        fill: #fe443b;
        transform: scale(1) rotate(0deg);
    }
    100%{
        fill: #fff;
        transform: scale(1) rotate(0deg);
    }
}

pathにhandというclass名をつけて、アニメーションをつけます。
わかりやすいように、animationの名前もhandにします。

1で動きを捉えた通り、handは拡大縮小を繰り返しながら一番拡大したところで、一番角度をつけて止まって、もとの大きさと位置に戻ります。

このアニメーションはすべてsvgの中心点で行われる必要があるため、transform-origin: center;を追加しています。

40%〜55%はアニメーションの指定がほとんど一緒ですが、ここが一度角度をつけて止まる部分です。ここは、色以外アニメーションしないようにしています。

また、60%〜100%の色の変化は、多少強引なグラデーションのため、きれいに色が変化するように間に色の指定をしています。
CSSのグラデーションは間の色が汚いグレーになることはありませんが、本当に数値上のグラデーションをするので人間の視覚的に違和感があるものになることがあります。
なので、スピードのあるアニメーションでグラデーションをFillを変更してグラデーションをかける場合は目で見て調整する必要があります。

次に、はっちゃける部分です
ここがいちばんむずかしいとおもいます

HTML

<line class="line line-1" x1="33.2" y1="33.67" x2="37.16" y2="37.63"/>
<line class="line line-4" x1="32.08" y1="8.25" x2="36.74" y2="3.59"/>
<line class="line line-3" x1="8.73" y1="7.3" x2="4.63" y2="3.2"/>
<line class="line line-2" x1="8.33" y1="35.38" x2="5.72" y2="37.99"/>
<path class="line line-2" d="m24.47,8.03c-1.32-1.84,1.6-5.11,2.06-2.97.37,1.74-4.2,0-2.68-2.97"/>
<path class="line line-6" d="m27.15,32.66c.75,1.37-2.07,5.62-2.82,3.96-.64-1.42,3.02-1.3,3.76,1.36"/>
<line class="line line-7" x1="33.46" y1="29.71" x2="37.97" y2="29.71"/>
<line class="line line-5" x1="7.56" y1="13.99" x2="2.91" y2="13.99"/>

CSS

.line {
    fill: transparent;
    stroke-dasharray: 40;
    stroke-dashoffset: 40;
    stroke-linecap:round;
    stroke-linejoin:round;
}

.line-1 {
    stroke: #fb493f;
    stroke-width: 2;
    animation: first 1.5s ease-in;
}

.line-2 {
    stroke: #cbfcc9;
    stroke-width: 1.5;
    animation: first 1.5s ease-in;
}

.line-3 {
    stroke: #dec83c;
    stroke-width: 3;
    animation: second 1.5s ease-in;
}

.line-4 {
    stroke: #cbfcc9;
    stroke-width: 2;
    animation: second 1.5s ease-in;
}

.line-5 {
    stroke: #fb493f;
    stroke-width: 2;
    animation: third 1.5s ease-in;
}

.line-6 {
    stroke: #ab71f7;
    stroke-width: 2;
    animation: third 1.5s ease-in;
}

.line-7 {
    stroke: #dec83c;
    stroke-width: 2;
    animation: third 1.5s ease-in;
}
@keyframes first {
    20% {
        stroke-dashoffset: 40;
    }
    40% {
        stroke-dashoffset: 0;
    }
    50% {
        stroke-dashoffset: 40;
    }
}

@keyframes second {
    30% {
        stroke-dashoffset: 40;
    }
    40% {
        stroke-dashoffset: 0;
    }
    50% {
        stroke-dashoffset: 40;
    }
}

@keyframes third {
    35% {
        stroke-dashoffset: 40;
    }
    40% {
        stroke-dashoffset: 0;
    }
    50% {
        stroke-dashoffset: 40;
    }
}

handのsvgにpathを追加します。

lineというclass名を共通で挿入します。
そこで、基本となる線の長さや丸い線にする指定をします。

次に、lineそれぞれ色や太さ、アニメーションが違うためそれぞれclass名を付与してstyleを指定します。

最後に、アニメーションを書きます。
ここでは、一番最初に動くものをfirstと名付けてsecond, thirdと続かせます。

50%ですべてのアニメーションが元の位置に戻るように設定し、ぱやぱやのアニメーションへ連結させるようにします。

stroke-dashoffsetとstroke-dasharrayは下記サイトを見るとかなりわかりやすく理解できると思います。

最後に、ぱやぱやさせる部分です。

まず、上でやったアニメーションを接続するための円にアニメーションをつけます。

HTML

            <circle class="circle-1" cx="8.85" cy="7.44" r="1.5"/>
            <circle class="circle-2" cx="33.2" cy="33.67" r="1"/>
            <circle class="circle-3" cx="32.08" cy="8.25" r=".75"/>
            <circle class="circle-3" cx="8.33" cy="35.38" r=".75"/>

CSS

.circle-1 {
    fill: #dec83c;
    opacity: 0;
    animation: circle 1.5s ease-in;
}

.circle-2 {
    fill: #fb493f;
    opacity: 0;
    animation: circle 1.5s ease-in;
}

.circle-3 {
    fill: #cbfcc9;
    opacity: 0;
    animation: circle 1.5s ease-in;
}

@keyframes circle {
    0% {
        opacity: 0;
    }
    49% {
        opacity: 0;
    }
    50% {
        opacity: 1;
    }
    60% {
        opacity: 0;
    }
    100% {
        opacity: 0;
    }
}

49%までは見えなくて、50%、つまり線のアニメーションが終わるタイミングで表示されるようにします。

そして、60%までに透過させながら消えさせます。

そこと一緒にぱやぱやさせる花火みたいなものをつけます

HTML

            <path class="flower-1" d="m9.1,5.37c-.24.14-.54.06-.68-.18s-.06-.54.18-.68.54-.06.68.18.06.54-.18.68Zm-2.42.32c-.28,0-.5.22-.5.5,0,.28.22.5.5.5s.5-.22.5-.5c0-.28-.22-.5-.5-.5Zm-.43,2.75c-.14.24-.06.54.18.68s.54.06.68-.18.06-.54-.18-.68-.54-.06-.68.18Zm2.17,1.75c.14.24.44.32.68.18s.32-.44.18-.68-.44-.32-.68-.18-.32.44-.18.68Zm2.6-1c.28,0,.5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5Zm.43-2.75c.14-.24.06-.54-.18-.68s-.54-.06-.68.18-.06.54.18.68.54.06.68-.18Z"/>
            <path class="flower-2" d="m7.83,33.13c0-.28.22-.5.5-.5s.5.22.5.5c0,.28-.22.5-.5.5s-.5-.22-.5-.5Zm-1.02,1.38c.14-.24.06-.54-.18-.68s-.54-.06-.68.18-.06.54.18.68.54.06.68-.18Zm0,1.75c-.14-.24-.44-.32-.68-.18s-.32.44-.18.68.44.32.68.18.32-.44.18-.68Zm1.52.88c-.28,0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5Zm1.52-.87c-.14.24-.06.54.18.68s.54.06.68-.18.06-.54-.18-.68-.54-.06-.68.18Zm0-1.75c.14.24.44.32.68.18s.32-.44.18-.68-.44-.32-.68-.18-.32.44-.18.68Z"/>
            <path class="flower-3" d="m32.7,36.17c0-.28.22-.5.5-.5s.5.22.5.5c0,.28-.22.5-.5.5s-.5-.22-.5-.5Zm3.1-1c.14-.24.06-.54-.18-.68s-.54-.06-.68.18-.06.54.18.68.54.06.68-.18Zm0-3c-.14-.24-.44-.32-.68-.18s-.32.44-.18.68.44.32.68.18.32-.44.18-.68Zm-2.6-1.5c-.28,0-.5.22-.5.5,0,.28.22.5.5.5s.5-.22.5-.5-.22-.5-.5-.5Zm-2.6,1.5c-.14.24-.06.54.18.68s.54.06.68-.18.06-.54-.18-.68-.54-.06-.68.18Zm0,3c.14.24.44.32.68.18s.32-.44.18-.68-.44-.32-.68-.18-.32.44-.18.68Z"/>
            <path class="flower-2" d="m32.58,6c0,.28-.22.5-.5.5s-.5-.22-.5-.5.22-.5.5-.5.5.22.5.5Zm-2.88.87c-.14.24-.06.54.18.68s.54.06.68-.18.06-.54-.18-.68-.54-.06-.68.18Zm0,2.75c.14.24.44.32.68.18s.32-.44.18-.68-.44-.32-.68-.18-.32.44-.18.68Zm2.38,1.38c.28,0,.5-.22.5-.5,0-.28-.22-.5-.5-.5s-.5.22-.5.5c0,.28.22.5.5.5Zm2.38-1.37c.14-.24.06-.54-.18-.68s-.54-.06-.68.18-.06.54.18.68.54.06.68-.18Zm0-2.75c-.14-.24-.44-.32-.68-.18s-.32.44-.18.68.44.32.68.18.32-.44.18-.68Z"/>

CSS

.flower-1 {
    fill: #dec83c;
    opacity: 0;
    animation: flower-1 1.5s ease-in;
}

.flower-2 {
    fill: #cbfcc9;
    opacity: 0;
    animation: flower-2 1.5s ease-in;
}

.flower-3 {
    fill: #fb493f;
    opacity: 0;
    animation: flower-1 1.5s ease-in;
}

@keyframes flower-1 {
    0% {
        opacity: 0;
    }
    49% {
        opacity: 0;
    }
    50% {
        opacity: .5;
    }
    55% {
        opacity: 1;
    }
    70% {
        opacity: 0;
    }
    100% {
        opacity: 0;
    }
}

@keyframes flower-2 {
    0% {
        opacity: 0;
    }
    55% {
        opacity: 0;
    }
    60% {
        opacity: .5;
    }
    65% {
        opacity: 1;
    }
    80% {
        opacity: 0;
    }
    100% {
        opacity: 0;
    }
}

花火はcircleと同じタイミングで発生するものと、少しずれて発生するものを作成し、それぞれ、handが元に戻るタイミングで消えるようにします。

4.  押したらアニメーションするようにする

アニメーションが完成したので、押したらアニメーションするように設定します。
いろいろなやり方がありますが、今回は一番簡易的なcheckboxで実装します。

checkboxにチェックがついた時にアニメーションが設定されたSVGを表示して、チェックが外れると、別のSVGが表示されるような仕様です。

HTML

    <input type="checkbox" id="check">
    <label for="check">
        <svg class="unchecked" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="40" height="40" stroke="#fff" stroke-width="1.5" fill="none">
            <path d="m17.5,29.71c-.55,0-1.02-.2-1.41-.59-.39-.39-.59-.86-.59-1.41v-10.18c0-.27.05-.52.16-.76.11-.24.25-.45.44-.64l5.43-5.4c.25-.23.55-.38.89-.42.34-.05.67,0,.99.17.32.17.55.4.69.7.14.3.17.61.09.92l-1.12,4.6h5.45c.53,0,1,.2,1.4.6.4.4.6.87.6,1.4v2c0,.12-.01.24-.04.38-.02.13-.06.26-.11.38l-3,7.05c-.15.33-.4.62-.75.85-.35.23-.72.35-1.1.35h-8Zm-6,0c-.55,0-1.02-.2-1.41-.59-.39-.39-.59-.86-.59-1.41v-9c0-.55.2-1.02.59-1.41.39-.39.86-.59,1.41-.59s1.02.2,1.41.59c.39.39.59.86.59,1.41v9c0,.55-.2,1.02-.59,1.41-.39.39-.86.59-1.41.59Z"/>
        </svg>
        <svg class="checked" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="40" height="40">
            <circle class="circle-1" cx="8.85" cy="7.44" r="1.5"/>
            <circle class="circle-2" cx="33.2" cy="33.67" r="1"/>
            <circle class="circle-3" cx="32.08" cy="8.25" r=".75"/>
            <circle class="circle-3" cx="8.33" cy="35.38" r=".75"/>
            <path class="flower-1" d="m9.1,5.37c-.24.14-.54.06-.68-.18s-.06-.54.18-.68.54-.06.68.18.06.54-.18.68Zm-2.42.32c-.28,0-.5.22-.5.5,0,.28.22.5.5.5s.5-.22.5-.5c0-.28-.22-.5-.5-.5Zm-.43,2.75c-.14.24-.06.54.18.68s.54.06.68-.18.06-.54-.18-.68-.54-.06-.68.18Zm2.17,1.75c.14.24.44.32.68.18s.32-.44.18-.68-.44-.32-.68-.18-.32.44-.18.68Zm2.6-1c.28,0,.5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5Zm.43-2.75c.14-.24.06-.54-.18-.68s-.54-.06-.68.18-.06.54.18.68.54.06.68-.18Z"/>
            <path class="flower-2" d="m7.83,33.13c0-.28.22-.5.5-.5s.5.22.5.5c0,.28-.22.5-.5.5s-.5-.22-.5-.5Zm-1.02,1.38c.14-.24.06-.54-.18-.68s-.54-.06-.68.18-.06.54.18.68.54.06.68-.18Zm0,1.75c-.14-.24-.44-.32-.68-.18s-.32.44-.18.68.44.32.68.18.32-.44.18-.68Zm1.52.88c-.28,0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5Zm1.52-.87c-.14.24-.06.54.18.68s.54.06.68-.18.06-.54-.18-.68-.54-.06-.68.18Zm0-1.75c.14.24.44.32.68.18s.32-.44.18-.68-.44-.32-.68-.18-.32.44-.18.68Z"/>
            <path class="flower-3" d="m32.7,36.17c0-.28.22-.5.5-.5s.5.22.5.5c0,.28-.22.5-.5.5s-.5-.22-.5-.5Zm3.1-1c.14-.24.06-.54-.18-.68s-.54-.06-.68.18-.06.54.18.68.54.06.68-.18Zm0-3c-.14-.24-.44-.32-.68-.18s-.32.44-.18.68.44.32.68.18.32-.44.18-.68Zm-2.6-1.5c-.28,0-.5.22-.5.5,0,.28.22.5.5.5s.5-.22.5-.5-.22-.5-.5-.5Zm-2.6,1.5c-.14.24-.06.54.18.68s.54.06.68-.18.06-.54-.18-.68-.54-.06-.68.18Zm0,3c.14.24.44.32.68.18s.32-.44.18-.68-.44-.32-.68-.18-.32.44-.18.68Z"/>
            <path class="flower-2" d="m32.58,6c0,.28-.22.5-.5.5s-.5-.22-.5-.5.22-.5.5-.5.5.22.5.5Zm-2.88.87c-.14.24-.06.54.18.68s.54.06.68-.18.06-.54-.18-.68-.54-.06-.68.18Zm0,2.75c.14.24.44.32.68.18s.32-.44.18-.68-.44-.32-.68-.18-.32.44-.18.68Zm2.38,1.38c.28,0,.5-.22.5-.5,0-.28-.22-.5-.5-.5s-.5.22-.5.5c0,.28.22.5.5.5Zm2.38-1.37c.14-.24.06-.54-.18-.68s-.54-.06-.68.18-.06.54.18.68.54.06.68-.18Zm0-2.75c-.14-.24-.44-.32-.68-.18s-.32.44-.18.68.44.32.68.18.32-.44.18-.68Z"/>
            <line class="line line-1" x1="33.2" y1="33.67" x2="37.16" y2="37.63"/>
            <line class="line line-4" x1="32.08" y1="8.25" x2="36.74" y2="3.59"/>
            <line class="line line-3" x1="8.73" y1="7.3" x2="4.63" y2="3.2"/>
            <line class="line line-2" x1="8.33" y1="35.38" x2="5.72" y2="37.99"/>
            <path class="line line-2" d="m24.47,8.03c-1.32-1.84,1.6-5.11,2.06-2.97.37,1.74-4.2,0-2.68-2.97"/>
            <path class="line line-6" d="m27.15,32.66c.75,1.37-2.07,5.62-2.82,3.96-.64-1.42,3.02-1.3,3.76,1.36"/>
            <line class="line line-7" x1="33.46" y1="29.71" x2="37.97" y2="29.71"/>
            <line class="line line-5" x1="7.56" y1="13.99" x2="2.91" y2="13.99"/>
            <path class="hand" d="m17.5,29.71c-.55,0-1.02-.2-1.41-.59-.39-.39-.59-.86-.59-1.41v-10.18c0-.27.05-.52.16-.76.11-.24.25-.45.44-.64l5.43-5.4c.25-.23.55-.38.89-.42.34-.05.67,0,.99.17.32.17.55.4.69.7.14.3.17.61.09.92l-1.12,4.6h5.45c.53,0,1,.2,1.4.6.4.4.6.87.6,1.4v2c0,.12-.01.24-.04.38-.02.13-.06.26-.11.38l-3,7.05c-.15.33-.4.62-.75.85-.35.23-.72.35-1.1.35h-8Zm-6,0c-.55,0-1.02-.2-1.41-.59-.39-.39-.59-.86-.59-1.41v-9c0-.55.2-1.02.59-1.41.39-.39.86-.59,1.41-.59s1.02.2,1.41.59c.39.39.59.86.59,1.41v9c0,.55-.2,1.02-.59,1.41-.39.39-.86.59-1.41.59Z"/>    
        </svg>
    </label>

CSS

input {
    display: none;
}
input + label {
    cursor: pointer;
}
input + label > .checked {
    display: none;
    transition: all .3s;
}
input:checked + label > .checked {
    display: block;
    transition: all .3s;
}
input:checked + label > .unchecked {
    display: none;
    transition: all .3s;
}

svg {
    vertical-align: middle;
}

checkboxをdisplay: none;にして、labelでチェックのオンオフができるようにします。

labelの中にオンオフそれぞれの状態のSVGを格納し、class名をつけて切り替えられるようにします。

また、imgやsvgはvertical-align: middle;を指定しないと、下に余分や余白ができるため、リセット要素として書きます。

unchecked状態のSVGは元の状態との差が生じないように、handのpathを利用し、簡易的にSVG内にborderだけにするstyleを書きました。

これで実際にうごくのがこちら

5. まとめ

CSSアニメーションは大体のことをやってくれますが、数値通りに行動されるのでやはり人間の視覚的に違和感が出る部分が多くあります。

それをうまくチューニングするとなめらかなアニメーションが実現できるので、試行錯誤しながら1%単位で動かしていけるといいかなと思いました。

ただ、美人は3日で飽きるのと同じで、派手なアニメーションも3日で飽きるので、めちゃくちゃ完成度の高い、コストの高いアニメーションを導入するよりも、少しラフでインタラクションを十分に感じられるアニメーションを導入するのが良さそうな気がしました。

Remaining : 0 characters / 0 images
100

Sign up / Continue after login

Related stories

Writer

Share

Popular stories

ローソンPBのパッケージデザインをくわしくみる

18154

iOSアプリリリースで、誰も教えてくれなかった10のこと

5934

M1 Macでnpmを使えるようにする

4916