ページめくりアニメーション 作り方。

〽️基本の仕組みを考える。 〽️画像を重ねて配置。 〽️アニメイション。 〽️自由に貼ってみよう。





⬆︎SVGついてのまとめページはこちら。

こんにちは、「ふ」です。
前回はSVG領域に画像を配置し、「見開きページ風」に切り替える、というのを作りました⬇︎。


SVGとCSSで、 見開きページ風に画像切り替え。

2021.01.24
SVG領域を確保すると便利。


今回はその続編です。もっとたくさんの画像を使って、ユーザのクリックイベントに対応した「ページめくりアニメーション」を作っていきましょう。
さらに完成したプログラムは「画像のurlを貼るだけ」でシステムが実装できる、というものにしたいと思います。そのためCSSアニメーションではなく、制御に優れたWeb Animations APIを使います。

では一緒に作っていきましょう♪

基本の仕組みを考える。


前回は2つの画像だけで行いましたが、今回はもっと多くの画像を想定します。
〜と、なると、どのような仕組みにすればよいのでしょうか?

3枚の画像⬆︎を例に考えてみます。




前回と同じく、左右半分ずつの画像に分割。

左側は背面から「青→赤→緑」と並べます。
それに対し右側の画像は重なりを逆にします。背面から「緑→赤→青」としています。

真上からみた様子です。
現在画面上には左に緑、右に青が表示されています。

ここで、左最背面の青は正面のまま、前面にある赤と緑はY軸を基点に-90°回転させ、事実上非表示にします。右側は3枚ともそのままにしておきます。

すると、画面には左右ともに青が表示されているはず。
これが初期状態です。

ページを1枚めくります。右の最前面の青をY軸基点で徐々に時計方向へと回転。

90°(実質非表示)になったところで固定。
右側は2ページ目の赤が完全に表示されました。

そのあと左側の2番目に重なっている赤を-90°から0°まで回転。

両側に赤が表示され、1ページめくった状態になりました。

これで、1turnのアニメーション完了とします。2ページ以降も同じ動きで再現できます。
また再生方向を順方向/逆方向とすることで、ページを進んだり戻ったりも可能に成増。

画像を重ねて配置。


画像を配置していきましょう。
HTMLの<body> 〜 </body>内にSVG領域を作ります。今回もA4比率としました。

◼︎ HTML > body

<svg viewBox = "0 0 297 210"> 「「1<!-- ① 左側の画像エリア -->」」 <g id = "leftimg"> <image xlink:href = "ao.png"/> <image xlink:href = "aka.png"/> <image xlink:href = "mido.png"/> </g> 「「1<!-- ② 右側の画像エリア -->」」 <g id = "rightimg"> </g> 「「1<!-- ③ clip用の長方形 -->」」 <clipPath id = "leftclip"> <rect width = "50%" height = "100%"/> </clipPath> <clipPath id = "rightclip"> <rect 「「4x = "50%"」」 width = "50%" height = "100%"/> </clipPath> </svg>

① 左画像を置くグループ「leftimg」を作り、その中に画像のリンクを記述します。

② 右画像を置く「rightimg」グループも作っておきます。
ここには後からleftimgの子要素をコピペするので、今は何も入れていません。

③ 左右半分サイズの長方形を作り、これで元画像をクリップします。結果、2分割された画像が出来上がります。

左クリップ用の「leftclip」。<rect>要素は始点座標を宣言しない場合はデフォルトの(0,0)が始点となるので、ここでは省略しています。
右クリップようの「rightclip」、これについてはSVG領域の右側に寄せるため、始点のx座標を「50%」に指定しました。



◼︎ CSS

「「1<!-- ④ SVGのスタイリング -->」」 svg { height:auto; width:60%; display:block; margin:0 auto; 「「4overflow:visible;」」 } 「「1<!-- ⑤ 読み込んだimg -->」」 image { height:100%; width:100%; 「「4transform-origin:center;」」 } 「「1<!-- ⑥ 左右をクリップ -->」」 #leftimg image { clip-path:url(#leftclip); } #rightimg image { clip-path:url(#rightclip); }

④ CSSです。SVGの位置や大きさはお好みで。
ページを立ち上げた際、領域からはみ出した部分を表示させたい。overflowを「visible」にしておきます。

⑤ SVG領域内に収める画像。
回転軸を中央にすべく、transform-originを「center」にしましょう。

⑥ 「leftimg」グループにおいた画像は左側の長方形でクリップ。「rightimg」の画像(このあと貼り付けます)は右側でクリップします。
これで左右3枚ずつの半分画像を作成します。

扨(さて)この状態から「leftimg」に貼った画像を複製し「rightimg」に格納する作業と、左側の画像を-90°に立ち上げる作業が残っています。

JavaScriptを書いていきましょう。

◼︎ JavaScript

「「1// ⑦ 左右の画像グループを取得」」 const leftimg = document.getElementById("leftimg"); const rightimg = document.getElementById("rightimg"); 「「1// ⑧ 子要素を複製して格納」」 for(i = leftimg.children.length; i>0; i--) { bblet clone = leftimg.children[(i-1)].cloneNode(true); bbrightimg.appendChild(clone); } 「「1// ⑨ 左の最背面以外の画像を立ち上げ」」 for(i=1; i<leftimg.children.length; i++) { leftimg.children[i].style.transform = "rotateY(-90deg)"; }

