小白实战!用JS实现一个3D翻书效果,附上代码

简介: 小白实战!用JS实现一个3D翻书效果,附上代码

前言

今天我们来聊聊,如何实现一个3D翻书的页面,这个效果也是十分的生动和有趣哦,我会结合代码和大家一起制作,看完之后,大家可以选择自行喜欢的图片自己制作哦~也可以复制代码先体验一下效果

首先,我们先来看看我们想要完成的效果

image.png

结构划分

首先,在我们开始制作之前,我们先需要观察一下页面,划分结构,这样也可以将我们的思路打开,这本书的整体我们命名为book p3d,这本书总共有两页,第一页我们叫做front-cover p3d,第二页我们叫做back-cover p3d

我们再仔细观察一下,第一页又可以分为两个部分,正面上有一张图片,我们命名为outside page,用来放置封面的图片,反面写了字,我们可以用inside page p3d flip容器用来存放字体。

接下来到了第二面,<div class="outside page"><div class="inside page p3d">: 这两个div元素分别表示第二页的外部和内部页面。内部页面包括了两个子元素,一个带有类名shadow,另一个带有类名card

我们整体的结构就分完了,接下来我们来看看html的代码

html

<!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="front-cover p3d">
      <div class="inside page p3d flip">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam fermentum nisl quis nulla eleifend dignissim.
          Curabitur varius lobortis tincidunt. Maecenas gravida, nulla quis luctus imperdiet, ipsum nibh consectetur
          ante, in sodales massa tortor eget neque. Donec porta ligula massa, id sagittis est. Ut nisl tellus, faucibus
          nec feugiat ut, laoreet iaculis felis. Suspendisse ultrices mauris vel tellus suscipit commodo. Integer vitae
          tortor erat. Pellentesque non tempor nisi.</p>
      </div>
      <div class="outside page"></div>
    </div>
    <div class="back-cover p3d">
      <div class="outside page"></div>
      <div class="inside page p3d">
        <div class="shadow"></div>
        <div class="card"></div>
      </div>
    </div>
  </div>
  <script src="./index.js"></script>
</body>
</html>

接下来到了CSS部分,我们来写样式

CSS

  1. * {...}: 这是通用样式,将所有HTML元素的默认外边距、内边距、边框、垂直对齐方式进行重置,同时使用盒子模型。这确保了元素的尺寸和布局受到更精确的控制。
*{
    margin: 0;
    padding: 0;
    border: 0;
    vertical-align: baseline;
    box-sizing: border-box;  /* 将容器声明成IE模型 */
  }
  1. body {...}: 这部分样式应用于body元素,设置了页面的字体、透视效果以及背景颜色和背景图像。页面的字体使用Helvetica、Arial、sans-serif字体族,透视效果设置为1000像素,背景颜色渐变从#444到#999。
