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

# どんなものが出来るのか

このgifを参考にしてください。　これは一番単純な動きです。
![2018-04-05_02-14-26.gif](https://qiita-image-store.s3.amazonaws.com/0/107334/734465ee-cf88-1236-5b23-9edee5e24e1c.gif)

最終的にこうなります。
![2018-04-05_07-02-13.gif](https://qiita-image-store.s3.amazonaws.com/0/107334/1d72b383-02cc-e3ab-b82e-224eabe84f02.gif)

#作り方

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

# 環境
CSSリセットとして[ress](https://github.com/filipelinhares/ress)、アニメーションは[anime.js](http://animejs.com/)で記述していきます。

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

## コーディング

### HTML

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

### CSS
```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

```javascript
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`要素の`visibility`を`hidden`にして非表示に設定する。

### ボーダー要素を拡大する (scaleX: [0, 1])
`scale`を0から1へアニメーションする。

### ボーダーの拡大が完了 (2回目のbegin関数)
`img`要素の`visibility`を`visible`にして非表示を解除する。

### ボーダーを0pxに縮小する (borderWidth: 0)
タイトル通りです。

基本の動きは以上です。

## コツ
[前回の記事](https://qiita.com/SnO2WMaN/items/467c56523a03b5ca4848)でも触れましたが、拡大するときにはイージングを`easeIn*`系、縮小する際には`easeOut*`を設定すると見栄えが良くなると思います。

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

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

![2018-04-05_06-08-59.gif](https://qiita-image-store.s3.amazonaws.com/0/107334/0dc59e97-0421-028e-d47c-6ccb2709d835.gif)


# 応用 

## その1

![2018-04-05_06-15-45.gif](https://qiita-image-store.s3.amazonaws.com/0/107334/caf397f5-05e4-a23d-4be6-8f8e594eb731.gif)

HTMLは一緒です。

### CSS
```css
.wrap {
    border-radius: 50%;
    overflow: hidden;
}
```
コツで書いたようにボーダー要素の`scale`を変更する手段を取った場合、この2行を追加してください。

### JS

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

```javascript
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`というのは見慣れない単位かもしれませんが、`1turn`は`360deg`です。別に`90deg`でも構いません。

また、ボーダー要素の`scale`を`1.05`になるように設定しています。

## その2
![2018-04-05_06-29-33.gif](https://qiita-image-store.s3.amazonaws.com/0/107334/80eca278-6353-eb61-6453-c9c43932c713.gif)
[前回の応用1](https://qiita.com/SnO2WMaN/items/467c56523a03b5ca4848#%E5%BF%9C%E7%94%A8-%E3%81%9D%E3%81%AE1)でやったことの応用で、ボーダー要素を2個にします。

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

### HTML

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

### CSS

```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
```javascript
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](https://qiita-image-store.s3.amazonaws.com/0/107334/518307b3-4907-7c87-d1b9-8bc1f3711477.gif)

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

### HTML

```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

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

    z-index: 0;

    width: 128px;
    height: 128px;

    opacity: 0;
    border-radius: 50%;
    background: #03a9f4;
}
```
コードには含めていませんが、`.wrap`の`overflow: hidden`を削除してください。
そして`opacity: 0`を忘れないでください。書き忘れるとこうなります。
![2018-04-05_07-14-44.gif](https://qiita-image-store.s3.amazonaws.com/0/107334/346e7767-1feb-16ac-9e94-6783c0d0625e.gif)


### JS
```javascript
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
			});
		}
	});
```
パーティクルのアニメーションを追加しました。
あまり`translateX`や`translateY`の値を大きくすると良くないかもしれません。
このサンプルでは`-100%`から`100%`の範囲で動きます。

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


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

[ブラウザ上で動かす](https://sno2wman.github.io/webmotion_lecture_2/)
[ソースコード](https://github.com/SnO2WMaN/webmotion_lecture_2)