一、前言
本文将使用纯 CSS 实现一个简单的 3D 书本展开动效。

二、实现思路
实现这么一个书本动效 乍一看可能会感觉有些复杂,实际很并不难,遇到这种组合动效的需求时,我们只要将整体拆分成多个小步骤去做,就很简单了。
1. 拆分主体
在实现动效前,我们需要先将书本画出来,画一个本子,我们可以先简单分成三个元素:封皮、书脊、扉页
2. CSS 变量声明与使用
本不打算加入这段,但考虑到有些没有用过的读者,还是讲一下。
在现代 CSS 中,在不使用预处理器的情况下,我们也可以声明 CSS 变量,在当前场景下,我们可以直接将书本的主题色与大小设置为变量,这样我们可以轻松的修改整个书本的样式。
变量声明
CSS 变量也成为 CSS 自定义属性。
CSS 变量定义:带有前缀 --
的属性名,比如 --example--name
,表示的是带有值的自定义属性
:root
伪类匹配文档树的根元素。对于 HTML 来说,:root
表示 <html>
元素,除了优先级更高之外,与 html 选择器相同,我们可以在 :root
伪类中声明全局 CSS 变量。
1 2 3
| :root { --color: red; }
|
变量使用
可以通过 var()
函数在使用声明的变量。
1 2 3
| div { color: var(--color) }
|
3. 绘制书本主体
根据第一步的拆分,我们将三个主要部分绘制出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| <style> :root { --main-height: 150px; --theme-bg-color: rgba(239, 112, 155, .9); }
div { box-sizing: border-box; }
#book { position: relative; margin: 150px auto 80px auto; width: 150px; height: var(--main-height); cursor: pointer; }
.book-side { position: absolute; left: -14px; width: 15px; height: var(--main-height); border-radius: 5px 0 0 5px;
background-color: var(--theme-bg-color); }
.book--cover { position: absolute; top: 0; left: -1px; padding: 10px; width: 105px; height: var(--main-height); font-size: 18px; font-weight: 400; border-radius: 0 6px 6px 0;
background-color: var(--theme-bg-color); }
.book--content { padding: 4px 5px 4px 2px; width: 105px; height: var(--main-height); border-radius: 2px;
background-color: var(--theme-bg-color); }
.book--inner { padding: 10px 0; width: 100%; height: 100%; color: #333; border-radius: 1px;
box-shadow: 0px 2px 14px 0px rgba(0, 0, 0, 0.2); background-color: rgba(250, 250, 250, 0.8); } </style>
<body> <div id="book"> <div class="book-side"></div> <div class="book--cover">封面</div> <div class="book--content"> <div class="book--inner"></div> </div> </div> </body>
|
实现效果

4. 书本张开效果
现在虽然有了雏形,但是完全闭合的书本并不好看,我们让他有一点微微打开的效果。
通过 rotate
函数,以 x 轴为逆时针旋转 8°, 以 y 轴为顺时针旋转 30°
1 2 3 4
| .book--cover { transform: rotateX(-8deg) rotateY(30deg); }
|
实现效果如下,可以看到有了一些样子,但是旋转是以中心进行旋转的,偏离了书脊,不符合我们的预期。

可以通过 transform-origin
改变元素变型的原点 来调整旋转的位置。
设置 x 轴以左侧为旋转点,y 轴以中心为旋转点进行旋转。
1 2 3 4 5
| .book--cover { transform: rotateX(-8deg) rotateY(30deg); transform-origin: left center; }
|
实现效果

5. 实现动效
完成了书本的基本骨架绘制后,我们可以将动画效果加进来了
鼠标悬浮动效
通过 :hover
伪类设置鼠标悬浮后的效果, 和第四步其实一样,打开效果通过 rotateX
与 rotateY
实现,为了模拟的更加真实,在掀开时加入阴影。
1 2 3 4
| #book:hover .book--cover { transform: rotateX(-10deg) rotateY(50deg); box-shadow: 30px 0 10px 0 rgba(0, 0, 0, 0.2); }
|
这样基本的打开效果就实现好了,但是太过直接,不够平滑,我们对元素加入过度效果。
1 2 3 4 5
| .book--cover { transform: rotateX(-8deg) rotateY(30deg); transform-origin: left center; }
|
这样基本的鼠标效果就实现好了。
书本打开动效
通过 :active
伪类设置长按时的打开效果, 通过 scale
将书本整体适当放大, 并将 封皮 的打开效果尽量放大即可。
1 2 3 4 5 6 7
| #book:active { transform: scale(1.7); }
#book:active .book--cover { transform: rotateX(-10deg) rotateY(138deg); }
|
添加上面的代码后,便实现了长按打开的效果,为了放大效果更加自然平滑,对 #book
元素添加过度效果
1 2 3 4
| #book { transition: all .7s linear; }
|
如此整体功能就实现完成了。
6. 美化内容
功能玩成后,我们便可以进一步对书本进行美化,现在整个书本太空,加入一些文字回显的更加丰满充实。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <div id="book"> <div class="book-side"></div> <div class="book--content"> <div class="book--cover"> CSS 圣经 </div> <div class="book--inner"> <div style="padding-left: 10px"> <div class="book-page--content boo-page--title">~~~</div> <div class="book-page--content">~~~~~~~~</div> <div class="book-page--content">~~~~~~~~</div> <div class="book-page--content">~~~~~~~~</div> <div class="book-page--content">~~~~~~~~</div> </div> </div> </div> </div>
|

三、完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
|
<!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D 书本动效</title>
<style> :root { --main-height: 150px; --theme-bg-color: rgba(239, 112, 155, .9); }
div { box-sizing: border-box; }
#book { position: relative; margin: 150px auto 80px auto; width: 150px; height: var(--main-height); transition: all .7s linear; cursor: pointer; }
.book-side { position: absolute; left: -14px; width: 15px; height: var(--main-height); border-radius: 5px 0 0 5px;
background-color: var(--theme-bg-color); }
.book--cover { position: absolute; top: 0; left: -1px; padding: 10px; width: 105px; height: var(--main-height); font-size: 18px; font-weight: 400; border-radius: 0 6px 6px 0; transform-origin: left center; transform: rotateX(-8deg) rotateY(30deg); transition: all .7s linear;
background-color: var(--theme-bg-color); }
#book:active { transform: scale(1.7); }
#book:hover .book--cover { transform: rotateX(-10deg) rotateY(50deg); box-shadow: 30px 0 10px 0 rgba(0, 0, 0, 0.2); }
#book:active .book--cover { transform: rotateX(-10deg) rotateY(138deg); }
.book--content { padding: 4px 5px 4px 2px; width: 105px; height: var(--main-height); border-radius: 2px;
background-color: var(--theme-bg-color); }
.book--inner { padding: 10px 0; width: 100%; height: 100%; color: #333; border-radius: 1px;
box-shadow: 0px 2px 14px 0px rgba(0, 0, 0, 0.2); background-color: rgba(250, 250, 250, 0.8); } </style> </head>
<body> <div id="book"> <div class="book-side"></div> <div class="book--cover">CSS 圣经</div> <div class="book--content"> <div class="book--inner"> <div style="padding-left: 10px"> <div class="book-page--content boo-page--title">~~~</div> <div class="book-page--content">~~~~~~~~</div> <div class="book-page--content">~~~~~~~~</div> <div class="book-page--content">~~~~~~~~</div> <div class="book-page--content">~~~~~~~~</div> </div> </div> </div> </div>
<p style="text-align:center">长按书本,即可展开书本</p> </body>
</html>
|