frameCount・剰余演算を使った p5.js でのループアニメーション：アニメーションの最初と最後に空白の時間を作る

## 今回の内容
p5.js でループするアニメーションを作る際に、frameCount と剰余演算を組み合わせたものをよく使うのですが、それに関連した内容です。

直近で、以下のようなアニメーション（イージングを適用したループアニメーション）を作ろうとした中で、試してみていた内容です。

<iframe width="560" height="315" src="https://www.youtube.com/embed/YFlnSDQg4-k?si=kcOQJwNemUbZkH_A" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

<iframe width="560" height="315" src="https://www.youtube.com/embed/EBcJ1HpIUeE?si=K7XSn9Y5dfrLLVH1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

## プログラムの内容など
プログラム例を紹介してから、少し補足を書いてみます。

### 実装１
まずは、1つ目のプログラム全体を掲載します。

```javascript
function setup() {
  createCanvas(550, 400);
  noStroke();
  fill(100, 120, 210);
}

function draw() {
  background(220);

  let f = frameCount % 300;

  f = (() => {
    if (f < 45) {
      return 0;
    }
    if (f > 200) return 1;
    return map(f, 45, 200, 0, 1);
  })();

  // console.log(f);
  circle(lerp(0, width, easing(f)), height / 2, 80);
}

function easing(t) {
  // https://easings.net/ja#easeInExpo
  return t === 0 ? 0 : pow(2, 10 * t - 10);
}
```

ループする時間間隔は `frameCount % 300` の部分の、300フレーム分毎の時間になります。

そして、その 300フレームの中では、45フレーム目まではゼロ、200フレームより大きいところは 1、その間では 0 から 1 までを線形増加する値を作っています。この値をもとに、lerp() と easing() を使ったイージングの実装を行っています。

上記の 0 にした部分と 1 にした部分は、イージングが適用された動きの開始時と終了時の、それぞれの位置に静止した状態になります。ここで、動きが止まる空白の時間を作っています。

#### イージングの関数について
イージングの関数の部分は、有名どころの以下を見て実装しました。

●イージング関数チートシート
　https://easings.net/ja
 
### 実装2
上に書いていたものとは、別の実装を試したものが以下になります。

```javascript
function setup() {
  createCanvas(550, 400);
  noStroke();
  fill(100, 120, 210);
}

function draw() {
  background(220);

  let f = frameCount % 200;

  f = f < 120 ? 0 : map(f, 120, 200, 0, 2);
  f = constrain(f, 0, 1);
  // console.log(f);

  circle(lerp(0, width, easing(f)), height / 2, 80);
}

function easing(t) {
  // https://easings.net/ja#easeInExpo
  return t === 0 ? 0 : pow(2, 10 * t - 10);
}
```

上記では、200 フレームのループが行われる中で、120フレームより小さい部分は 0 の値にするようにしました。そして、それより大きい値のところは、0 から 2　まで線形増加するようにしつつも、[constrain()](https://p5js.org/reference/#/p5/constrain) を使って上限の値を 1 に制限してみました。

これにより、上記の線形増加している部分で、1 から 2 の間の部分が全て 1 の値になります。
この事例でも 1つ目の実装と同じく、動きのある区間の前後それぞれおで、動きのない空白の時間を作っています。

そして、2つ目の実装例でも、1つ目の実装例と同じようにイージングを適用しています。

## おわりに
自分が p5.js のアニメーション実装でよく使う frameCount と剰余演算を使ったループアニメーションの処理に関する話を書いてみました。

実装方法は他にもいろいろあると思いますので、他の実装も試していければと思っています。

## 実装1 の即時実行関数式の部分について
1つ目の実装例について、即時実行関数式を使った部分があります。
ここでの書き方は、最近見かけた以下の記事を参考にしました。

ちなみに以下の記事の概要は、多段の入れ子の三項演算子・条件文を避ける、という方向の内容でした。

●Stop nesting ternaries in JavaScript | Sonar
　https://www.sonarsource.com/blog/stop-nesting-ternaries-javascript/
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3bcb96b3-5658-68df-28ca-05115e0752fd.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/8f21d5e9-924e-e497-e386-cadcc5170bc6.png)

```javascript
// 関数
function animalName(pet) {
  if (pet.canBark() && pet.isScary()) { return "wolf"; }
  if (pet.canBark()) return "dog";
  if (pet.canMeow()) return "cat";
  return "probably a bunny";
}

// 即時実行関数式
const animalName = (() => {
  if (pet.canBark() && pet.isScary()) { return "wolf"; }
  if (pet.canBark()) return "dog";
  if (pet.canMeow()) return "cat";
  return "probably a bunny";
})();
```