web制作アニメーション備忘録　パート②


## web 制作アニメーション備忘録 ②

[前回の続き](https://qiita.com/nagisa-afadfadf/items/b815e1aa864da219f334)です。

ぜひ参考にしてください。

### 完成イメージ

[本のアニメーションイメージはこちら](https://github.com/kawashima-nagisa/web-animation/issues/2#issue-2618479504)

ページをスワイプしたりクリックすることで、本のページがめくられるアニメーションです。ページを自由に追加したりもちろんめくったページを戻ることもできます。
<br>レスポンシブにも対応しているので、若干字は小さくなりますが、スマホ画面でも自由に使うことができます。

[該当コードはこちら](https://github.com/kawashima-nagisa/web-animation/blob/main/book.html)
[参考にしたコードはこちら](https://codepen.io/mahnunchik/full/KKzeGb)

⇧[codepen](https://codepen.io/)で本のページめくり animation を探したときに
本のページを追加したり、削除したりが比較的容易でしたのでこちらを参考にしました。

Hammer.js 公式サイトを調べてみると以下のような記載がありました。

> Hammer は、タッチ、マウス、ポインター イベントによるジェスチャーを認識できるオープン ソース ライブラリです。依存関係がなく、サイズも小さく、縮小 + gzip 圧縮してもわずか 7.34 kB です。

> お使いのブラウザや OS がリストにない場合でも、問題なく動作する可能性があります。Internet Explorer 8 以前ではサポートされていません。

確かに、[公式リファレンス](https://hammerjs.github.io/browser-support/)を見てみると今回使用した Swipe オプションは全てのブラウザで対応しそうです。

他にも下記のオプションがあるようです。
今後気になったら調べてみようと思います。

> タップ（tap）: シングルタップを検出（クリックに似た動作）。
> ダブルタップ（doubletap）: 2 回連続でタップする動作を検出。
> スワイプ（swipe）: 指をスライドさせる動作を検出。方向（左、右、上、下）も認識可能。
> パン（pan）: スワイプに似ていますが、短いスライドではなく、長く指をスライドさせた動作を検出。
> ピンチ（pinch）: 2 本の指を使って拡大・縮小する操作を検出。
> 回転（rotate）: 2 本の指で回転させる操作を検出。

### コードの解説

1. 必要なライブラリの読み込みと必要な画像の用意

ページの捲る部分の左側と右側の部分の切り抜きを全部で３枚準備する(自然にページを捲るため)
![Document.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3550857/bdada3bf-6d12-4ee3-0c41-e5ef47c33cc7.png)

一枚目：<br><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3550857/1145d91a-b3cc-1a7e-681a-8dc012a246d8.png" width="300">

二枚目：<br><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3550857/473dc8e8-1e02-0025-6b4d-a5e414021ef5.png" width="300">

三枚目:<br>[本全体の画像](https://github.com/kawashima-nagisa/web-animation/issues/3#issue-2621107540)

```html
<!-- Hammer.jsのCDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
```

Hammer.js はスワイプ操作の検出、jQuery は DOM 操作の簡略化に使用します。

2. 初期設定とイベントリスナーの追加

```javascript
  コードをコピーする
  document.addEventListener('DOMContentLoaded', function () {
  var bookElement = document.querySelector('.book');

// Hammer.js のインスタンスを作成
var hammer = new Hammer(bookElement);

hammer.on('swipeleft', nextPage);
hammer.on('swiperight', prevPage);
```

Hammer.js で bookElement をスワイプできるようにし、左スワイプ (swipeleft) で次のページ、右スワイプ (swiperight) で前のページに移動します。

```javascript
bookElement.addEventListener("click", function (event) {
	if (event.target.closest(".active")) {
		nextPage();
	} else if (event.target.closest(".flipped:not(.initial)")) {
		prevPage();
	}
});
```

クリックされた要素が .active クラスを持っていれば nextPage() を実行し、次のページへ移動。
.flipped クラスがついていて、かつ .initial でない場合は prevPage() を実行し、前のページに戻ります。

前のページに戻る関数 `prevPage()`

```javascript
function prevPage() {
	// フリップされたすべてのページを取得
	var lastFlipped = document.querySelectorAll(".flipped");

	// 最後にフリップされたページ（最後にめくられたページ）を取得
	var lastFlippedPage = lastFlipped[lastFlipped.length - 1];

	// 最後のページが初期ページでない場合に処理を続行
	if (!lastFlippedPage.classList.contains("initial")) {
		// 最後のフリップされたページから `flipped` クラスを削除し、`active` クラスを追加（アクティブ状態に戻す）
		lastFlippedPage.classList.remove("flipped");
		lastFlippedPage.classList.add("active");

		// 兄弟要素（現在のページ以外の要素）から `active` クラスを削除
		var siblings = Array.prototype.filter.call(
			lastFlippedPage.parentNode.children,
			function (child) {
				return child !== lastFlippedPage;
			}
		);
		siblings.forEach(function (sibling) {
			sibling.classList.remove("active");
		});
	}
}
```

最後にめくられたページをアクティブな状態に戻し、他のページから active クラスを削除します。
これにより、前のページが再度表示されるような動作になります。

5. 次のページに進む関数 `nextPage()`

```javascript
function nextPage() {
	// 現在のアクティブなページ（現在表示中のページ）を取得
	var activePage = document.querySelector(".active");

	// 次のページが存在しないか、次の要素がページでない場合は関数を終了
	if (
		!activePage.nextElementSibling ||
		!activePage.nextElementSibling.classList.contains("page")
	) {
		return;
	}

	// 現在のアクティブなページから `active` クラスを削除し、`flipped` クラスを追加（めくられた状態にする）
	activePage.classList.remove("active");
	activePage.classList.add("flipped");

	// 次のページをアクティブページとして設定
	var nextPage = activePage.nextElementSibling;
	nextPage.classList.add("active");

	// 兄弟要素（現在のページ以外の要素）から `active` クラスを削除
	var siblings = Array.prototype.filter.call(
		nextPage.parentNode.children,
		function (child) {
			return child !== nextPage;
		}
	);
	siblings.forEach(function (sibling) {
		sibling.classList.remove("active");
	});
}
```

現在のページを「フリップされた」状態にし、次のページを「アクティブ」なページに設定します。
これにより、現在のページがめくられて次のページが表示されるような動作になります。

6. 初期状態の設定

```javascript
document.querySelector(".page").classList.add("flipped", "initial");
document.querySelector(".page:nth-of-type(2)").classList.add("active");
```

最初のページをフリップ状態 (flipped) と初期状態 (initial) にし、2 番目のページに active クラスを追加して表示されるようにします。

## まとめ

この 2 つの関数により、nextPage() で次のページに進み、prevPage() で前のページに戻る動作が実現されています。これにより、ページをめくるような操作を簡単に実現でき、現在のページがどれなのかをクラスによって管理しています。