Ionicアニメーションで遊ぼう

<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## はじめに

こんにちは！

最近はかっこいいアニメーションをするUIが多いですね。
UI/UXを考える上で、アニメーションは必須技術となっています。
そこで、今回はIonicアニメーションで遊んでみたいと思います。

元ネタは次のページです。詳しく学びたい方は次のリンクからどうぞ！

- https://ionicframework.com/docs/utilities/gestures
- https://ionicframework.com/docs/utilities/animations

---
##自己紹介

- Twitter: s_kozake
- 特技: 🍺が好きです

---

##ジェスチャー

Ionicアニメーションで遊ぶ前に、ジェスチャーについて少し触れます。
アニメーションの動作させるには、ユーザーからの何らかの操作が起点となることが多いですので。
特にスマフォでは、スワイプやダブルタップ、ロングタップなど様々な操作があります。

このような複雑な操作をサポートするために、Ionicにはジェスチャーユーティリティが用意されています。
便利な世の中ですね！

---
## ジェスチャーユーティリティのインストール

CDNからインストールすれば簡単です。
AngularとかReactなどのフレームワークがなくても使えるのは便利ですね！
もちろん、AngularやReact、Vueから使える手段も用意されています（むしろこちらが主流）。

```javascript
import { createGesture } from 'https://cdn.jsdelivr.net/npm/@ionic/core@latest/dist/esm/index.mjs';
```

---
## ジェスチャーの基本

`createGesture` 関数を使ってジェスチャーを作成します。
`createGesture` 関数の代表的なオプションは次のとおりです。

```javascript
const gesture = createGesture({
  el: elementRef,              // ジェスチャーを受け取る対象要素
  disableScroll: false,        // ジェスチャーの間スクロールを無効にするかどうか
  direction: 'x',              // ジェスチャー検出を特定する軸
  gesturePriority: 0,          // ジェスチャーの優先度。高いものが優先される
  threshold: 15,               // ジェスチャー開始を判断する開始点からのポインタ移動量
  gestureName: 'my-gesture',   // ジェスチャーの名前
  onMove: ev => onMove(ev)     // ジェスチャー動作時に呼ばれるコールバック関数
});
```
---
## ジェスチャーの基本

`onMove` に設定したコールバック関数に渡されるイベントでジェスチャー情報を取得できます。

```javascript
const onMove = (detail) => {
  const type = detail.type;            // 検出されたジェスチャーのタイプ
  const startX = detail.startX;        // ジェスチャーを開始したx座標
  const currentX = detail.currentX;    // ジェスチャーの現在のx座標
  const deltaX = detail.deltaX;        // ジェスチャー開始点からのx軸上の移動距離
  const velocityX = detail.velocityX;  // ジェスチャーのx軸上の移動速度
}

```

