☰
【每天一个CSS | Day09】翻过来是背面的3D个人名片
2026/10/1 10:17:59 网站建设 项目流程

写在前面

昨天的磁吸导航我们玩的是“整排联动”——鼠标动一下,五颗按钮一起回应。

今天换一种交互:一张卡片,绕自己的纵轴翻过来。

  • 正面:品牌色名片,名字、职位、logo、扫光
  • 背面:联系方式列表 + 一枚手写体签名

鼠标一碰,名片“啪”地翻180°;再移开,翻回来。

全程只有三个CSS属性在干活:preserve-3d+backface-visibility+rotateY(180deg)。

这三个属性是所有3D翻转效果的地基——今天的名片、明天的卡片、后天的翻牌游戏,套路完全一样。学会这一篇,你以后做任何“翻面”效果都不用再查资料。


一、先看效果


深色房间里悬浮着一张墨绿名片,轻轻上下浮动——静置时也有生命感。

鼠标一碰,名片绕纵轴翻到背面:

  • 正面:深绿品牌色 + 左上角“LX” 圆角logo + 名字 + 职位 + 右下角一枚PURE CSS小胶囊
  • 翻面过程:正面还会扫过一道斜向高光,像塑封卡在灯下转了一下
  • 背面:深灰底 + 列着MAIL / SITE / GITHUB三行 + 右下角一枚斜置手写体签名“以CSS为笔”

整张名片宽340px、高205px,标准银行卡比例。

怎么跑起来:

  1. 新建一个demo.html
  2. 把下面第二节的完整代码粘进去
  3. 双击用浏览器打开

📸截图党提示:翻转过程是0.8s,比“啪”一下更有质感。建议录3秒GIF抓翻转全程;或者截两张(正面 / 背面)拼图做文章头图,视觉对比更强。


