项目内容
利用 HTML 构建书本结构,CSS 设置 3D 场景并设计样式,JavaScript 实现鼠标交互计算与控制,使书本能够沿 Y 轴进行 3D 旋转,配合 CSS animation 实现平滑流畅的翻页动画效果,并协调书影、书内容等组件的变换,实现了一个交互式的 3D 翻书动画,具有较好的技术实现和细节优化。
1. HTML结构
我们可以从结构的角度来描述这个HTML布局:
书本由两个主要部分组成------左侧的正文部分(front-cover)和右侧的书皮部分(book-cover)。
在书皮部分,我们使用一个pic盒子容纳书皮的图片,pic盒子在翻动书的过程中会缓慢立起来并使用一个shadow盒子添加投影效果。
而在正文部分,我们使用一个back盒子放置背面的装饰图片,一个front盒子放置需要展示的正文内容。
这样通过HTML的结构化标签布局,我们分区划分了书本的不同组成部分,为后续的CSS样式设计和JS交互提供了基础。这种语义化的结构化思维,是编写可维护代码的重要方式。
- book 代表整个书本
- book-cover 代表书皮部分
- front-cover 代表正文页部分
- page 分别代表书皮和正文的前后页
- shadow 和 pic 代表正文页上的阴影和图片
- 结构如下代码所示:
xml
<div class="book p3d">
<div class="book-cover p3d">
<div class="page back flip"></div>
<div class="page front p3d">
<div class="shadow"></div>
<div class="pic"></div>
</div>
</div>
<div class="front-cover p3d">
<div class="page front flip p3d">
<p>
</p>
</div>
<div class="page back"></div>
</div>
</div>
2. CSS样式
上面我们已经设置好了该项目的基本结构,接下来我们要使用css来实现翻转动画。具体可以分为以下几个步骤:
1.1 书本样式
- 设置了书本的大小、位置、旋转角度等
- transform属性使其呈现开书状态
1.2 书页样式
- 绝对定位在书本内部构成书页
- 设置了前后页的背景颜色
1.3 正文样式
- 设置了transform-origin作为旋转原点
- 保证翻转时书脊不动
1.4 背面页样式
- 添加了背景图片
- 用translateZ使其稍微偏离正面
1.5 动画样式
- 利用transition实现平滑翻页效果
1.6 书影与内容样式
- 设置大小、位置、背景等
- 与书页位置相关联
1.7 3D场景构建
- perspective属性开启3D场景
- transform-style开启子元素3D转换
css代码如下:
css
*{
margin: 0;
padding: 0;
border: 0;
box-sizing: border-box;
}
html{
height: 100%;
}
body{
height: 100%;
font: 100%/1.25 Helvetica, arial, helvetica;
color: #fff;
perspective: 1000px;
background: linear-gradient(to bottom, #444, #999);
}
.book{
width: 300px;
height: 300px;
position: absolute;
left: 50%;
margin-left: -150px;
top: 50%;
margin-top: -150px;
cursor: pointer;
user-select: none;
transform: rotateX(60deg);
}
.page{
width: 300px;
height: 300px;
padding: 1em;
position: absolute;
left: 0;
top: 0;
text-indent: 2em;
}
.front{
background-color: rgb(50, 86, 163);
}
.back{
background-color: #fff;
}
.front-cover{
transform-origin: 0 50%;
transform: rotateY(0deg);
}
.p3d{
transform-style: preserve-3d;
}
.front-cover .back{
background-image: url(https://ts1.cn.mm.bing.net/th/id/R-C.b130c87b75a17febd7f7ff8934238178?rik=sVqYsZWZ9sOmVA&riu=http%3a%2f%2fpic.baike.soso.com%2fp%2f20131221%2f20131221174024-912240027.jpg&ehk=TiyBsTIzWYVfwoi8BrKTPVZeble44GPuqAXOrQdC2Hk%3d&risl=&pid=ImgRaw&r=0);
background-size: cover; /* 将背景图片缩放以完全包含在元素内 */
transform: translateZ(3px);
}
.flip{
transform: rotateY(180deg);
}
.shadow,
.pic{
width: 196px;
height: 132px;
position: absolute;
left: 60px;
top: 60px;
transform-origin: 0 100%;
}
.pic{
background: url(https://tse1-mm.cn.bing.net/th/id/OIP-C.YptwK-yAXqzoIxrBYtbc2wAAAA?rs=1&pid=ImgDetMain);
background-size: cover;
}
.shadow{
background-color: rgba(0, 0, 0, 0.5);
}
3. JavaScript 实现 3D翻页效果
在使用JavaScript实现3D翻页效果主要分为以下几个步骤:
- 创建监听鼠标事件,定义公式计算旋转角度
- 控制元素的transform角度产生3D旋转效果
- 配合CSS动画实现翻书交互
js代码如下:
xml
<script>
// 鼠标摁住事件
// 鼠标移动事件
var hold = false
var page = document.querySelector('.front-cover')
var pic = document.querySelector('.pic')
var shadow = document.querySelector('.shadow')
var clamp = function(val, min, max) {
return Math.max(min, Math.min(val, max))
}
page.onmousedown = function () {
hold = true
}
window.onmouseup = function () {
hold = false
}
window.onmousemove = function(e) { // 摁住才能执行
if (hold == true) {
console.log(e.pageX);
var angle = clamp((window.innerWidth / 2 - e.pageX + 300) / 300 * -90, -180, 0)
page.style.transform = `rotateY(${angle}deg)`
// pic 要立起来 饶x轴旋转 angle / 2
pic.style.transform = `rotateX(${angle / 2}deg)`
// shadow 要倾斜x angle / 8
shadow.style.transform = `skewX(${angle / 8}deg)`
}
}
</script>
4. 项目运行效果


5. 项目源码
html 与js 部分:index.html
xml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="book p3d">
<div class="book-cover p3d">
<div class="page back flip"></div>
<div class="page front p3d">
<div class="shadow"></div>
<div class="pic"></div>
</div>
</div>
<div class="front-cover p3d">
<div class="page front flip p3d">
<p>
亲爱的客户们,
我代表整个团队向您送上最诚挚的祝福!感谢您选择我们作为您的汽车销售伙伴。您的信任和支持对我们来说意义重大。
在您选择购买一辆汽车时,我们深知这是一项重要的决定。因此,我们始终致力于为您提供最优质的产品和最专业的服务。您的满意是我们工作的最大动力,也是我们最大的荣耀。
再次感谢您选择我们,成为我们的尊贵客户。我们期待着与您共同开启汽车之旅,为您提供无与伦比的驾驶体验。
祝您驾驶愉快,一路平安!
</p>
</div>
<div class="page back"></div>
</div>
</div>
<script>
// 鼠标摁住事件
// 鼠标移动事件
var hold = false
var page = document.querySelector('.front-cover')
var pic = document.querySelector('.pic')
var shadow = document.querySelector('.shadow')
var clamp = function(val, min, max) {
return Math.max(min, Math.min(val, max))
}
page.onmousedown = function () {
hold = true
}
window.onmouseup = function () {
hold = false
}
window.onmousemove = function(e) { // 摁住才能执行
if (hold == true) {
console.log(e.pageX);
var angle = clamp((window.innerWidth / 2 - e.pageX + 300) / 300 * -90, -180, 0)
page.style.transform = `rotateY(${angle}deg)`
// pic 要立起来 饶x轴旋转 angle / 2
pic.style.transform = `rotateX(${angle / 2}deg)`
// shadow 要倾斜x angle / 8
shadow.style.transform = `skewX(${angle / 8}deg)`
}
}
</script>
</body>
</html>
css 部分 : style.css
css
*{
margin: 0;
padding: 0;
border: 0;
box-sizing: border-box;
}
html{
height: 100%;
}
body{
height: 100%;
font: 100%/1.25 Helvetica, arial, helvetica;
color: #fff;
perspective: 1000px;
background: linear-gradient(to bottom, #444, #999);
}
.book{
width: 300px;
height: 300px;
position: absolute;
left: 50%;
margin-left: -150px;
top: 50%;
margin-top: -150px;
cursor: pointer;
user-select: none;
transform: rotateX(60deg);
}
.page{
width: 300px;
height: 300px;
padding: 1em;
position: absolute;
left: 0;
top: 0;
text-indent: 2em;
}
.front{
background-color: rgb(50, 86, 163);
}
.back{
background-color: #fff;
}
.front-cover{
transform-origin: 0 50%;
transform: rotateY(0deg);
}
.p3d{
transform-style: preserve-3d;
}
.front-cover .back{
background-image: url(https://ts1.cn.mm.bing.net/th/id/R-C.b130c87b75a17febd7f7ff8934238178?rik=sVqYsZWZ9sOmVA&riu=http%3a%2f%2fpic.baike.soso.com%2fp%2f20131221%2f20131221174024-912240027.jpg&ehk=TiyBsTIzWYVfwoi8BrKTPVZeble44GPuqAXOrQdC2Hk%3d&risl=&pid=ImgRaw&r=0);
background-size: cover; /* 将背景图片缩放以完全包含在元素内 */
transform: translateZ(3px);
}
.flip{
transform: rotateY(180deg);
}
.shadow,
.pic{
width: 196px;
height: 132px;
position: absolute;
left: 60px;
top: 60px;
transform-origin: 0 100%;
}
.pic{
background: url(https://tse1-mm.cn.bing.net/th/id/OIP-C.YptwK-yAXqzoIxrBYtbc2wAAAA?rs=1&pid=ImgDetMain);
background-size: cover;
}
.shadow{
background-color: rgba(0, 0, 0, 0.5);
}