---
## ジェスチャーのデモ

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="kozake" data-slug-hash="WNpbYGw" 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="Ionic Gestures - Basic">
  <span>See the Pen <a href="https://codepen.io/kozake/pen/WNpbYGw">
  Ionic Gestures - Basic</a> by Sinichi Kozake (<a href="https://codepen.io/kozake">@kozake</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

---

## アニメーション

では、Ionicアニメーションで遊んでいきましょう！
Ionicにはアニメーションを簡単に扱えるユーティリティが用意されています。
これを用いることで直感的にアニメーションを作成することができます。

---
## アニメーションユーティリティのインストール


CDNからインストールすれば簡単です。
ジェスチャー同様、AngularやReact、Vueから使える手段も用意されています。


```javascript
import { createAnimation } from 'https://cdn.jsdelivr.net/npm/@ionic/core@latest/dist/esm/index.mjs';
```

---
## アニメーション（基本１）

`createAnimation` 関数を使ってアニメーションを作成します。

```javascript
createAnimation()
  .addElement(document.querySelector('.square'))  // アニメーション対象要素
  .duration(1500)                                 // アニメーション間隔
  .iterations(1)                                  // アニメーション回数
  .fromTo('opacity', '1', '0.2');                 // 開始と終了
```

上記コードでは、1.5秒の間隔で透過が変化するアニメーションを作成しています。

---
## アニメーション（基本１）のデモ

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="kozake" data-slug-hash="OJpPrpg" 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="Ionic Animations - Basic">
  <span>See the Pen <a href="https://codepen.io/kozake/pen/OJpPrpg">
  Ionic Animations - Basic</a> by Sinichi Kozake (<a href="https://codepen.io/kozake">@kozake</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

---
## アニメーション（基本２）

もっと複雑なアニメーションも簡単に作成できます！

```javascript
const animation = createAnimation()
   .addElement(document.querySelector('.square'))
   .duration(1500)
   .iterations(Infinity)
   .direction('alternate')
   .fromTo('transform',
           'translateX(0px) scale(0.5) rotate(0)',
           'translateX(100px) scale(1) rotate(45deg)')
  .fromTo('opacity', '1', '0.2');
```

上記コードでは、1.5秒の間隔で透過や大きさ、傾きやＸ軸が変化するアニメーションを作成しています。
`iterations(Infinity)`と指定することで、アニメーションは停止するまで何度も繰り返されます。

---
## アニメーション（基本２）のデモ

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="kozake" data-slug-hash="QWpwJeq" 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="Ionic Animations - Basic2">
  <span>See the Pen <a href="https://codepen.io/kozake/pen/QWpwJeq">
  Ionic Animations - Basic2</a> by Sinichi Kozake (<a href="https://codepen.io/kozake">@kozake</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

---
## アニメーション（キーフレーム）

キーフレームを使うと、さらに詳細にアニメーション動作を指定することができます！

```javascript
const animation = createAnimation()
   .addElement(document.querySelector('.square'))
   .duration(1000)
   .keyframes([
     { offset: 0, transform: 'translateX(0px) rotate(0)' },
     { offset: 0.3, transform: 'translateX(150px) rotate(15deg)' },
     { offset: 1, transform: 'translateX(100px) rotate(0)' }
   ]);
```
上記コードでは、開始0.3秒後に右に150px移動し、45度回転します。
その後、0.7秒かけて左に50px移動して傾きも元に戻ります。

---
## アニメーション（キーフレーム）のデモ

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="kozake" data-slug-hash="rNyaomR" 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="Ionic Animations - Keyframes">
  <span>See the Pen <a href="https://codepen.io/kozake/pen/rNyaomR">
  Ionic Animations - Keyframes</a> by Sinichi Kozake (<a href="https://codepen.io/kozake">@kozake</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

---
## アニメーション（グループ）

アニメーションをグループ指定することも出来ます！

```javascript
const squareA = createAnimation()
  .addElement(document.querySelector('.square-a'))
  .keyframes([...]);

const squareB = createAnimation()
  .addElement(document.querySelector('.square-b'))
  .keyframes([...]);

const squareC = createAnimation()
  .addElement(document.querySelector('.square-c'))
  .duration(5000)
  .keyframes([...]);

const parent = createAnimation()
  .duration(2000)
  .iterations(Infinity)
  .addAnimation([squareA, squareB, squareC]);
```

上記コードでは、それぞれのアニメーションをまとめて動作させます。
`squareA`と`squareB`ではアニメーション間隔を指定していないので、`parent`に指定された2秒が適用されますが、`squareC`では指定した5秒間隔でアニメーションが実行されます。

---
## アニメーション（グループ）のデモ

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="kozake" data-slug-hash="gOmbZGR" 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="Ionic Animations - Group">
  <span>See the Pen <a href="https://codepen.io/kozake/pen/gOmbZGR">
  Ionic Animations - Group</a> by Sinichi Kozake (<a href="https://codepen.io/kozake">@kozake</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

---
## アニメーション（チェーン）

アニメーションを連鎖（チェーン）することもできます！

```javascript
const squareA = createAnimation()
  .addElement(document.querySelector('.square-a'))
  .fill('none').duration(1000).keyframes([...]);

const squareB = createAnimation()
  .addElement(document.querySelector('.square-b'))
  .fill('none').duration(1000).keyframes([...]);

const squareC = createAnimation()
  .addElement(document.querySelector('.square-c'))
  .fill('none').duration(1000).keyframes([...]);

await squareA.play();
await squareB.play();
await squareC.play();
```

`await` でアニメーションの完了を待つだけです。簡単ですね！

---
## アニメーション（チェーン）のデモ

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="kozake" data-slug-hash="VwpYRvV" 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="Ionic Animations - Chaining">
  <span>See the Pen <a href="https://codepen.io/kozake/pen/VwpYRvV">
  Ionic Animations - Chaining</a> by Sinichi Kozake (<a href="https://codepen.io/kozake">@kozake</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

---
## アニメーション（ジェスチャー）のデモ

ジェスチャーと組み合わせた例が次のとおりです。
アニメーション動作をジェスチャーなどの情報と組み合わせて動作させることが出来ます。
下記のコードでは、四角をドラックして動かすことが出来ます。また、スワイプして動作させることも出来ます。

```javascript
let initialStep = 0;
let started = false;

const square = document.querySelector('.square');
const MAX_TRANSLATE = 400;

const animation = createAnimation()
  .addElement(square)
  .duration(1000)
  .fromTo('transform', 'translateX(0)', `translateX(${MAX_TRANSLATE}px)`);

const gesture = createGesture({
  el: square,
  threshold: 0,
  gestureName: 'square-drag',
  onMove: ev: onMove(ev),
  onEnd: ev: onEnd(ev)
})

gesture.enable(true);

const onMove = (ev): {
  if (!started) {
    animation.progressStart();
    started = true;
  }

  animation.progressStep(getStep(ev));
}

const onEnd = (ev): {
  if (!started) { return; }

  gesture.enable(false);

  const step = getStep(ev);
  const shouldComplete = (Math.abs(ev.velocityX) > 0.2) ?
    initialStep == 0 : step > 0.5;

  animation
    .progressEnd((shouldComplete) ? 1 : 0, step)
    .onFinish((): { gesture.enable(true); });

  initialStep = (shouldComplete) ? MAX_TRANSLATE : 0;
  started = false;
}

const clamp = (min, n, max): {
  return Math.max(min, Math.min(n, max));
};

const getStep = (ev): {
  const delta = initialStep + ev.deltaX;
  return clamp(0, delta / MAX_TRANSLATE, 1);
}
```

---
## アニメーション（ジェスチャー）のデモ

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="kozake" data-slug-hash="dyvPrWa" 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="Ionic Animations - Gesture">
  <span>See the Pen <a href="https://codepen.io/kozake/pen/dyvPrWa">
  Ionic Animations - Gesture</a> by Sinichi Kozake (<a href="https://codepen.io/kozake">@kozake</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

---
## アニメーション（スクロール）

最後によくあるスクロールと連動してアニメーションするやつです。
画面下から出てくる四角がスクロールと連動して大きくなります。

```javascript
const animation = createAnimation()
   .addElement(document.querySelector('.square'))
   .duration(1) 
   .fromTo('opacity', '0', '1')
   .fromTo('transform', 'scale(0)', 'scale(1)');

const square = document.querySelector('.square');

let started = false;

window.onscroll = () => {
  const documentHeight = document.documentElement.clientHeight;
  const scrollTop = document.documentElement.scrollTop;
  const targetPosition = square.offsetTop;

  const step = Math.min((scrollTop + documentHeight - targetPosition) / 200, 1);

  if(step > 0 && step < 1){
    if (!started) {
      animation.progressStart();
      started = true;
    }
    animation.progressStep(step);
  } else {
    if (started) {
      animation.progressEnd(step == 1 ? 1 : 0, step)
      started = false;
    }
  }
}
```

---
## アニメーション（スクロール）のデモ

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="kozake" data-slug-hash="NWpPOwV" 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="Ionic Animations - Scroll ">
  <span>See the Pen <a href="https://codepen.io/kozake/pen/NWpPOwV">
  Ionic Animations - Scroll </a> by Sinichi Kozake (<a href="https://codepen.io/kozake">@kozake</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

---
## おわり

Ionicアニメーション楽しいですね！
色々と試して遊んでみてください🍻(´∀｀*v)