LoginSignup
14
10

More than 5 years have passed since last update.

border-widthで実装する動きのあるアイコンのアニメーション

Last updated at Posted at 2018-04-04

どんなものが出来るのか

このgifを参考にしてください。 これは一番単純な動きです。
2018-04-05_02-14-26.gif

最終的にこうなります。
2018-04-05_07-02-13.gif

作り方

仕組みは単純で、border-widthの値を変更することで実装します。

環境

CSSリセットとしてress、アニメーションはanime.jsで記述していきます。

お使いの環境に合わせて適宜変更していってください。

コーディング

HTML

<div class="wrap">
    <div class="border"></div>
    <img src="icon.png">
</div>

CSS


.wrap {
    position: relative;

    width: 128px;
    height: 128px;
}

.wrap > .border {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 1;

    width: 128px;
    height: 128px;

    border-width: 64px;
    border-color: #03a9f4;
    border-style: solid;
    border-radius: 50%;
    box-sizing: border-box;
}

.wrap > img {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 0;

    width: 128px;
    height: 128px;

    border-radius: 50%;
}

今回は大きさを128pxで設定しています。scss、stylusなどの場合は変数化しておくとラクだと思います。
重要なのはborder-weightを大きさの半分に設定することです。
border: 64px solid #03a9f4と書いても問題ありません。

JS

var wrap = basic.getElementsByClassName("wrap")[0];
var border = wrap.getElementsByClassName("border")[0];
var img = wrap.getElementsByTagName("img")[0];

var timeline = anime.timeline();

timeline
    .add({
        targets: border,
        duration: 500,
        easing: "easeInExpo",
        scale: [0, 1],
        begin: () => (img.style.visibility = "hidden")
    })
    .add({
        targets: border,
        duration: 500,
        easing: "easeOutExpo",
        borderWidth: 0,
        begin: () => (img.style.visibility = "visible")
    });

何をやっているか

アニメーション開始時点 (1回目のbegin関数)

img要素のvisibilityhiddenにして非表示に設定する。

ボーダー要素を拡大する (scaleX: [0, 1])

scaleを0から1へアニメーションする。

ボーダーの拡大が完了 (2回目のbegin関数)

img要素のvisibilityvisibleにして非表示を解除する。

ボーダーを0pxに縮小する (borderWidth: 0)

タイトル通りです。

基本の動きは以上です。

コツ

前回の記事でも触れましたが、拡大するときにはイージングをeaseIn*系、縮小する際にはeaseOut*を設定すると見栄えが良くなると思います。

また、easeOutExpoのような急なイージングの際には縮小の際にボーダーが1px近くの小さな値で留まりやすいです。(下の画像のように)

ボーダー要素のopacityもアニメーションさせておくと違和感が残らなくて良いかもしれません。
または、ボーダー要素のscale1.05のように少しだけ大きくするのも一つの手でしょう。

2018-04-05_06-08-59.gif

応用

その1

2018-04-05_06-15-45.gif

HTMLは一緒です。

CSS

.wrap {
    border-radius: 50%;
    overflow: hidden;
}

コツで書いたようにボーダー要素のscaleを変更する手段を取った場合、この2行を追加してください。

JS

コードはほぼ一緒なので、timelineの部分だけ抜き出します。

timeline
    .add({
        targets: border,
        duration: 500,
        easing: "easeInExpo",
        scale: [0, 1],
        begin: () => (img.style.visibility = "hidden")
    })
    .add({
        targets: border,
        duration: 500,
        easing: "easeOutExpo",
        borderWidth: 0,
        scale: 1.05,
        begin: () => {
            img.style.visibility = "visible";
            anime({
                targets: img,
                duration: 1000,
                easing: "easeOutElastic",
                scale: [0, 1],
                rotateZ: [`${0.25}turn`, 0]
            });
        }
    });

画像側のアニメーションを2回目のbegin関数に追加しました。
turnというのは見慣れない単位かもしれませんが、1turn360degです。別に90degでも構いません。

また、ボーダー要素のscale1.05になるように設定しています。

その2

2018-04-05_06-29-33.gif
前回の応用1でやったことの応用で、ボーダー要素を2個にします。

先程のコードを改造します。

HTML

<div class="wrap">
    <div class="border"></div>
    <div class="border"></div>
    <img src="icon.png">
</div>

CSS

.wrap > .border:nth-type-of(1) {
    background-color: #03a9f4;
    z-index: 2;
}

.wrap > .border:nth-type-of(2) {
    background-color: #0277bd;
    z-index: 1;
}

JS

var wrap = adv_1.getElementsByClassName("wrap")[0];
var border1 = wrap.getElementsByClassName("border")[0];
var border2 = wrap.getElementsByClassName("border")[1];
var img = wrap.getElementsByTagName("img")[0];

var timeline = anime.timeline();

timeline
    .add({
        targets: [border1, border2],
        duration: (target, index) => 500 - index * 100,
        easing: "easeInExpo",
        scale: [0, 1],
        begin: () => (img.style.visibility = "hidden")
    })
    .add({
        targets: [border1, border2],
        duration: 500,
        delay: (target, index) => index * 100,
        easing: "easeOutExpo",
        borderWidth: 0,
        scale: 1.05,
        begin: () => {
            img.style.visibility = "visible";
            anime({
                targets: img,
                duration: 1000,
                easing: "easeOutElastic",
                scale: [0, 1],
                rotateZ: [`${0.125}turn`, 0]
            });
        }
    });

その3

2018-04-05_07-02-13.gif

その2を更に応用して、パーティクルを出します。

HTML

<div class="wrap">
    <div class="border"></div>
    <div class="border"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <div class="particle"></div>
    <img src="icon.png">
</div>

pugなどが使える環境ではループを使って書いたほうがよっぽど衛生的だと思います。

CSS

.wrap > .particle {
    position: absolute;
    top: 0;
    left: 0;

    z-index: 0;

    width: 128px;
    height: 128px;

    opacity: 0;
    border-radius: 50%;
    background: #03a9f4;
}

コードには含めていませんが、.wrapoverflow: hiddenを削除してください。
そしてopacity: 0を忘れないでください。書き忘れるとこうなります。
2018-04-05_07-14-44.gif

JS

var particles = wrap.getElementsByClassName("particle");

var timeline = anime.timeline();
timeline
    .add({
        targets: [border1, border2],
        duration: (target, index) => 500 - index * 100,
        easing: "easeInExpo",
        scale: [0, 1],
        begin: () => (img.style.visibility = "hidden")
    })
    .add({
        targets: [border1, border2],
        duration: 500,
        delay: (target, index) => index * 100,
        easing: "easeOutExpo",
        borderWidth: 0,
        begin: () => {
            img.style.visibility = "visible";
            anime({
                targets: img,
                duration: 1000,
                easing: "easeOutElastic",
                rotateZ: [`${0.125}turn`, 0]
            });
            anime({
                targets: particles,
                duration: 350,
                delay: (target, index) => index * 10,
                easing: "easeOutQuad",
                translateX: () => `${(Math.random() - 0.5) * 200}%`,
                translateY: () => `${(Math.random() - 0.5) * 200}%`,
                scale: [() => Math.min(Math.random() * 2, 1), 0],
                opacity: 1
            });
        }
    });

パーティクルのアニメーションを追加しました。
あまりtranslateXtranslateYの値を大きくすると良くないかもしれません。
このサンプルでは-100%から100%の範囲で動きます。

また、今回はボーダーの縮小にscale: 1.05を使うとハミ出るので削除します。

最後

間違ってたらコメント欄でご指摘ください。

ブラウザ上で動かす
ソースコード

14
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10