Svelteのコンポーネントを理解したくて『シュバババッ！』コンポーネントを作ってみた

こんにちは。ちょっと自分でもどこを目指しているのかわからない [^1] [あずきしろもち（@azukisiromochi）]() です。

[^1]: 色の勉強をしてみたり、コード書いたり、絵を描いたり、小説を書いたり……。絶賛迷走中。



今回は `Svelte` のコンポーネントの話。



## 前フリ

>  ※ループさせていないので、右下の [Rerun] ボタンを押して試してみてください

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="result" data-user="azukisiromochi" data-slug-hash="dBwwOj" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Syubababa!!!">
  <span>See the Pen <a href="https://codepen.io/azukisiromochi/pen/dBwwOj/">
  Syubababa!!!</a> by あずきしろもち (<a href="https://codepen.io/azukisiromochi">@azukisiromochi</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
CSSアニメーションを勉強しようと思ってこういうやつ作ったんです。

『 **シュバババッ！** 』って感じのやつ。
（あとで気づいたけど、右スクロールするとオブジェクト見えてるなｗ）


いま、ポートフォリオサイトを作成していて、モーダルをオープンするときのアニメーションに、この『 **シュバババッ！** 』を使ってみたいなーと思ったわけです。



## どんなコンポーネントにするのか

### そもそもコンポーネントって？

`Svelte` の公式リファレンスにはコンポーネントの定義が記載されていません（たぶん）。
`Svelte` は `Vue.js` に非常に近い構文ということもあって、そちらの定義を確認してみましょう。

> 小さく、自己完結的で、（多くの場合）再利用可能なインスタンスをコンポーネントという

キーワードは

- 小さい
- 自己完結的
- 再利用可能

みたいですね。



### 『シュバババッ！』はどんなコンポーネントになるのか？

先程の `CodePen` のコードでは

```html
<div class="syubababa a"></div>
<div class="syubababa b"></div>
<div class="syubababa c"></div>
<div class="syubababa d"></div>
<div class="syubababa e"></div>
```

こんな感じに `<div>` 要素が並んでいました。

つまり、5つの『 **シュバッ** 』が順々にアニメーションしているんです。
コンポーネントのキーワードにあった **再利用可能** に、非常によく当てはまると思いませんか？

ということで、この1つの『 **シュバッ** 』を `Svelte` のコンポーネントとして定義して進めてみましょう！



### コンポーネントの仕様を明確にしよう！

コンポーネントは、1つの『 **シュバッ** 』の単位にすることは決めました。
ですが、ただこの『 **シュバッ** 』を並べるだけでは面白くありません。

『 **シュバッ** 』の **高さ** を変えたり、 **速度** を変えたり、 **動き出すタイミング** を変えたり……
このあたり（仕様）を明確にしていきます。

```html
<div class="syubababa a"></div>
<div class="syubababa b"></div>
<div class="syubababa c"></div>
<div class="syubababa d"></div>
<div class="syubababa e"></div>
```

もう一度、このコードに戻ります。
それぞれの `<div>` に共通の `.syubababa` クラスと、個々の `.a` `.b` `.c` `.d` `.e` クラスがあります。

コンポーネント化に置き換えると、 `.syubababa` クラスがコンポーネントの共通仕様で、 `.a` `.b` `.c` `.d` `.e` クラスが動的に変動させることのできる要素になるでしょう。


例えば `.a` の場合、

```css
.a {
  animation-duration: 800ms;
  animation-delay: 700ms;
}
```

と設定されていますし、 `.d` の場合、

```css
.d {
  animation-delay: 1400ms;
  height: 10vh;
}
```

と設定されています。

コンポーネントの変動的な要素としては、次のようになりそうです。

| animation-duration | 仕様                                    |
| ------------------ | --------------------------------------- |
| animation-duration | アニメーション一回分の時間の長さ ≒ 速度 |
| animation-delay    | アニメーションの開始タイミング          |
| height             | 『 **シュバッ** 』の高さ                |

コンポーネントの単位では、

```javascript
// Set style `animation-duration` (default: 800ms)
let duration = '';
// Set style `animation-delay` (default: 0)
let delay = '';
// Set style `height` (default: 15vh)
let height = '';
```

この3種類のプロパティーを扱って『 **シュバッ** 』を変動させていこうと思います。



## コンポーネントを実装していく

ここまでで『 **シュバババッ！** 』は、『 **シュバッ** 』コンポーネントをいくつか並べて実現することを決めました。
また、『 **シュバッ** 』コンポーネントは、 `duration` `delay` `height` をプロパティに持ち、それぞれの『 **シュバッ** 』に個性を持たせることも決めました。

それでは順を追って `Svelte` のコンポーネントを実装していきましょう！



### コンポーネントのファイルを作成する

```vue:components/Syubababa.svelte
<script>
  // JavaScript 書くところ
</script>

<style>
  /* CSS 書くところ */
</style>

<!-- html 書くところ -->
<div/>
```

`.svelte` ファイルの構造はこのようになっています。
`Vue.js` と非常によく似ていますが、 `<template>` タグはなく直接 html タグをコーディングする形になります。


コンポーネントの仕様により、実装する順序はまちまちですが、今回は『 **シュバババッ！** 』を作成した順序を一例として紹介したいと思います。

### スタイルを適用させる

```vue:components/Syubababa.svelte
<script>
  // JavaScript 書くところ
</script>

<style lang="scss">
$color1: #e1315b;
$color2: #f47d4a;
$color3: #ffff42;
$color4: #00cffa;

div {
  background: $color1;
  height: 15vh;
  width: 10vw;
  margin-left: -15vw;
	animation-name: fadeIn;
  animation-duration: 800ms;
  animation-timing-function: ease;
  // animation-iteration-count: 1;
  animation-fill-mode:forwards;
}

@keyframes fadeIn {
  15% { background: $color1; }
	30% { background: $color2; }
	45% { background: $color3; }
  50% {
    transform: scaleX(15) skew(-30deg);
    transform-origin: 0% 50%;
  }
  75% { border-radius: 30% 20% / 40% 15%; }
	80% { background: $color4; }
  90% { box-shadow: -20px 10px 10px 10px rgba(0, 0, 0, .8); }
  100% {
    transform: translatex(120vw) scaleX(2) skew(0deg);
    transform-origin: 0% 50%;
    margin-left: 0;
  }
}
</style>

<!-- html 書くところ -->
<div/>
```

`CodePen` の実装を参考に CSS を設定しました。

SCSS を使ってしまっていますが、色を調整するための変数としてしか利用していません。
（ `Svelte` や `Sapper` で SCSS を使いたい人がいましたら [こちら](https://qiita.com/azukisiromochi/items/7313c958b46048c7bcdc) をどうぞ）

この状態でも『 **シュバッ** 』はコンポーネントとして動作します。

```vue:index.svelte
<script>
import Syubababa from '../components/Syubababa.svelte';
</script>

<style lang="scss">
</style>

<Syubababa/>

```

`index.svelte` の `<script>` タグでコンポーネントを `import` し、タグとして `<Syubababa/>` をコーディングすると……
![syuba.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317946/146d2afc-fd8a-7d43-8475-c8e84cca8d2e.gif)
と、個性のない（変動的な要素のない）『 **シュバッ** 』コンポーネントが出来上がりました！

続いて、個性を出していきましょう！



### コンポーネントにプロパティを持たせる

```javascript:components/Syubababa.svelte
<script>
  // Set style `animation-duration` (default: 800ms)
	export let duration = '';
  // Set style `animation-delay` (default: 0)
  export let delay = '';
  // Set style `height` (default: 15vh)
  export let height = '';
</script>
```

`<script>` タグだけ切り出しましたが、仕様のところで決めた3つの変動要素（変数）を `export` させています。
これで親コンポーネントから『 **シュバッ** 』コンポーネントにプロパティを設定し、個性を出すことができるようになります。


変数を `export` させたのはいいですが、実際の html に変動要素のスタイルを適用する必要があります。
今度は html 部分を見ていきましょう。

```html:components/Syubababa.svelte
<div style="animation-duration: {duration}; animation-delay: {delay}; height: {height}"/>
```

`<div>` タグの `style` 要素に `animation-duration` `animation-delay` `height` を設定し、それぞれに対して、 `<script>` タグで宣言した変数を設定しています。
`Vue.js` の場合は `{{ hoge }}` でバインドしますが、 `Svelte` の場合は `{ hoge }` でバインドします。


それでは、親コンポーネント（ `index.svelte` ）側からプロパティを設定してみましょう。

```vue:index.svelte
<script>
import Syubababa from '../components/Syubababa.svelte';
</script>

<style lang="scss">
</style>

<Syubababa duration={"800ms"} delay={"300ms"} height={"50vh"} />
```

 `<Syubababa/>` コンポーネントの3つのプロパティを設定すると……
![syuba2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317946/8f8a0dad-f900-3556-66d1-154026389ad6.gif)
個性が出ましたね！



### シュバババッ！

ここまでくれば、あとはコンポーネントを並べて『 **シュバババッ！** 』しましょう！

```vue:index.svelte
<script>
import Syubababa from '../components/Syubababa.svelte';
</script>

<style lang="scss">
</style>

<Syubababa duration={"800ms"} delay={"700ms"} />
<Syubababa duration={"700ms"} delay={"1000ms"} height={"30vh"} />
<Syubababa duration={"600ms"} delay={"1200ms"} height={"20vh"} />
<Syubababa delay={"1400ms"} height={"10vh"} />
<Syubababa delay={"1500ms"} height={"25vh"} />
```

![syubababa!!!.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317946/e85d4f1d-5738-07c0-5536-16f50950d475.gif)


## まとめ

`Svelte` のコンポーネントは `Vue.js` のものと非常に似ていて、 `Vue.js` を触ったことのある人ならすんなり使えるのではないでしょうか。
今回は紹介していませんが、 `Svelte` にも `<slot>` が用意されており、より自由度の高いコンポーネントが作れると思います。

両方とも使っている身としては、 `Svelte` の早さは魅力的で **もっと流行ってほしいな―** と思います。

みなさんも興味を持ったなら、ぜひ使ってみてくださいね :yum:

<br>

それではお別れは作成途中のポートフォリオサイトのモーダルアニメーションで～ :wave:

![modal.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317946/c778b371-f5f5-c8ca-188a-09fb930a8fa0.gif)