body{
    height: 100%;
    font: 100%/1.25 Helvetica, arial, sans-serif;
    perspective: 1000px;
    background-color: #444;
    background-image: linear-gradient(to bottom, #444, #999);
  }
  1. .p3d {...}: 这是一个自定义类名,用于设置3D变换效果的元素的样式。它通过transform-style属性来保持3D效果。
.p3d{
    transform-style: preserve-3d;
  }
  1. .book {...}: 这个规则定义了书籍容器的样式。它设置了容器的尺寸、位置、文字颜色以及X轴旋转角度,使书籍看起来有立体感。user-select属性设置为none,以禁止选择文本。
.book{
    width: 300px;
    height: 300px;
    position: absolute;
    left: 50%;
    top: 50%;
    /* transform: translateY(-50%); */
    margin-top: -150px;
    color: #fff;
    -webkit-transform: rotateX(60deg);
    -moz-transform: rotateX(60deg);
    -o-transform: rotateX(60deg);
    user-select: none;
  }
  1. .front-cover {...}: 这部分样式应用于书籍的封面。它定义了封面的鼠标光标样式和Y轴旋转角度。
front-cover{
    cursor: move;
    transform-origin: 0 50%;
    transform: rotateY(0deg);
  }
  1. .page {...}: 这个规则用于定义页面元素的样式,包括尺寸、位置和文本缩进。
.page{
    width: 300px;
    height: 300px;
    padding: 1em;
    position: absolute;
    left: 0;
    top: 0;
    text-indent: 2em;
  }
  1. .inside {...}.outside {...}: 这两个规则分别定义了内部页面和外部页面的样式,其中内部页面的背景颜色设置为#d93e2b,而外部页面的背景颜色为白色。
.inside{
    background-color: #d93e2b;
  }
  .outside{
    background: #fff;
  }
  1. .front-cover .outside {...}: 这个规则应用于封面的外部页面,它设置了背景图片、大小和3D位移效果。
.front-cover .outside{
    background-image: url(https://photo.16pic.com/00/53/63/16pic_5363878_b.jpg);
    background-repeat: no-repeat;
    background-size: cover;
    transform: translateZ(3px);
  }

  
  1. .flip {...}: 这个规则用于创建翻页效果,通过将元素旋转180度来实现。
.flip{
    transform: rotateY(180deg);
  }
  1. .back-cover .outside {...}.back-cover .inside {...}: 这两个规则分别定义了封底的外部和内部页面的样式,类似于封面的设置。
.back-cover .outside{
    transform: translateZ(-3px);
  }
  .back-cover .inside {
    background-color: #d93e2b;
  }
  1. .card {...}.shadow {...}: 这两个规则定义了卡片和阴影的样式,包括尺寸、位置和旋转原点。卡片的背景设置为一个URL链接的图像,而阴影的背景颜色是半透明的黑色。
.card,
  .shadow{
    width: 196px;
    height: 132px;
    position: absolute;
    left: 60px;
    top: 60px;
    transform-origin: 0 100%;
  }
  
  .shadow{
    background-color: rgba(0, 0, 0, 0.5);
  }
  .card{
    background: url(https://foruda.gitee.com/avatar/1677018300688056396/3045275_snail_wn_1620711440.png!avatar200) no-repeat;
    background-size: cover;
  }

图片的话大家放上自己喜欢的图片就可以啦~ 附上完整的CSS代码

*{
    margin: 0;
    padding: 0;
    border: 0;
    vertical-align: baseline;
    box-sizing: border-box;  /* 将容器声明成IE模型 */
  }
  html{
    height: 100%;
  }
  body{
    height: 100%;
    font: 100%/1.25 Helvetica, arial, sans-serif;
    perspective: 1000px;
    background-color: #444;
    background-image: linear-gradient(to bottom, #444, #999);
  }
  .p3d{
    transform-style: preserve-3d;
  }
  
  .book{
    width: 300px;
    height: 300px;
    position: absolute;
    left: 50%;
    top: 50%;
    /* transform: translateY(-50%); */
    margin-top: -150px;
    color: #fff;
    -webkit-transform: rotateX(60deg);
    -moz-transform: rotateX(60deg);
    -o-transform: rotateX(60deg);
    user-select: none;
  }
  .front-cover{
    cursor: move;
    transform-origin: 0 50%;
    transform: rotateY(0deg);
  }
  
  .page{
    width: 300px;
    height: 300px;
    padding: 1em;
    position: absolute;
    left: 0;
    top: 0;
    text-indent: 2em;
  }
  .inside{
    background-color: #d93e2b;
  }
  .outside{
    background: #fff;
  }
  
  .front-cover .outside{
    background-image: url(https://photo.16pic.com/00/53/63/16pic_5363878_b.jpg);
    background-repeat: no-repeat;
    background-size: cover;
    transform: translateZ(3px);
  }
  
  .flip{
    transform: rotateY(180deg);
  }
  
  
  .back-cover .outside{
    transform: translateZ(-3px);
  }
  .back-cover .inside {
    background-color: #d93e2b;
  }
  
  .card,
  .shadow{
    width: 196px;
    height: 132px;
    position: absolute;
    left: 60px;
    top: 60px;
    transform-origin: 0 100%;
  }
  
  .shadow{
    background-color: rgba(0, 0, 0, 0.5);
  }
  .card{
    background: url(https://foruda.gitee.com/avatar/1677018300688056396/3045275_snail_wn_1620711440.png!avatar200) no-repeat;
    background-size: cover;
  }

接下来该到了JS部分了,也是我们此次的重头戏

JS

这个例子中我们使用JS的目的是要监听我们的鼠标,也就是当我们鼠标在书上点时,可以实现翻书的效果,我们先贴上代码

var front = document.getElementsByClassName('front-cover')[0]//byClassName默认以数组返回
var book = document.getElementsByClassName('book')[0]
var card = document.getElementsByClassName('card')[0]
var shadow = document.getElementsByClassName('shadow')[0]
var hold= false
var clamp = function(val,min,max){
    return Math.max(min,Math.min(val,max))
}
//onmousedown监听鼠标是否点击,是否按下
front.onmousedown = function(){
    hold = true
}
window.onmouseup=function(){
    hold = false
}
window.onmousemove=function(e){
    if(hold){
        //修改左半本书的角度,卡片旋转,阴影倾斜
        //鼠标在x轴移动的距离控制角度
        var deg = clamp((window.innerWidth/2-e.x+300)/300 * -90,-180,0)
        front.style.transform = `rotateY(${deg}deg)`
        //整本书立起来60+deg/8
        book.style.transform=`rotateX(${deg/8+60}deg)`
        //卡片立起来deg/2
        card.style.transform = `rotateX(${deg/2}deg)`
        //阴影倾斜的角度deg/8
        shadow.style.transform = `skew(${deg/8}deg)`
    }
}
  1. var front = document.getElementsByClassName('front-cover')[0]: 这行代码通过getElementsByClassName方法获取页面中具有类名front-cover的元素,并将第一个匹配的元素存储在front变量中。这个元素通常是书籍的封面。
  2. var book = document.getElementsByClassName('book')[0]: 同样,这行代码获取页面中具有类名book的元素,通常是整本书的容器,然后将其存储在book变量中。
  3. var card = document.getElementsByClassName('card')[0]: 这行代码获取类名为card的元素,通常是卡片,然后将其存储在card变量中。
  4. var shadow = document.getElementsByClassName('shadow')[0]: 同样,这行代码获取具有类名shadow的元素,通常表示阴影,然后将其存储在shadow变量中。
  5. var hold = false: 这行代码创建一个布尔变量hold,用于标记鼠标是否按下。初始值设置为false,表示鼠标未按下。
  6. var clamp = function(val, min, max) {...}: 这是一个自定义的JavaScript函数,用于将给定的值val限制在指定的最小值min和最大值max之间。如果val小于min,则返回min;如果val大于max,则返回max;否则返回val本身。
  7. front.onmousedown = function() {...}: 这行代码为封面元素front添加一个鼠标按下事件监听器。当用户按下鼠标按钮时,触发这个事件处理函数,将hold变量设置为true,表示鼠标按下。
  8. window.onmouseup = function() {...}: 这行代码为window对象添加一个鼠标松开事件监听器。当用户释放鼠标按钮时,触发这个事件处理函数,将hold变量设置为false,表示鼠标松开。
  9. window.onmousemove = function(e) {...}: 这行代码为window对象添加一个鼠标移动事件监听器。当用户移动鼠标时,触发这个事件处理函数。在事件处理函数中,首先检查hold变量的值,如果鼠标按下(holdtrue),则执行下面的代码块。
  10. var deg = clamp((window.innerWidth / 2 - e.x + 300) / 300 * -90, -180, 0): 这行代码计算一个角度值deg,该角度值受鼠标在x轴上的移动距离影响。clamp函数用来确保deg在指定范围内,最小值为-180度,最大值为0度。
  11. front.style.transform = rotateY(${deg}deg);: 这行代码通过修改front元素的transform属性,将封面元素绕Y轴旋转,角度由deg值控制。这实现了翻页效果。
  12. book.style.transform = rotateX(${deg/8+60}deg);: 这行代码修改整本书的容器元素booktransform属性,使书本立起来,角度由deg值的八分之一加60度控制。
  13. card.style.transform = rotateX(${deg/2}deg);: 这行代码修改卡片元素cardtransform属性,使卡片立起来,角度由deg值的二分之一控制。
  14. shadow.style.transform = skew(${deg/8}deg);: 这行代码修改阴影元素shadowtransform属性,使阴影产生倾斜效果,角度由deg值的八分之一控制。

总之,这段代码创建了一个具有3D翻页效果的交互式书籍,用户可以通过拖动鼠标来实现书页的翻动,同时调整书页、卡片和阴影的角度,以营造真实的书本翻页效果。

现在,我们这个3D翻书效果已经制作完成啦~小伙伴们赶快去试试吧,也可以分享给你们的小伙伴一起观看一起完成呀

image.png

今天的内容就到这啦,如果你觉得小编写的还不错的话,或者对你有所启发,请给小编一个辛苦的赞吧

相关文章
|
5天前
|
JavaScript
Nest.js 实战 (十一):配置热重载 HMR 给服务提提速
这篇文章介绍了Nest.js服务在应用程序引导过程中,TypeScript编译对效率的影响,以及如何通过使用webpackHMR来降低应用实例化的时间。文章包含具体教程,指导读者如何在项目中安装依赖包,并在根目录下新增webpack配置文件webpack-hmr.config.js来调整HMR相关的配置。最后,文章总结了如何通过自定义webpack配置来更好地控制HMR行为。
|
9天前
|
消息中间件 JavaScript 中间件
函数计算产品使用问题之WebIDE编写的Node.js代码是否会自动进行打包部署
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
11天前
|
JavaScript 前端开发 编译器
解锁JavaScript模块化编程新纪元:从CommonJS的基石到ES Modules的飞跃,探索代码组织的艺术与科学
【8月更文挑战第27天】随着Web应用复杂度的提升,JavaScript模块化编程变得至关重要,它能有效降低代码耦合度并提高项目可维护性及扩展性。从CommonJS到ES Modules,模块化标准经历了显著的发展。CommonJS最初专为服务器端设计,通过`require()`同步加载模块。而ES Modules作为官方标准,支持异步加载,更适合浏览器环境,并且能够进行静态分析以优化性能。这两种标准各有特色,但ES Modules凭借其更广泛的跨平台兼容性和现代语法逐渐成为主流。这一演进不仅标志着JavaScript模块化的成熟,也反映了整个JavaScript生态系统的不断完善。
28 3
|
8天前
|
SQL 运维 监控
Nest.js 实战 (十):使用 winston 打印和收集日志记录
这篇文章介绍了在Nest服务中如何使用Winston记录日志。文章首先强调了日志记录在后台服务中的重要性,接着提到Nest默认的内部日志记录器,并指出可以通过@nestjs/common包中的Logger类来全面控制日志系统的行为。文章还提到,为了在生产环境中实现更高级的日志功能,可以使用如Winston之类的Node.js日志包。接下来,文章介绍了如何在Nest服务中使用Winston记录日志,包括安装相关依赖、创建winston配置文件以及实现简单的日志记录示例。最后,文章指出更高级的自定义日志功能需要读者自己去探索。
Nest.js 实战 (十):使用 winston 打印和收集日志记录
|
6天前
|
JSON JavaScript 前端开发
如何使用代码注释:关于JavaScript与TypeScript
TSDoc是一种标准化TypeScript代码文档注释的规范,使不同工具能无干扰地提取内容。它包括多种标记,如@alpha、@beta等发布阶段标记;@decorator、@deprecated等功能标记;@defaultValue、@eventProperty等描述标记;@example、@experimental等示例与实验性标记;@inheritDoc、@internal等引用与内部标记;@label、@link等链接标记;@override、@sealed等修饰符标记;以及@packageDocumentation、@param、
18 5
|
8天前
|
JavaScript 前端开发 UED
JavaScript代码技巧大分享,在数组中去重元素
本文介绍了一系列实用的JavaScript函数,包括将内容复制到剪贴板、获取鼠标选中内容、打乱数组顺序、颜色值转换(RGBA与十六进制)、计算平均值、判断奇偶数、数组去重、检查空对象、反转字符串、计算日期间隔、首字母大写、生成随机字符串和随机数等,帮助提升网站的用户体验和功能丰富性。
15 4
|
8天前
|
JavaScript 前端开发 测试技术
如何写高质量的JavaScript代码
在现代Web开发中,JavaScript扮演着至关重要的角色。本文介绍了提升JavaScript代码质量的关键技巧:采用语义化命名增强代码可读性;通过模块化设计提升代码的可维护性和复用性;利用恰当的注释与文档说明代码功能;合理管理全局变量避免命名冲突;实施有效的异常处理增加程序稳定性;并借助工具和框架提高开发效率和代码质量。这些实践共同助力打造高效、可维护的Web应用。代码示例和效果参见相关链接。
15 3
|
8天前
|
JavaScript 前端开发 iOS开发
学习强大的JavaScript一行代码,能够节省你的时间和代码量
这段内容介绍了25个实用的JavaScript一行代码技巧,涵盖复制内容到剪贴板、打乱数组、颜色值转换、计算平均值、检查数字奇偶性、数组去重、对象为空检测、字符串反转、日期计算、首字母大写、生成随机字符串、四舍五入、清除Cookie、检测暗黑模式等,帮助开发者提高效率并简化代码。
13 2
|
9天前
|
JSON JavaScript 前端开发
Android调用Vue中的JavaScript代码
Android调用Vue中的JavaScript代码
11 3
|
8天前
|
存储 JavaScript 前端开发
JavaScript代码是怎么拼写的,高中开始
这段代码集合提供了多种实用的 JavaScript 功能,包括获取浏览器 Cookie 值、RGB 转十六进制、复制文本到剪贴板、检查日期有效性、找出一年中的某一天、字符串首字母大写、计算两天之间的天数差、清除所有 Cookie、生成随机十六进制颜色、数组去重、从 URL 获取查询参数、确认奇偶数、求平均值、回到页面顶部、翻转字符串、检查数组是否为空、获取用户选定的文本、打乱数组顺序以及检测用户是否处于暗模式。这些功能可以帮助开发者快速实现常用操作。
38 1
下一篇
DDNS