【CSS】本のページをめくる有機的な動きをCSSでキレイに実装する

ちょっと必要があってそんなwebページを実装したのですが、あんまりこういう実装を紹介してくれている人がいなかったので、書いておきます。

まあ紹介しない理由は分かります。めんどくさいんですよ、これ。

パターン1:よくある簡単な実装

まずは簡単な実装です。rotateYを使って要素を180度フリップします。

(↓にhoverするとページがめくれます)

<style type="text/css">
#book1 {
  display: flex;
  flex-direction: row;
  perspective: 1000px;
}

.page, .page-lock {
  width: 200px;
  height: 300px;
  border: 1px solid black;
  background-color: beige;
  position: relative;
  transform-style: preserve-3d;
  transition: 1s;
}

.page {
  transform-origin: left center;
}

#book1:hover .page {
  transform: rotateY(-180deg);
}
</style>
<div id="book1">
  <div class="page-lock">
  </div>
  <div class="page">
  </div>
</div>

回転させたい要素にtransform-style: preserve-3d;と、親要素に perspective: 1000px;(ここでは1000pxにしましたが数字は何でもいいです) を指定することで、奥行きのある動きが可能になります。cssでページめくりのアニメーションを実装しようとして調べて出てくるのはほとんどこんな感じだと思います。

しかし、この動きだとなんだか紙をめくっているというよりは板を回しているように感じられるかもしれません。

パターン2:滑らかだけどめんどくさい実装

こちらをご覧ください。

(↓にhoverするとページがめくれます)

<style type="text/css">
#book2 {
  position: relative;
  width: 400px;
  height: 350px;
  margin: 10px;
}

.page2, .page-lock2 {
  width: 200px;
  height: 300px;
  background-color: transparent;
  border: 1px solid black;
  transition: 1s;
}

#page2-wrapper {
  width: 400px;
  height: 800px;
  position: absolute;
  top: -346.4px;
  right: 200px;
  transform-origin: top right;
  transform: rotate(-30deg);
  transition: 1s;
  overflow: hidden;
}
#page2 {
  position: absolute;
  top: 346.4px;
  left: 100%;
  background: #bcffcc;
  transform-origin: 0 -346.4px;
  transform: rotate(30deg);
  transition: 1s;
}
#page3-wrapper {
  width: 400px;
  height: 800px;
  position: absolute;
  top: -346.4px;
  right: 200px;
  transform-origin: top right;
  transform: rotate(-30deg);
  transition: 1s;
  overflow: hidden;
}
#page3 {
  position: absolute;
  top: 346.4px;
  left: 50%;
  background: #feccaa;
  transform-origin: 200px -346.4px;
  transform: rotate(-30deg);
  transition: 1s;
}
#book2:hover #page2-wrapper,#book2:hover #page2,#book2:hover #page3-wrapper,#book2:hover #page3{
  transform: rotate(0deg);
}
</style>
<div id="book2">
  <div class="page-lock2"></div>
  <div id="page2-wrapper">
    <div id="page2" class="page2"></div>
  </div>
  <div id="page3-wrapper">
    <div id="page3" class="page2"></div>
  </div>
</div>

どうでしょう? 紙のページをめくっているような雰囲気が出ていませんか? これに紙をめくるSEでもつけてやれば完璧ですね。

ただし、パターン1で実装するよりもはるかに面倒です。まず、html の構造からして大きく違っています。

パターン1の html

<div id="book1">
  <div class="page-lock"> <!-これが左ページ->
  </div>
  <div class="page"> <!-これが右ページ->
  </div>
</div>

パターン2の html

<div id="book2">
  <div class="page-lock2"></div> <!-これが左ページ->
  <div id="page2-wrapper">
    <div id="page2" class="page2"></div> <!-これが右ページの表->
  </div>
  <div id="page3-wrapper">
    <div id="page3" class="page2"></div> <!-これが右ページの裏->
  </div>
</div>

表示されていない wrapper 要素は、overflow: hidden; を指定して子要素の一部を切り取るために作っています。

実際にこの wrapper 要素をすべて表示し、 overflow: visible; を指定するとこんな感じになります。

wrapper 要素の右上が回転の軸になるようにして、右ページの裏と同時に回転させています。position は三角関数で手計算してそれっぽい数字を入れておけばOKです。

CSSを完全に理解していないとこの実装はなかなか難しいとは思いますが、慣れるまで試行錯誤してみるのも面白いですよ。

【おまけ】CSS完全に理解した人にしかできない実装

CSS
完全に理解した

これなに?と思ったあなたはこちらをどうぞ……