二、完整代码(复制即用)

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Day 09 · 3D 翻转个人名片</title><style>*{margin:0;padding:0;box-sizing:border-box;}html, body{height:100%;}body{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:44px;overflow:hidden;background:#0f1216;font-family:"PingFang SC",sans-serif;}.scene{perspective:1200px;animation:bob 5s ease-in-out infinite alternate;}@keyframesbob{from{transform:translateY(0);}to{transform:translateY(-8px);}}.card{position:relative;width:340px;height:205px;cursor:pointer;transform-style:preserve-3d;transition:transform .8scubic-bezier(.2,.7,.2,1);}.scene:hover .card{transform:rotateY(180deg);}.face{position:absolute;inset:0;border-radius:16px;overflow:hidden;backface-visibility:hidden;-webkit-backface-visibility:hidden;box-shadow:0 24px 60pxrgba(0,0,0,.5);}/* —— 正面 —— */.front{padding:26px 28px;color:#f2faf7;background:radial-gradient(ellipse at 85% 15%,rgba(255,255,255,.16),transparent 45%),linear-gradient(135deg,#0d8574,#0a5c50 70%,#08443c);}.front .mark{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;background:rgba(255,255,255,.14);border:1px solidrgba(255,255,255,.25);font:600 16px/1"SF Mono",Consolas,monospace;}.front h1{margin-top:26px;font-size:24px;font-weight:600;letter-spacing:.06em;}.front p{margin-top:6px;font:400 11px/1"SF Mono",Consolas,monospace;letter-spacing:.24em;color:rgba(242,250,247,.72);}.front .chip{position:absolute;right:26px;bottom:24px;font:400 9px/1"SF Mono",Consolas,monospace;letter-spacing:.2em;color:rgba(242,250,247,.5);border:1px solidrgba(255,255,255,.25);padding:5px 9px;border-radius:99px;}.front .shine{position:absolute;inset:0;background:linear-gradient(105deg,transparent 40%,rgba(255,255,255,.14)50%,transparent 60%);transform:translateX(-120%);transition:transform .9s ease;}.scene:hover .front .shine{transform:translateX(120%);}/* —— 背面 —— */.back{transform:rotateY(180deg);padding:24px 28px;background:#161b22;color:#dbe4ee;}.back h2{font:400 10px/1"SF Mono",Consolas,monospace;letter-spacing:.3em;color:#6b7a8c;}.back ul{list-style:none;margin-top:16px;display:grid;gap:10px;}.back li{display:flex;align-items:center;gap:10px;font:400 12.5px/1"SF Mono",Consolas,monospace;}.back li b{color:#5cc9b4;font-weight:500;width:56px;}.back .sig{position:absolute;right:26px;bottom:18px;font-family:"Songti SC","STSong","Noto Serif SC",serif;font-size:15px;color:#8fa0b3;transform:rotate(-4deg);}.back::before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(90deg,rgba(255,255,255,.02)0 2px,transparent 2px 6px);}.hint{font:400 10px/1"SF Mono",Consolas,monospace;letter-spacing:.3em;color:#39434f;}.tag{position:fixed;right:18px;bottom:14px;font:400 10px/1"SF Mono",Consolas,monospace;letter-spacing:.28em;color:rgba(219,228,238,.25);}</style></head><body><divclass="scene"><divclass="card"><divclass="face front"><divclass="mark">LX</div><h1>LIN X.</h1><p>FRONT-END ENGINEER</p><divclass="chip">PURE CSS</div><divclass="shine"></div></div><divclass="face back"><h2>CONTACT</h2><ul><li><b>MAIL</b>hi@example.dev</li><li><b>SITE</b>example.dev</li><li><b>GITHUB</b>github.com/example</li></ul><divclass="sig">以 CSS 为笔</div></div></div></div><divclass="hint">HOVER TO FLIP · 180° IN 0.8S</div><divclass="tag">每天一个惊艳 CSS · DAY 09</div></body></html>

三、三个核心技法拆解

技法1:翻转三件套——三个属性缺一不可

整个效果是三个属性的固定搭配:

/* ① 容器给透视:让旋转有“景深”,不是平面剪切 */.scene{perspective:1200px;}/* ② 卡片本体保留 3D:让两个面共处一个三维空间 */.card{transform-style:preserve-3d;}/* ③ 旋转:hover 时把卡片绕纵轴转 180° */.scene:hover .card{transform:rotateY(180deg);}

逐条理解:

属性作用少了会怎样
perspective提供“观察者视角”,近大远小翻转看起来像平面剪切,不是立体
preserve-3d让子元素留在同一个3D空间两个面被拍平,背面内容直接“贴”在正面
rotateY(180deg)真正执行旋转不转,没效果

关键结构:两个面都是绝对定位铺满卡片:

.face{position:absolute;inset:0;border-radius:16px;}

inset: 0就是top: 0; right: 0; bottom: 0; left: 0的简写——两个面尺寸完全一致,只是各自transform不同。

背面要预先翻好:

.back{transform:rotateY(180deg);}

这一步至关重要。正面不动,背面先翻180°——这样当卡片整体转到180° 时,背面正好朝正前方,是正的而不是镜像的。

如果忘了这一步,翻过去看到的会是左右颠倒的联系方式——像照镜子。


技法2:backface-visibility是「两面牌」的灵魂

上面说了两个面都铺满卡片,尺寸完全一致。那问题来了:

正面翻过去之后,它和背面不就叠在一起了吗?

答案就是backface-visibility: hidden:

.face{backface-visibility:hidden;-webkit-backface-visibility:hidden;/* Safari 前缀 */}

这个属性的作用:当一个元素的“背面”朝向观察者时,整个元素隐身。

视觉效果:

  • 卡片在0°:正面朝前(可见),背面朝后(隐身)→ 你看到正面
  • 卡片在180°:背面朝前(可见),正面朝后(隐身)→ 你看到背面
  • 翻转过程中:两面交替隐身,永远只有一面在画面里

这就是“两面牌”的原理——同一时刻只显示一面。

打个比方:backface-visibility: hidden就像给每个面装了一扇单向玻璃。看得到这一面,看不到背面;翻到背面,又能看到那一面。

为什么两个面都要写?

这是个容易漏的点:

/* ❌ 只写在背面上 */.back{backface-visibility:hidden;}/* 正面翻到背面时,会显示镜像的正面 → 两层叠成“鬼影” */

正面也要写backface-visibility: hidden。否则正面翻到背向观众时不会隐身,会和背面叠在一起——画面上会出现“半透明叠影”,非常难看。

记住一句话:凡是参与翻转的面,都写backface-visibility: hidden。


技法3:扫光与浮动——翻面之外的氛围件

光有“翻面”还不够,容易显得“干”。

两个氛围件让质感上一档:

① 正面扫光——模拟塑封卡反光

.front .shine{position:absolute;inset:0;background:linear-gradient(105deg,transparent 40%,rgba(255,255,255,.14)50%,transparent 60%);transform:translateX(-120%);transition:transform .9s ease;}.scene:hover .front .shine{transform:translateX(120%);}

思路:

  • linear-gradient(105deg, ...)→ 一条斜向的白色亮带(105°是“从左上斜向右下”)
  • 亮带两侧透明,中间10%是白色 → 只有中间一段有光
  • 平时停在画面左侧外面(translateX(-120%))
  • hover 时移到画面右侧外面(translateX(120%))
  • 0.9s的过渡 → 光带横穿整个正面

时机要稍慢于翻转:翻转0.8s、扫光0.9s——光比翻转“迟到一步”,观感上就像卡片翻动时“顺带”反射了一下。

② 整卡浮动——静置时的生命感

.scene{animation:bob 5s ease-in-out infinite alternate;}@keyframesbob{from{transform:translateY(0);}to{transform:translateY(-8px);}}

整张卡片以5秒周期上下浮动±8px。

为什么时长要5s,而不是和翻转同期的0.8s?

因为两个动画要“拉开量级”:

  • 翻转(0.8s)是事件性动画——鼠标碰到才触发,要快、要有反馈
  • 浮动(5s)是氛围性动画——一直在那,要慢、要静

如果浮动也做0.8s,你会看到一个疯狂抖动的卡片,鼠标还没碰它就在“抽搐”,非常廉价。

通用心法:*氛围动画的时长 ≥ 事件动画的5倍。*让观众在无操作时“感觉它在呼吸”,有操作时“感觉它在回应”——两种动画的气质完全不同。


四、踩坑位(我全踩过,你直接避)

①backface-visibility只写在背面上

/* ❌ 正面翻到背面时不隐身,和背面叠成“鬼影” */.back{backface-visibility:hidden;}/* ✅ 两个面都要写 */.front, .back{backface-visibility:hidden;}

正面翻过去之后,如果不隐身,会和背面叠在一起——画面上会出现半透明叠影,两面内容糊成一团。

**这是翻转卡最常见的翻车点。**记住:凡是参与翻转的面,都要写backface-visibility: hidden。

②preserve-3d忘在卡片本体

/* ❌ 忘了 preserve-3d:两个面被拍扁到同一平面 */.card{transform-style:flat;}/* ✅ 卡片本体保留 3D */.card{transform-style:preserve-3d;}

不加preserve-3d,浏览器会把所有子元素的3D变换降维到父元素的平面上。翻过去看到的是背面内容“贴”在正面——完全没有立体感,像换了一张图。

Day03的等距城市也踩过这个坑——所有3D父子嵌套的场景,都要写preserve-3d。

③ 祖先设了overflow: hidden

这一条老读者应该有印象——Day03的3D城市就踩过。

/* ❌ 祖先一旦不是 visible,3D静默失效 */.ancestor{overflow:hidden;}/* ✅ 给3D场景的整条祖先链放行 */.ancestor{overflow:visible;}

overflow一旦不是visible,浏览器会强制把该元素及子孙的preserve-3d按flat处理——所有3D变换静默失效。

这是最隐蔽的一个坑:代码看着都对,就是没效果,怎么查都查不出来。排查3D失效时,第一反应就是检查祖先链的overflow。

④hover触发在移动端无效

触屏没有hover概念。手机上点击后,hover状态可能“卡”在激活状态,也可能完全不触发——各浏览器行为不一。

两个解决方案:

  • 方案A(推荐):用隐藏的checkbox接管——见下面“变式挑战”
  • 方案B(偷懒):让卡片默认每6秒自动翻一次,不用交互

⑤ 两面内容高度不齐

两个面都是position: absolute; inset: 0——尺寸完全一致,内容多的一侧会溢出。

正确做法:先定卡片尺寸(本例340 × 205),再往里放内容。如果内容天然放不下,就得先改卡片尺寸,不要指望元素“自己撑开”——绝对定位的元素不会撑开父容器。

⑥ 翻面过程中文字“糊掉”或“折痕”明显

如果卡片很宽(比如800px以上),翻转到90°附近时,文字会变成一条极细的线(因为完全侧向观众)——这是正常现象,不用管。

但如果文字在翻转过程中出现明显折痕、或部分行闪烁,多半是backface-visibility的兼容问题,试试加-webkit-backface-visibility: hidden(Safari / 老版本浏览器)。


五、变式挑战(10–30分钟,动手才有收获)

任务:给背面加二维码位 + 支持触屏翻转。

① 加一个假的二维码位(10分钟)

右下角留一块56×56的方框,用两层repeating-linear-gradient画个假二维码纹理:

.back .qr{position:absolute;right:24px;top:24px;width:56px;height:56px;border-radius:6px;background:repeating-linear-gradient(90deg,#dbe4ee 0 4px,transparent 4px 8px),repeating-linear-gradient(0deg,#dbe4ee 0 4px,transparent 4px 8px);opacity:.35;}

效果:一块网格纹理,远看像二维码,近看是CSS画的——完全零图片。

② 支持触屏翻转(checkbox hack)

在.scene前面放一个隐藏checkbox,把整张名片做成它的label:

<inputtype="checkbox"id="flip"hidden><labelclass="scene"for="flip"><divclass="card">...</div></label>
/* 用checkbox状态替代hover状态 */#flip:checked ~ .scene .card, .scene:hover .card{transform:rotateY(180deg);}/* 触屏上点击时,扫光也跟随触发 */#flip:checked ~ .scene .front .shine, .scene:hover .front .shine{transform:translateX(120%);}

这样就两全其美:

  • 桌面端:鼠标hover翻转(第一次体验)
  • 移动端:手指点击翻转(checkbox被勾选 / 取消)

注意:把#flip放在.scene前面——~是向后兄弟选择器,状态必须在视图前面。这是Day04、Day06一路强调的纯CSS交互骨架。

③ 进阶挑战(选做)

  • 加一个翻牌音效(可选半开戒3行JS):card.addEventListener('mouseenter', () => new Audio('flip.mp3').play())——但这就破坏“零JS”人设了,慎用
  • 做一套“翻牌游戏”:三张牌、六个面(配Day04的radio状态机),点击某张翻转,露出背后的图案
  • 名片背面加一个“渐变光圈”:radial-gradient做一层随翻转缓缓扩大的光晕,让背面的“出场”更有仪式感

六、小结

今天这一篇里藏着5个通用技能点:

技能适用场景
perspective+preserve-3d+rotateY翻转三件套所有3D翻转效果
backface-visibility: hidden两面牌原理翻牌、卡牌、翻书、任何正反两面切换
::before或独立元素做“扫光”塑封反光、卡片质感、按钮高光
氛围动画时长 ≥ 事件动画5倍所有“静置 + 交互”双动画场景
纯CSS交互骨架(input前置 +~选择器)所有零JS状态切换

最值得记住的两条:

① 翻转三件套(perspective+preserve-3d+rotateY)是所有3D翻转的地基。

三者分工明确:

  • perspective→ 让“世界”有景深
  • preserve-3d→ 让“空间”别被拍扁
  • rotateY→ 让“元素”动起来

少了任何一个,翻转效果都垮。

②backface-visibility: hidden是“两面牌”的灵魂。

它让每个面在转向背面时自动隐身,同一时刻永远只有一面在画面里。任何两面切换的效果——翻牌、翻书、翻日历——都靠这一个属性。


如果这一篇对你有帮助,三连支持一下这个专栏 🙏
点赞 = 更新加速,收藏 = 随时复制代码,评论 = 你想看什么效果,我排进日程。

本专栏所有 demo,复制即用,随便商用。
目标更新:不断更。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询