尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
纯前端复刻折叠屏:CSS 3D变换与交互动画实战解析
1. 项目缘起发布会上的折叠屏为什么非要用网页做出来大家都有过这种经历看发布会直播时镜头给到那台折叠屏新机开合之间屏幕带着光效展开铰链阻尼感仿佛隔着屏幕都能听到。弹幕里全是“这也太帅了”但散场之后能真正摸到真机的人少之又少。我当时的想法很简单——既然摸不到那我能不能自己做一个网页让所有人都能在浏览器里“把玩”这台折叠屏于是就有了这个项目用纯前端技术在网页里复刻发布会级别的折叠屏演示动画。项目核心就一件事把发布会上那几秒钟的折叠开合变成一个可交互、可拖拽、能在手机和电脑上直接打开的网页版体验。不是视频不是建模软件渲染而是用HTML、CSS和JavaScript搭建的、可运行在任意现代浏览器里的三维折叠效果。做完之后发给朋友对方在微信里点开就能玩不需要装任何App这就是“网页版”的价值所在——零成本触达打开即用。这个项目适合谁如果你是前端开发者想知道transform-style: preserve-3d到底怎么用才能做出真实的折叠效果这篇内容会把从零到一的过程全部拆开如果你只是对折叠屏感兴趣的非技术读者也可以跳过我讲代码细节的部分直接看思路和效果如果你恰好两者都是那这篇就是为你准备的。项目最终效果是一台由CSS绘制的折叠屏手机悬浮在页面中央你可以用鼠标拖拽它左右旋转滚动滚轮或滑动屏幕控制它从完全贴合到完全展开屏幕上的壁纸内容会跟着折叠角度实时变化边框高光、折痕阴影、铰链细节一应俱全。很多人问我为什么不用SketchFab或者Blender导出交互模型非要自己用网页搓一个答案是Model Viewer那类方案虽然能加载三维模型但发布会风格的动效讲究的是“叙事感”——镜头推近、屏幕点亮、机身翻开这些都需要对每一个动画帧有绝对控制权。而用前端技术实现等于拿到了一张空白画布所有动画节奏、交互手感、渲染细节都能按自己的想法来而且发布就是一个链接传播成本极低。更关键的是这件事并没有想象中难。折叠屏直观感受复杂拆开看就是一个三维平面围绕一条轴旋转正好是CSS 3D变换的看家本领。2. 折叠屏网页的核心原理把“折纸”翻译成CSS矩阵2.1 三维空间里的折叠本质一次旋转加一次位移先说结论折叠屏在网页里呈现的本质是让两个半屏分别围绕一条公共边做旋转同时让它们的父容器具备三维空间感。听起来很简单但实际操作时很多人第一步就栽了——屏幕不是像门一样平着转而是绕着自身边缘的Y轴旋转且左右两半的旋转方向相反。为了直观一点可以想象一张纸对折纸上有一条中线左半边沿着中线向左下翻转右半边沿着中线向右下翻转纸就能合上。在CSS里这条中线对应的DOM元素就是“铰链容器”左右半屏是这个容器的两个子元素。关键代码是这样.phone-hinge { position: relative; transform-style: preserve-3d; } .phone-left, .phone-right { position: absolute; top: 0; height: 100%; transform-origin: right center; /* 左半屏绕右边缘转 */ transform: rotateY(var(--left-angle)); } .phone-right { left: 50%; transform-origin: left center; /* 右半屏绕左边缘转 */ transform: rotateY(var(--right-angle)); }这里最容易被忽略的是transform-origin。默认的旋转中心是元素正中心如果忘了改成边缘你会看到两个半屏像风车一样原地打转永远合不上。左半屏的旋转轴必须设置在其右边缘右半屏的旋转轴必须设置在其左边缘这样两半共享同一条铰链轴看起来才像真的折叠。说到旋转角度的关系如果左半屏从0度转到-90度右半屏从0度转到90度两个半屏会向内合拢反过来左半屏90度、右半屏-90度则是向外展开。为了防止背面穿帮需要配合backface-visibility: hidden。但这个属性是个双刃剑后面性能部分我会展开讲。2.2 preserve-3d与perspective没有这两个属性一切旋转都是纸片很多人写完上面的代码发现页面毫无立体感——两半屏幕确实转了但看起来像平面里的梯形变化。原因是你缺了最重要的两个概念perspective透视距离和transform-style: preserve-3d保留3D空间。perspective决定了观察者离物体多远数值越小透视效果越强烈。比如perspective: 1000px意味着观察者站在距离物体1000像素的位置。折叠屏开合时离观察者近的一边会显得大远的一边会显得小这种近大远小就是靠透视营造的。我把透视设在容器上而不是单个元素上.scene { perspective: 1200px; perspective-origin: 50% 30%; }perspective-origin控制视点的位置默认是正中心。如果想让屏幕打开时有一种“从斜上方俯视”的发布会镜头感把视点上移就对了。transform-style: preserve-3d是另一块基石。它的作用是告诉浏览器子元素的3D变换不要被压平到父元素的平面上而是保持在同一个三维场景中。如果不加浏览器默认flat子元素旋转后只会渲染成压扁的投影自然看不到厚度和前后关系。2.3 铰链坐标计算的细节为什么半屏间隙总是对不齐做折叠屏网页时最容易遇到的一个问题是合上状态两个半屏之间有一条缝怎么调都消不掉。这不是CSS写错了而是坐标计算不够精确。假设整个手机宽度为W左半屏宽度为W / 2。如果左半屏和右半屏都正好占父容器的一半宽度那么左右两个半屏的边缘会紧贴在一起但3D渲染时每个子元素还会有默认的边框和背景裁切加上相邻元素之间可能出现的亚像素渲染误差缝隙随之而来。我的解法是让两个半屏宽度各占calc(50% 1px)并把左半屏向右延伸1像素作为重叠区.phone-left { width: calc(50% 1px); right: 50%; } .phone-right { width: calc(50% 1px); left: 50%; }这样两半在平面状态下会有一个1像素的重叠肉眼不可见但在旋转合拢时重叠区域正好作为铰链的物理缓冲避免缝隙露出背景色。别小看这1像素发布会演示最怕的就是这种细节穿帮。另一个容易出问题的点是半屏的厚度。真实折叠屏合上后有厚度如果你只做两个平面合拢时它们会像两张纸一样完全贴合缺少真机那种圆润的铰链包边。后来我在铰链位置加了一个独立的弧形容器用border-radius和渐变模拟转轴并且让它在折叠角度变化时稍微缩放模拟铰链被压缩的视觉感受。这个后续章节会详细说。3. 在网页里重建发布会名场面从静态产品图到动态开合3.1 搭建手机模型屏幕、边框、铰链的DOM骨架做发布会风格第一眼很重要。我要的不是一个抽象的几何体而是一台看起来像“会出现在宣传片里”的手机。所以DOM结构不能省得有层次感。我的结构大致分四层场景层.scene负责透视和整体旋转。手机外壳层.phone包含机身圆角、边框阴影、整体悬浮动画。铰链层.hinge左右半屏的父容器也是折叠角度的控制中心。屏显层.screen-left/.screen-right用来显示壁纸、系统UI、反光等。屏幕本身也是多层的。为了后期加光效屏幕内部至少要有背景层、内容层、玻璃反光层。背景层放壁纸或渐变内容层放一个仿系统界面的时间锁屏反光层用来做动态高光。这样后续折叠时可以让内容层做对应的透视变换产生屏幕弯曲的视觉错觉。边框和机身用的是CSS模拟而非图片。好处是可以自由控制颜色和反光且保持在任何分辨率下都锐利。比如钛合金边框我是用多个渐变叠加实现的.phone-frame { border-radius: 36px; background: linear-gradient(145deg, #4a4a4a, #1a1a1a 40%, #3d3d3d); box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4), inset 0 -2px 4px rgba(255, 255, 255, 0.1), inset 0 2px 4px rgba(0, 0, 0, 0.6); }外阴影负责悬浮感内阴影负责边框厚度高光渐变则模拟金属拉丝。3.2 折痕与光影逼真度的关键细节发布会视频里折叠屏最迷人的瞬间不是完全展开而是半开状态——屏幕中间会出现一道柔和的折痕光线在折痕处骤然转折。CSS里复现这个效果我用的是叠加在屏幕上的一个渐变遮罩层。折痕层位于两个半屏之上是一个独立的元素通过clip-path只显示中间一带区域。它的背景是从亮到暗再到亮的渐变模拟凹陷产生的光影.crease { position: absolute; top: 0; bottom: 0; left: calc(50% - 30px); width: 60px; background: linear-gradient( to right, transparent, rgba(0, 0, 0, 0.25) 30%, rgba(0, 0, 0, 0.45) 50%, rgba(0, 0, 0, 0.25) 70%, transparent ); filter: blur(2px); opacity: calc(var(--fold-angle) / 180); transform: translateZ(1px); }opacity绑定折角变量合拢时折痕最深完全摊平时几乎看不见符合现实观感。不过折痕只是静态光影真正的临场感来自动态高光。当手机翻转时发布会视频里屏幕反光会像流水一样扫过表面。我用了一个倾斜的白色渐变条叠加在屏幕上透明度只在一个角度范围内出现.gloss { background: linear-gradient( 115deg, transparent 20%, rgba(255, 255, 255, 0.35) 40%, transparent 60% ); transform: rotateY(var(--device-rotate)) translateX(var(--gloss-shift)); }当--device-rotate改变时高光位置同步变化整个屏幕就像在转台上被追光灯扫过一样。3.3 让屏幕内容跟随折叠同步变化如果只是外壳折叠、屏幕里的内容纹丝不动那就穿帮了。“折叠屏”的核心是屏幕内容必须跟着折角走。这里的做法是给左右两个屏幕的内容层分别施加与外壳一半角度对应的变换模拟屏幕弯曲后内容被卷起来的效果。具体来说当左半屏旋转-90度时它的内容层并不需要跟着转否则字都是倒的。正确做法是内容层只做非常小的偏移和缩放幅度控制在看起来材质被拉伸的范围内。我用JS每帧计算const maxAngle 90; const leftAngle Math.min(angle, maxAngle); const rightAngle Math.max(-angle, -maxAngle); leftContent.style.transform perspective(800px) rotateY(${leftAngle * 0.05}deg) scale(${1 Math.sin(leftAngle * Math.PI / 180) * 0.02}) ; rightContent.style.transform perspective(800px) rotateY(${rightAngle * 0.05}deg) scale(${1 Math.sin(rightAngle * Math.PI / 180) * 0.02}) ;这组数值很小但能让内容产生一种“吸附在屏幕上、随屏幕微曲”的视觉线索。失真度控制在5%以内看起来就很自然。千万不要让内容旋转大于15度否则字会糊成一片像贴纸起皱。4. 交互与节奏像发布会一样“讲故事”4.1 滚动驱动 vs 时间驱动哪种更贴近发布会体验动手之前我先想清楚了一件事用户来这个网页他的操作预期是什么两种方案一种是滚动驱动页面往下滚手机就跟着打开另一种是时间驱动手机自动开合用户只能旋转视角。滚动驱动的优点是天然贴合“阅读演示页”的预期和苹果官网的产品页如出一辙时间驱动则更像看视频用户是被动的。最终我的选择是两者混合页面加载后先播放一段自动开合的“开场动画”把发布会那种节奏感做出来动画结束后进入自由交互模式用户可以滚动控制开合角度、拖拽旋转视角。这两个状态之间的切换要平滑不能在动画播放到一半时突然被用户抢走控制权。实现滚动驱动的方式是监听滚动容器的scroll事件将scrollY映射到0到180的角度区间。这里要注意一个细节不要直接对scroll事件里操作大量DOM要用requestAnimationFrame做节流或者直接使用ScrollTimeline。考虑到浏览器的兼容性我用的是requestAnimationFramelet targetProgress 0; let currentProgress 0; function updateProgress() { targetProgress Math.min(1, window.scrollY / (document.body.scrollHeight - window.innerHeight)); } function animate() { currentProgress (targetProgress - currentProgress) * 0.12; const angle currentProgress * 180; setFoldAngle(angle); requestAnimationFrame(animate); } window.addEventListener(scroll, updateProgress); animate();缓动系数0.12让角度变化自带惯性不会因为滚轮快速跳动而显得生硬这个手感是调了好几轮才找到的。4.2 关键帧设计与缓动函数选择做“高级感”不能只靠默认ease发布会动效的“高级感”很大程度来自缓动。如果全部用ease或linear会显得廉价。真实机械结构的开合有一个共同点启动时有一个微小阻力越接近闭合或完全展开时速度越快中间过程反而有一种均匀的进给感。用缓动函数表示类似cubic-bezier(0.65, 0, 0.35, 1)接近“先慢后快再慢”。开场动画我设计了三段式第一段手机从屏幕中央缓缓浮现透明度从0到1持续400ms。第二段机身绕Y轴旋转从-20度转到0度营造被镜头逐渐对准的错觉。第三段屏幕折叠角从180度开始闭合同时一个高光条扫过屏幕整个过程约1200ms。每一段的缓动都不一样。浮现有cubic-bezier(0.22, 1, 0.36, 1)旋转是cubic-bezier(0.4, 0, 0.2, 1)折叠开合则用更久的cubic-bezier(0.16, 1, 0.3, 1)。三段通过animation-delay串联每个动画结束后用CSS自定义属性通知JS进行状态切换。你可能觉得这些微小的差别无关紧要但把同一套动画分别用默认ease和手工调过的缓动放给朋友盲测几乎所有人都能第一时间指出哪个是“发布会质感”。这就是细节的复利。4.3 手势拖拽控制折叠角度鼠标和触摸的归一化处理除了滚动控制我还想让用户能直接“抓住”手机掰开。实现逻辑不复杂在手机区域监听pointerdown、pointermove、pointerup把鼠标/手指的X轴位移映射为折叠角度增量。这里有个兼容性陷阱如果用touchstart/touchmove就得同时处理mousedown很容易发生拖拽冲突。最省事的方式是用Pointer Events统一处理一套代码覆盖桌面和移动端const phoneElement document.querySelector(.phone-container); let isDragging false; let startX 0; let startAngle 0; phoneElement.addEventListener(pointerdown, (e) { isDragging true; startX e.clientX; startAngle currentFoldAngle; phoneElement.setPointerCapture(e.pointerId); }); phoneElement.addEventListener(pointermove, (e) { if (!isDragging) return; const delta (e.clientX - startX) * 0.8; const newAngle Math.max(0, Math.min(180, startAngle delta)); setFoldAngle(newAngle); });角度范围限制在0到180之间正好对应完全闭合和完全展开。乘以0.8是让拖拽的位移和角度的比例不要过灵敏否则手指稍微一动屏幕就猛地翻开。使用setPointerCapture确保手指移出元素后依然能继续接收事件这是移动端拖动流畅的关键。5. 性能与兼容性的那些坑实机测试后的优化记录5.1 为什么iPhone上掉帧背面子层重绘问题代码写完第一版时我在Mac的Chrome上跑得很流畅结果拿到iPhone上一点开转动视角时掉帧明显屏幕高光闪烁。这一度让我怀疑是不是CSS 3D性能在移动端不行。后来排查发现问题出在backface-visibility: hidden的滥用。我为了避免背面穿帮给所有屏幕层和内容层都加了backface-visibility: hidden。本意是好的但在iOS Safari里这个属性会把元素强制分配为一个独立的合成层而且当元素从背面翻转到正面时整个层需要重新栅格化。折叠过程中两半屏幕一直在翻转等于每帧都有两个图层在反复重绘不掉帧才怪。优化方式很直接只对最外层机身添加backface-visibility: hidden内部的壁纸、内容层、高光层不再单独设置。内部元素本来就不会暴露背面因为它们的父容器已经做了遮挡。改动之后iPhone上的帧率恢复稳定几乎不掉帧。5.2 折叠屏设备上的真实尺寸适配真机上的“物理正确”这个网页有一个很有意思的验证场景在真实折叠屏手机上打开它。我有幸借到一台折叠屏设备做测试结果发现了许多在普通手机上发现不了的问题。最明显的是角度映射错误。在普通手机上CSS像素宽度一般小于折叠屏展开后的宽度同样的折叠角度在折叠屏上看到的视觉幅度要小很多。为了让折叠屏设备上也有发布会视频里的冲击感需要做一个动态的透视缩放当设备宽度超过900像素时整体手机模型等比放大1.3倍同时perspective从1200px拉近到900px。这一拉近折叠屏的立体感立刻出来了。尺寸适配的另一个细节是安全区域。如果网页在折叠屏的内屏上打开浏览器可能会用上下挖孔的全面屏模式机身模型不能被刘海或挖孔遮挡。我在布局时给.scene加了一个padding: max(20px, env(safe-area-inset-top))确保礼物场景再怎么旋转都在安全范围内。5.3 GPU合成层与will-change的使用边界提到前端动画性能一定有人说“加上will-change: transform就快了”。这个说法只说对了一半。过度使用will-change会导致浏览器为每个元素创建独立的合成层占用大量GPU内存在移动端反而会造成灾难性的闪烁或崩溃。我在项目里做了个测试给左右半屏、铰链、折痕、高光、内部内容共7个元素都加上will-change: transform后桌面Chrome的表现和原来差不多移动端却出现了明显的闪烁和点按延迟。逐一删除后发现真正需要独立图层的是左右两个半屏以及最高层的折痕遮罩。这三个元素每帧都在变化其余元素位置稳定交给浏览器自动优化即可。最终保留的三处.phone-left, .phone-right { will-change: transform; } .crease { will-change: transform, opacity; }这样既保证了关键动画的流畅又不至于把GPU内存烧光。如果你在低端安卓机上测试后发现卡顿第一个排查方向就是will-change是不是加多了。6. 项目完成后的延伸同样的思路还能做哪些演示6.1 从折叠屏到翻页书、卡片翻转一套逻辑多处复用折叠屏项目的核心是“两个元素各绕公共轴反向旋转”这个逻辑一旦想通几乎所有翻折类效果都是同一个套路。我顺手用它做了两个衍生演示。一个是翻页书把每一页设为半屏宽书脊处就是公转轴鼠标拖拽时前一页绕书脊向左旋转后一页保持静止。除了旋转轴位置不同核心代码几乎不用改。另一个是银行卡翻转效果卡片正面和背面两个元素正面绕Y轴从0转到-180度背面初始就是180度配合backface-visibility: hidden就能实现真实的双面翻转。所以这套项目的价值不只在折叠屏本身它更像一个“3D翻转引擎”。以后再做产品展示、电子画册、卡片交互都能直接复用这套铰链思路。6.2 用AI网页版辅助编码从调试动画参数到生成DOM骨架说实话这个项目从零开始写一半的工作量是在磨动画参数。光是一个缓动曲线我就来回改了不下十次。后来我试着把问题描述给AI网页版工具不是让你复制现成代码而是把思路讲给它让它从“对象存储、结构组织”的角度提供建议。比如我想给折痕加一个“跟随折叠角度动态模糊”的效果手写了好几个版本都感觉过渡生硬。我把视觉目标描述给AI工具它建议我用filter: blur()配合CSS变量在折叠角度变化时同步更新模糊值。虽然这个方案后来我微调过数值但框架确实省了很多探索时间。另外DOM骨架的重复结构AI工具也能一次性生成。比如左右两块屏幕里各有壁纸层、内容层、反光层手写会有大量重复标签我用AI网页版生成一个模板再手动调整类名。这个工作流程适合所有前端动画项目不必从零手敲每一个标签。6.3 后续改动方向从演示到可配置工具做完这个项目之后我脑子里冒出的一个想法是是不是可以把它做成一个“发布会折叠屏模板”工具让不懂代码的人也能随时换壁纸、改配色、调折叠速度目前手机壁纸是内置的一张图如果把壁纸改成用户上传再开放一个控制角度的滑动条就能变成一个轻量的互动展示页。技术上没有太大障碍无非是加一个input typefile读取图片替换掉背景层的background-image再加一个input typerange绑定setFoldAngle。但对用户来说眼见着自己的图片出现在一台可交互折叠屏上那种“立刻能用”的满足感和只看演示是完全不同的。这也回答了最开始的问题为什么非要在网页里做折叠屏因为网页能把原本只存在于发布会视频里的东西变成一个谁都能上手玩一下、还能带进自己作品里的工具。
RELATED

相关推荐

基于LMD与能量熵的轴承故障诊断MATLAB实现

基于LMD与能量熵的轴承故障诊断MATLAB实现

1. 项目背景与核心价值在工业设备状态监测领域,滚动轴承作为旋转机械的核心部件,其健康状态直接影响整机运行安全。传统振动信号分析方法在面对早期微弱故障时往往力不从心,这正是我们开发这套基于LMD(Local Mean Decomposition&a…

📅 2026/9/20 16:05:36
江恩波动率副图指标源码解析:通达信ATR归一化实战

江恩波动率副图指标源码解析:通达信ATR归一化实战

简介:通达信江恩波动率副图指标源码,以doc文档形式封装,适合股票技术分析爱好者、通达信指标编写者用于理解江恩理论在副图上的量化表达。文档内含七层波动率计算公式、红波白波EMA均线、MA5动态阈值、吸筹信号、前高突破与粉嫩次数统计等完整…

📅 2026/9/20 16:05:36
MATLAB实现结构光三维重建:三频四步相移法全解析

MATLAB实现结构光三维重建:三频四步相移法全解析

前阵子有个研究生来问我,MATLAB做结构光三维重建到底该从哪儿入手。很多新手一上来就翻论文,三频四步相移法、多频外差、包裹相位展开这些术语看得头大,真正能跑的代码却拼不出一套。其实这套方法远没有想象中那么神秘:投影仪往被…

📅 2026/9/20 16:00:36
MORE NEWS

更多资讯

📰

如何组建AI数学研究团队?Superhuman团队的组织经验启示

如何组建AI数学研究团队?Superhuman团队的组织经验启示 【免费下载链接】superhuman 项目地址: https://gitcode.com/GitHub_Trending/sup/superhuman Superhuman 是 Google DeepMind 超人类推理团队(由 Thang Luong 领衔)开源的 AI …

📰

激光原理习题解答:谐振腔与高斯光束计算难点全解析

简介:《激光原理与激光技术》(北工大)习题解答PDF,面向学习激光原理的光学、物理及光电子专业学生,系统梳理激光相干性、单色性参数、光子特性与粒子跃迁等核心概念。资源共1个PDF文件,大小895KB&#xff0…

📰

弹射弹珠 gotoxy 全屏重绘闪屏?TaoToken 配进 Codex config.toml

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

给 Claude Code 配 TaoToken,读透 irqreturn_t 的中断返回

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

PyTorch深度学习实战:从环境搭建到模型训练全攻略

简介:《Python 深度学习基于 PyTorch》配套代码与笔记资源包,面向深度学习初学者及 PyTorch 使用者,帮助读者结合书本理解神经网络原理并开展框架实践。压缩包共 31 个文件,大小约 38.42MB,包括 14 个 Python 脚本、8 …

📰

SWE-Bench Pro 80.3% 的 Fable 5 接进编程工具当主力模型,Base URL 填 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