简介:一款基于CSS3的卷角特效源码,适合Web前端开发者和网页设计爱好者,用于博客卡片、档案展示、电子相册等场景,帮助快速营造纸张卷边的立体纸质质感。压缩包内共5个文件,包含1个HTML演示页面和4张图片素材(2张png、2张jpg),整体仅763KB,轻量易用,可直接在支持CSS3的火狐或Chrome浏览器中打开预览。已有272人学习浏览,适合希望研究纯CSS3实现卷边效果的中初级开发者。通过查看演示页面与对应样式,可观察边角卷起的结构设计与实现技巧,理解如何借助CSS3特性模拟真实纸张的弯曲形态;在此基础上可自由调整卷曲角度、阴影或材质纹理,拓展到更多页面装饰。随包图片素材补足了视觉细节,让效果更完整,是一份简明实用的前端特效参考。
1. 纯CSS3做维基百科档案纸张卷角,先搞清楚它在解决什么问题
想把一张普通卡片做出维基百科档案页那种米黄底、右下角微微翘起的纸张卷角效果,很多人的第一反应是去素材站拖一张卷角PNG。贴图确实能出效果,但真放进项目里就会发现麻烦:底色一变、圆角一改、深色主题一开,贴图就要重新抠,更别提多个页面要保持同一套光影。纯CSS3模拟维基百科档案纸张卷角效果,是用两个伪元素加几层linear-gradient,把纸面、折痕、纸背反光这三层信号画出来,不需要JavaScript,也不用引图片资源。这个方案的落点很明确:文档站、古籍专题页、博客引文块、证照卡片这类想把“旧档案感”做进组件里的场景。新手照着下面的代码可以直接跑通,熟手则可以重点看参数边界和后半部分的踩坑清单。
2. 从旧档案纸的质感反推:卷角效果到底要画哪几层
2.1 眼睛收到的第一信号不是三角形,而是明暗断层
真正盯着一张旧档案纸的卷角看,人眼最先注意到的并不是那个三角形缺口,而是三处明暗变化:纸张正面和翻起纸背之间存在色温差,折痕线是一条锐利的深色细线,纸角下方还有一个被按压出的沉影。这三样缺了任何一样,视觉都会立刻告诉你“这是贴上去的图”或者“这是个切角”。
以右下角卷起为例,纸面是暖黄米色,翻起来的纸背因为受光角度不同,会显得更亮、更干净一些,折痕处则因为纸纤维挤压而变暗。这个明暗断层是“卷起来了”的核心信号,而不是那块被切掉的面积。理解了这一点,再去看网上很多CSS卷角教程,就能明白为什么有些效果一眼假:它们只用border画了一个纯色三角形,既没有折痕线,也没有卷筒下方的按压阴影,三角形和纸面之间没有任何过渡,看起来就像把角落贴了一块补丁。
所以后面落地时,我一般不会只写一个伪元素,而是把三件事分开考虑:纸面由卡片背景负责,纸背和折痕线由伪元素的渐变负责,按压阴影由背景层或inset box-shadow负责。三样都到位,卷角才有说服力。
2.2 为什么linear-gradient加伪元素刚好能覆盖这三层
CSS3的linear-gradient看起来只是个颜色渐变工具,但它有一个对卷角来说至关重要的特性:颜色节点可以做成硬边界。用transparent 50%, #fbf6ea 50%这种写法,渐变会在方块对角线位置瞬间切换颜色,形成锐利的折痕边,而不是一个模糊的过渡带。也就是说,一个正方形伪元素配合一层对角线渐变,就能精确切出“纸背三角形”。
伪元素方面,::before和::after正好提供两个叠加绘图层,但实际写的时候会发现一层往往就够了:把折痕线和纸背放进同一个渐变里,用透明区占据折痕另一侧,父元素的背景色自然成为纸面。这样一来,一个伪元素加一层渐变就同时完成了“切角、纸背、折痕线”三件事,另一个伪元素则可以用来做额外的高光或投影,不会遇到层数不够的窘境。
选择纯CSS3而不是图片方案,理由是透明的。图片方案每换一种底色就要重新导一张图,CSS方案则把颜色全部抽成变量,换个主题只是换几个色值。响应式上,图片在2倍屏下需要额外出@2x资源,CSS方案用到的只是像素值和百分比,放大缩小不会糊。如果以后还想给卷角加hover动效,CSS方案也只需要transform,图片方案则要处理额外DOM和动画库依赖。
2.3 三种常见实现路线的对比,为什么多数场景选CSS3
| 实现方式 | 资源体积 | 改底色/换方向 | 响应式与打印 | 视觉上限 |
|---|---|---|---|---|
| 卷角PNG贴图 | 每张几十到几百KB,多方向要多张 | 难,基本要重新作图 | 高分屏要出@2x,打印正常 | 可达照片级 |
| Canvas/SVG手绘卷角 | 引入一个库或几十行逻辑 | 改起来比图片方便 | 尺寸计算麻烦,字体缩放后要重绘 | 可做复杂光影 |
| 纯CSS3伪元素+渐变 | 零资源,几行样式 | 一个CSS变量搞定 | 天然矢量,设定打印色后正常 | 近似级,足够装饰用途 |
从这个表能看出,图片方案赢在视觉上限,CSS方案赢在维护成本和自由度。我所经历的绝大多数“档案纸张卷角”需求都是装饰性的:它服务于界面的氛围,而不是需要精确复现物理光影的产品展示页。装饰性需求选CSS3,性价比是最高的。
3. 可照抄的纸张卷角实现:最小结构与四方向参数对照
3.1 最小HTML:一张档案卡片的骨架
先搭一个最小可用的页面结构。卡片不需要特殊容器,一个普通的article或div就够,重点是把文字内容留出足够的padding,尤其是靠近卷角的那一侧,否则文字会顶到纸背区域影响阅读。
<article class="archive-card"> <h2>馆藏档案 · 1943</h2> <p>这份手稿记录了战时的民间书信往来,纸张边缘已泛黄,右下角因反复翻阅被卷起。</p> <footer>编号:023 / P.R.</footer> </article>这个结构的用意是让卷角效果只依赖一个类名,挂到任何块级元素上都能生效。标题和footer是占位内容,实际使用中换成文档标题或站点信息即可。需要注意,卡片里的文字不要一路排到右下角,因为卷角伪元素会占用右下约--curl-size见方的区域,padding留足后观感才自然。
3.2 核心CSS:一个伪元素同时画纸背和折痕线
下面的CSS是右下角卷角的主实现,代码量很小但每行都有实际作用。
.archive-card { /* 卷角边长,改这个值控制卷角大小 */ --curl-size: 28px; /* 纸张正面颜色 */ --paper: #f4ecd8; /* 折起后露出的纸背颜色,比纸面亮一档 */ --paper-back: #fbf6ea; /* 折痕线颜色,用半透明棕灰更自然 */ --curl-line: rgba(90, 64, 32, 0.5); position: relative; padding: 30px 34px 52px 34px; border-radius: 4px; background: var(--paper); box-shadow: inset -6px -6px 10px -6px rgba(80, 60, 30, 0.25); } .archive-card::after { content: ""; position: absolute; right: 0; bottom: 0; width: var(--curl-size); height: var(--curl-size); background: linear-gradient( to top left, transparent calc(50% - 2px), var(--curl-line) calc(50% - 2px) 50%, var(--paper-back) 50% ); }这段代码的逻辑拆开看是这样的:伪元素是贴在卡片右下角的一个正方形,正方形被一条对角线分成两个三角形。渐变方向to top left表示颜色从右下角向左上角推进,calc(50% - 2px)之前是透明,透明区对应折痕线的右下一侧,也就是被翻走后的空白处;从calc(50% - 2px)到50%这一段填折痕线颜色;从50%开始填纸背颜色。这样实现出来的纸背三角形位于左上角,折痕线则刚好卡在纸背边缘内侧2px,不会像独立渐变层那样在透明区域也画出一条多余的线。
参数方面,重点调四个变量:--curl-size控制卷角大小,一般24px到40px之间比较常见,超过40px会明显挤压内容区;--paper-back比--paper亮12%左右最自然,亮太多像发光贴片,暗一档反而像污渍;--curl-line不要用纯黑,半透明棕灰贴合纸张色温;inset阴影的偏移量和模糊半径共同决定按压纵深的强弱,数值建议保持在8px以内,过大会让整张卡片边缘都发黑。
3.3 四个方向的类:只改定位和渐变方向就能复用
卷角不一定总在右下,文献卡片或引文块的卷角位置经常要跟版面对齐。四个方向的实现方式完全一致,只是伪元素定位和渐变方向不同。
/* 右下角卷角 */ .archive-card--br::after { right: 0; bottom: 0; background: linear-gradient( to top left, transparent calc(50% - 2px), var(--curl-line) calc(50% - 2px) 50%, var(--paper-back) 50% ); } /* 右上角卷角 */ .archive-card--tr::after { right: 0; top: 0; background: linear-gradient( to bottom left, transparent calc(50% - 2px), var(--curl-line) calc(50% - 2px) 50%, var(--paper-back) 50% ); } /* 左下角卷角 */ .archive-card--bl::after { left: 0; bottom: 0; background: linear-gradient( to top right, transparent calc(50% - 2px), var(--curl-line) calc(50% - 2px) 50%, var(--paper-back) 50% ); } /* 左上角卷角 */ .archive-card--tl::after { left: 0; top: 0; background: linear-gradient( to bottom right, transparent calc(50% - 2px), var(--curl-line) calc(50% - 2px) 50%, var(--paper-back) 50% ); }记忆规律很直接:纸背永远出现在卷角位置的对面。右下角卷起,纸背落在左上;右上角卷起,纸背落在左下。渐变方向这样对应后,四个方向的表现是统一的。如果你在项目里临时改方向改乱了,最快的恢复办法是把--paper-back临时替换成red,观察红色三角落在哪个方向,再对照上面的类名修正。
3.4 增强纵深的两个可选层:inset阴影与角部径向渐变
主实现已经能骗过大部分人的眼睛,但如果想让卷角更有立体感,可以在父元素上加一层角部径向渐变。做法是在background上叠加一个以右下角为圆心的色晕,模拟纸张被折起时产生的按压暗部。
.archive-card--br { background: radial-gradient(circle at bottom right, rgba(80, 60, 30, 0.15), transparent 60%) no-repeat, var(--paper); }这里circle at bottom right把暗部锚定在卷角一侧,transparent 60%保证色晕只影响角落周围,不会污染整张卡片。如果你用了--paper-back纸背色,径向渐变位于background底层,纸背会叠在它上面,形成“按压处发暗、纸背发亮”的层次。
方向随卷角位置同步切换即可:右上角用circle at top right,左下角用circle at top left,以此类推。需要提醒的是,inset阴影和径向渐变都属于增强层,没有它们卷角效果也成立,有它们则光影更完整。不要两者都拉到很高数值,否则右下角会黑成一片,反而丢失纸色对比。
4. 把卷角组件装进真实页面:选型、圆角、层级与降级
4.1 什么时候用纯CSS卷角,什么时候别用
我的判断标准是看这个卷角是“内容”还是“装饰”。如果卡片里的纸张效果是产品核心卖点,用户会放大、会截图、会希望它像一本真的旧书扫描件,那老老实实用高清实拍图或者Canvas精细绘制,纯CSS3的物理模拟还做不到那个精度。反过来,如果卷角只是给文档列表、引文块、证照占位卡增加一点档案氛围,那CSS方案就是最优解。
还有一条分界线是复用频率。站点里如果有十几种卡片都需要卷角,但尺寸和底色各不相同,图片方案要维护一整套不同尺寸的切图,CSS方案只需要把--curl-size和--paper换成对应变量。我在实际项目里采用的是给组件基类挂变量,再让业务类覆盖变量,而不是在每张卡片里重写一遍伪元素,这样后续有人要换颜色,不需要懂伪元素原理也能改。
4.2 圆角与overflow的隐藏矛盾,以及处理顺序
这是卷角落地时最容易在验收阶段暴露的问题。卡片为了美观通常会设border-radius: 8px,有些布局还会用overflow: hidden去裁掉内部多余的元素。一旦加上overflow: hidden,伪元素如果被定位在容器边界外,或者投影使用外部box-shadow,就会整个被裁掉,卷角直接消失。
处理顺序我一般是这样:先确认伪元素完全位于容器内部,通过right: 0; bottom: 0配合width/height让三角形留在卡片轮廓内;然后把阴影改成inset阴影,因为inset阴影不会受overflow裁剪。用这套组合,即使卡片需要border-radius: 8px和overflow: hidden,卷角依然完整可见。如果项目里还希望卷角下方露出一小截“纸边”,那就得放弃overflow hidden,改用其他手段裁内容,比如给内部内容区单独做圆角。
4.3 文字避让与层叠顺序
伪元素默认参与文档流内的层叠排序,这意味着当正文和伪元素出现位置重叠时,谁压谁取决于DOM顺序和各自的定位属性。很多初写卷角的人把伪元素的z-index设为-1,结果发现整块纸背被卡片的背景色吞掉,或者被正文压在下面看不到折痕线;另一些人则什么都不设,结果伪元素盖住了标题文字。
稳妥的做法是给伪元素一个明确的z-index: 0,同时给内容区设置相对定位和z-index: 1。这样内容永远在卷角上方,伪元素永远在背景和内容之间,不会出现一半字在纸背里、一半字在纸面外的诡异断层。padding侧同样要配合,右下角方向至少留出--curl-size加上12px的呼吸空间,避免最后一个字符贴近折痕线。
.archive-card__content { position: relative; z-index: 1; } .archive-card::after { z-index: 0; }这段样式不看也能跑,但一旦页面里出现弹窗、吸顶栏这类高z-index元素,层叠上下文就可能被改写。提前把层级关系固定下来,以后排查问题会省很多时间。
4.4 降级策略:老浏览器至少保底可读
CSS3渐变和伪元素在现代浏览器里覆盖度已经很高,但仍要面对极少数老旧环境。我的处理方式是让卷角成为增强层而非必需层:即使伪元素完全不显示,卡片本身还有纸张底色、内容和内边距,读起来依旧完整。
具体做法是把伪元素包在@supports (background: linear-gradient(to top left, #000 0, #000 0))条件里。老浏览器不认识这个条件,会跳过整个规则,只留下干净的卡片;现代浏览器则正常绘制卷角。这个写法只增加几行代码,却能在兼容性评审时省下一堆口舌。另外别忘了伪元素的content: ""本身就是空内容,它对读屏软件是安全的,不会把装饰性元素读成文本。
5. 常见问题与避坑清单:五个最容易翻车的现场
5.1 渐变方向算反,纸背贴在纸面上像块补丁
现象:运行代码后发现折痕线还在,但纸背三角形落在卡片外侧或错误的一边,看起来像角落贴了块不发光的小牌子。
原因:CSS渐变的方向关键词和视觉直觉常常相反。to top left并不是“往左上角倾斜”,而是渐变轴从右下角指向左上角,整个色彩推进方向是反直觉的。开发时凭感觉写方向,十个里有五个会翻车。
解决:写完后先临时把--paper-back改成大红色,看红色三角形落点对不对。红色显示在卷角的对角侧,说明方向正确;否则把to top left改成to bottom right再试。改到正确后再恢复纸色。这个方法比直接记四个方向要可靠得多,因为它验证的是视觉结果而不是语法记忆。
5.2 父级overflow: hidden把卷角吃掉了
现象:卡片一加border-radius和overflow: hidden,卷角伪元素就只剩半个,或者完全消失,纸背像被撕掉一块。
原因:很多卷角教程把投影或纸背三角形放在容器边界外侧,一旦overflow隐藏生效,露在边界外的部分全被裁剪。如果伪元素中有外部box-shadow,shadow也会被同一规则裁掉。
解决:改用我在第3章写的主方案,伪元素完全放在容器内部,阴影用inset box-shadow而不是普通box-shadow。这样overflow: hidden对卷角几乎无影响。如果你确实需要外部投影,只能去掉overflow hidden,同时用border-radius加在内部元素上。
5.3 纸背与纸面色差拉不开,深色卡片上又灰成一片
现象:浅色卡上卷角几乎看不见,只有细看才发现有一条线;深色卡上纸背发灰,像一块没擦干净的旧污迹。
原因:纸背色和纸面色的亮度差只有几个百分点时,人眼分辨不出这个“翻折”动作;但在深色背景下,纸背色的固有色相又显得过于突兀,缺少暖色融进环境。
解决:浅色场景下,纸背比纸面亮10%到14%之间最合适;深色场景则要把纸背的明度压回纸面附近,同时提高饱和度。比如纸面用#3a3228时,纸背用#4a3f32再加一点暖黄倾向,折痕线则用半透明白而不是半透明黑,这样深色卡上才能看出“纸张受光”的感觉。
| 场景 | 纸面颜色 | 纸背颜色 | 折痕线 |
|---|---|---|---|
| 浅色档案卡 | #f4ecd8 | #fbf6ea | rgba(90,64,32,.5) |
| 深色档案卡 | #3a3228 | #4a3f32 | rgba(255,240,210,.35) |
5.4 伪元素盖住正文或正文盖住折痕
现象:卡片右下角的文字被一块纸背色挡掉半边;或者反过来,折痕线从文字中间穿过去,视觉上非常脏。
原因:伪元素和正文都没有专门处理z-index,层叠结果取决于它们在文档树里的顺序和定位方式。不同浏览器解析同一段代码可能有细微差异,只在这台机器调好不够稳。
解决:按第4章的规则,给正文容器加position: relative; z-index: 1,给伪元素加z-index: 0。这一步不是可选优化,而是把层级关系锁死。另外检查一下正文的padding-bottom和padding-right是否大于--curl-size,如果内容区太挤,即使z-index正确,字也会顶在折痕线旁边,观感紧绷。
5.5 打印和导出PDF时纸张色与折痕一起消失
现象:页面在浏览器里卷角完整,打印预览时纸质底色没了,折痕线和纸背三角形也变成空白。
原因:多数浏览器默认不打印背景颜色和背景渐变,所有依赖background的效果在打印时都会失效。伪元素里的渐变属于背景资源,不会被带到PDF里。
解决:给卡片加-webkit-print-color-adjust: exact; print-color-adjust: exact;,同时不要依赖background之外的透明叠加做视觉区分。如果这套样式在打印样式表里还要进一步简化,可以在@media print中把卷角类隐藏,保留纸张底色和文字即可,因为打印场景下用户更关心可读性而不是装饰细节。
6. 进阶:把卷角做成一盒CSS变量组件,并验证像素级表现
6.1 把颜色和尺寸抽到根变量,一个主题一处改
与其每个使用位置都写一遍变量,不如把卷角相关的变量挂到组件根上,业务类只负责覆盖。比如全站浅色主题下一套默认值,深色主题下只需覆盖三五个变量,卷角自动跟随主题切换。
:root { --curl-size: 28px; --paper: #f4ecd8; --paper-back: #fbf6ea; --curl-line: rgba(90, 64, 32, 0.5); } .archive-card { background: var(--paper); } .archive-card::after { width: var(--curl-size); height: var(--curl-size); background: linear-gradient( to top left, transparent calc(50% - 2px), var(--curl-line) calc(50% - 2px) 50%, var(--paper-back) 50% ); }换主题时只动:root里的色值,卷角不会出现某一处改了渐变但另一处还是旧色的问题。这个组件化的收益在使用超过三个页面后体现得特别明显。
6.2 给卷角加一个hover微动效
想让卷角在鼠标悬停时轻微放大,不要试图transition background渐变,因为渐变本身不可过渡。正确做法是给伪元素加transform,并根据卷角方向设置对应的transform-origin。
.archive-card--br:hover::after { transform: scale(1.15); transform-origin: bottom right; } .archive-card--tr:hover::after { transform: scale(1.15); transform-origin: top right; }动画时长控制在0.2s左右,配合transition: transform 0.2s ease-out。这个交互能明显提升档案卡片的精致感,但要留意hover状态如果被用在触屏设备上,:hover可能没有对应效果,不要把它做成唯一的信息入口。
6.3 验证卷角像素级表现的习惯动作
我每次调整完卷角方向或折痕线宽度,都要做三件事:第一,用浏览器开发者工具把卡片放大到200%,检查折痕线是否有锯齿毛边,如果发虚就把渐变里的2px改成1.5px或3px试;第二,做一个四方角对比页,同一张卡片四角分别放四个方向的卷角,用截图脚本一次性导出,肉眼对比明暗是否一致;第三,把--curl-size临时改成40px,确认纸背和折痕线仍然对齐,再改回目标值。
这个先放大再缩小的调试习惯帮我避免过很多次“小尺寸下看着对、放大后漏出破绽”的情况。如果你也做组件库,值得把这套验证动作沉淀成团队的检查清单。希望帮到你。
本文还有配套的精品资源,点击获取