⑦ 先づは左右の画像グループを取得。

⑧ SVGでは、先に記述された要素が背面に置かれます。「leftimg」の最後の子要素から取り出し、cloneNodeメソッドで複製。
それを「rightimg」に格納するのですが、親要素にappendChildメソッドを使うと、子要素の最後尾に追加されます。

これを繰り返すことで、「rightimg」に順番を逆転させて画像を追加していきます。

⑨ 作戦どおり「leftimg」の最背面以外の画像を-90°回転させて、非表示にしています。

〜ボタンをテキトーに作りました。
コーダーさんが見ると、笑われるかもしれません。ふふふ。

◼︎ HTML

<div id = "buttonarea"> 「「1<!-- ⑩ onclickイベントをつけておく -->」」 <button id = "modoru" onclick = "modoru()"> ◀︎ </button> <button id = "susumu" onclick = "susumu()"> ▶︎ </button> </div>

◼︎ CSS

#buttonarea { display:flex; width:80%; margin:0 auto; } button { width:100%; background-color:transparent; border:none; margin-top:1em; padding:0; font-size:2em; color:#222; }

⑩ ボタンにはそれぞれonclickイベントで、ページを進む/戻るのアニメーション関数を呼び出せるようにしておきます。


ここまでのプレビューがこちら。
初期状態が完成しました。

アニメイション。


ではアニメーションを仕込みます。

◼︎ JavaScript

「「1// ⑪ 現在のページを管理するためのフラグ」」 let flag = 1; 「「1// ⑫ タイミングオブジェクト」」 let timing = { bbduration:1000, bb「「4fill:"forwards"」」, bb「「4direction:"normal"」」 }; 「「1// ⑬ キーフレーム」」 let keyframeL = [ bb{transform:"rotateY(-90deg) skewY(20deg)"}, bb{transform:"rotateY(-90deg) skewY(20deg)"}, bb{transform:"rotateY(0deg) skewY(0deg)"} ]; let keyframeR = [ bb{transform:"rotateY(0deg) skewY(0deg)"}, bb{transform:"rotateY(90deg) skewY(-20deg)"}, bb{transform:"rotateY(90deg) skewY(-20deg)"} ]; 「「1// ⑭ 「進む」関数」」 function susumu() { bbif (flag < rightimg.children.length) { bbbbtiming.direction = "normal"; bbbbrightimg.children[(rightimg.children.length)-(flag)].animate(keyframeR,timing); bbbbleftimg.children[flag].animate(keyframeL,timing); bbbbflag++; bb} else { bbbbreturn; bb} }; 「「1// ⑮ 「戻る」関数」」 function modoru() { bbif(flag > 1) { bbbbtiming.direction = "reverse"; bbbbleftimg.children[flag-1].animate(keyframeL,timing); bbbbrightimg.children[(rightimg.children.length)-(flag-1)].animate(keyframeR,timing); bbbbflag--; bb} else { bbbbreturn; bb} };

⑪ 「現在何ページ目の状態か」を判別するフラグ用の変数です。
アニメーションが実行されるたびに加算/減算させます。


⑫ タイミングオブジェクト。

duration(継続時間):1秒

fill(終了時の状態):forward(状態を保持)
これを指定しないと、アニメーションが終わったときに開始前の状態に戻ってしまいます。

direction(方向):通常
初期値は「通常」にしましたが、「戻る」ボタンを押した時は「reverse(逆再生)」に変更します。


⑬ キーフレームは、ページを進んだ時と戻った時の2種類を用意しました。
ページが回転し立ち上がるに連れて、rotateYで少し傾きを付けます。


⑭ 「進む」関数です。

最終ページの状態だとそれ以上進むことができないため、if文でリミッターをかけます。

direction(アニメーションの方向)はnormalのまま。

左右それぞれアニメーションさせ、flagを1増やします。


⑮ 「戻る」関数はflagの扱いが少しややこしくなりました。つじつまが合うように1を引いたりしています。

これも最初のページ状態だとreturn。

directionはreverse(逆再生)に。

flagを1つ減らします。

結果。




結果はこちら⬆︎。
実際にボタンを押してみてください、ページめくりアニメーションが実行されます。

これで完成です。お疲れ様でした!

自由に貼ってみよう。


うまくできましたか?
記事中では単純な画像3枚を使って進めてきましたが、「leftimg」の子要素に好きなだけ画像を読み込ませて、「ページめくりアニメーション」を実装することができます。試してみてください。

最後までお読みいただき、ありがとうございます。
また新しいアニメーションができたら、記事にしたいと思いますのでそのときお会いしましょう。ではまた〜 ♫



関連記事


HTMLで画像を重ねる、便利な方法。

2021.01.20
positionやz-index指定なし。


SVGでWeb Animations API。

2020.11.12
〽️ ネイティブJavaScriptでのアニメーション。


SVG、use要素を使ったモーションパス。

2020.09.07
〽️ ポイントは‥「マイナスの遅れ」。



SVG、波を表現する。

2021.01.16
合成されたリアルな波を目指す。












「ふ」です。

swift、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。