ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CSS背景从入门到精通:属性、渐变、多重背景与实战

CSS背景从入门到精通:属性、渐变、多重背景与实战 背景样式这东西在我刚学 CSS 那会儿是最容易被跳过的章节——无非就是给元素铺个色、贴张图能有多难真正做项目之后才发现页面里那些“高级感”十有八九都是背景层撑起来的Hero 区的大图压暗、卡片悬浮时的柔光、按钮上的斜纹、加载时的骨架屏、甚至整站的主题色切换全都绕不开 background 这一族属性。这篇就围绕 CSS 从入门到精通系列里的背景样式把八个背景属性、渐变、多重背景、混合模式以及一堆实战场景一次性讲透。不管你是刚把选择器和盒模型看完的新手还是已经写过几个页面但背景总是调不到位的朋友都能从这里拿到能直接抄的代码和踩坑经验。我默认你至少知道 CSS 的三种定义与引用方式行内、内部样式表、外部样式表也大概摸过文本与字体样式剩下的交给这篇就够。1. 背景样式的整体设计与思路拆解1.1 为什么背景层值得单独拎出来讲很多人把背景理解成“装饰”这个认知一旦形成写出来的页面就会有明显的割裂感。实际上浏览器渲染一个元素时背景是最底层的一张画布它决定了内容的对比度、视觉重心和呼吸感。同一个卡片组件背景从纯白换成一层极淡的线性渐变观感立刻从“Excel 表格”变成“设计稿”。更关键的是背景可以用纯 CSS 生成图案不需要切任何图片这意味着零 HTTP 请求、零额外体积、随分辨率无限缩放这正是原子化 CSS 与设计系统都喜欢用渐变和多重背景做视觉基座的原因。从能力维度看背景样式解决了四类问题一是打底色负责区域区分和品牌色承载二是贴素材负责装饰性插画和照片三是造纹理用渐变模拟条纹、网格、噪点四是做遮罩与氛围配合混合模式让图片和文字和平共处。把这四类需求分清楚后面选属性的时候就不会乱。我见过不少同学做背景时习惯性地先切一张 1920 宽的 PNG结果移动端糊、加载慢、改色还得回设计软件这就是没想清楚需求类型的典型后果。1.2 属性清单与 background 简写的排布逻辑背景相关的长属性一共八个先把它们摆出来后面逐个拆长属性作用常用值background-color背景底色颜色值、透明色background-image背景图层url()、渐变函数、nonebackground-repeat平铺方式repeat、no-repeat、repeat-x/ybackground-position图层定位关键词、长度、百分比background-size图层尺寸cover、contain、长度、百分比background-attachment跟随滚动方式scroll、fixed、localbackground-origin定位参照原点border-box、padding-box、content-boxbackground-clip绘制裁剪范围三个 box 值 textbackground简写是把上面这些揉成一行规则是颜色只能出现一次且必须放在最后一层其余属性可以按任意顺序书写但为了可读性业内基本固定成color image repeat position/size attachment origin clip或者更口语化的image position/size repeat attachment这种写法。这里有个新手最容易翻车的地方——position和size之间必须用斜杠隔开写成background: url(a.png) center/cover no-repeat;漏掉斜杠整条声明直接失效浏览器控制台也不会给你任何报错静悄悄地什么都不显示。注意简写background会把所有没写到的背景长属性重置为初始值。所以在组件里如果先写了background-color后面又用background: url(...)覆盖底色会被清成透明。要么统一用简写要么统一用长属性混着写就是在给自己埋雷。1.3 背景的绘制顺序与层叠模型多重背景是这个章节里最容易被忽视但威力最大的部分。当你在background-image里写多个图层时写在前面的图层永远在最上方越往后的越贴近底部而background-color永远在所有图层的最底下。这个顺序和z-index的直觉相反数值大的在上所以很多人第一次写多重背景会搞反把最该露出来的那层写在了最后。理解这个模型之后很多效果就能拆解了。比如“图片上压一层半透明黑”的经典写法.hero { background-image: linear-gradient(rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0.55)), url(./hero.jpg); background-size: cover; background-position: center; }这里的linear-gradient其实是两个颜色相同的半透明色等于一张纯色半透明膜写在最上层照片写在第二层。background-size: cover作用在两层上但纯色膜是无限大的缩放对它没有影响。这种思路可以无限延展——上面铺网格、铺噪点、铺光斑下面放照片全部零图片资源。2. 八大背景属性逐个拆解与实操要点2.1 background-color 与透明度的那些坑background-color支持所有颜色语法包括十六进制、rgb()、hsl()、以及带 alpha 的rgba()、hsla()和八位十六进制如#00000080。日常做遮罩层我更习惯用rgba(0, 0, 0, .5)这种写法因为 alpha 值单独拎出来调整很直观八位十六进制虽然短但改透明度的时候得重数位数容易看花眼。真正需要留意的是transparent这个词。它等价于rgba(0, 0, 0, 0)本身没问题但在渐变插值里会出大问题从red过渡到transparent浏览器会按rgba(255,0,0,1)到rgba(0,0,0,0)插值中间会出现明显的发暗发灰因为红色分量和绿色分量都在往 0 走。想要干净地淡出应该写成rgba(255,0,0,0)也就是同色但 alpha 为零。这个细节在写“文字逐渐淡出”的遮罩渐变时特别关键我当初做新闻列表的底部渐隐就栽在这上面调了半天以为是浏览器差异其实是颜色空间插值的锅。另外父元素设置了background-color子元素默认是透明的这个大家都知道。但如果在父元素上用了background-clip: text之类的技巧子元素的内容反而可能被裁掉这个放到 2.6 节细说。2.2 background-image 的引用方式与路径陷阱url()里的路径一直是新手重灾区尤其是用了“三行模式的 CSS 文件”也就是行内、内联、外链三个阶段演进过来的写法时相对路径的参考点会变。记住一条铁律相对路径永远相对于 CSS 文件本身而不是引用这个 CSS 的 HTML 文件。所以css/style.css里写url(images/bg.png)浏览器找的是css/images/bg.png不是根目录下的images/bg.png。解决办法要么用../images/bg.png往上退一级要么统一从根写/images/bg.png注意这种方式在本地file://协议直开 HTML 时会失效需要起个本地服务。url()的引号也是可有可无的只要路径里没有空格和特殊字符。但我建议永远加上引号一是留出改名的余量二是不少编辑器的语法高亮更准确。还有个小技巧url()可以用 data URI 直接把小图标内联进去图标数量少的时候能省请求代价是 CSS 文件体积变大且无法缓存单独更新所以只适合那种几个月都不动的装饰性小图。2.3 background-repeat 与 background-position 的配合background-repeat的取值里repeat是默认值图片会朝四个方向铺满整个元素。repeat-x和repeat-y只在单方向平铺常用在顶部装饰线和侧边纹理上。space和round这两个值很多人没用过space会在图片之间平均分配空隙保证图片完整不裁切round会拉伸图片让平铺恰好填满。做无缝图案时space能避免图案边缘被切一半的尴尬。background-position可以用关键词left、center、right、top、bottom、长度值和百分比。这里最需要讲清楚的是百分比的计算方式它和大部分人想的不一样偏移量 (容器尺寸 - 图片尺寸) × 百分比也就是说background-position: 50% 50%并不是把图片左边缘挪到容器一半的位置而是让图片的中心点与容器的中心点重合。所以当图片比容器大时0%对齐左/上边缘100%对齐右/下边缘中间的百分比在两端之间插值。理解了这条公式就不会再困惑为什么background-position: center和background-position: 50%效果一样也不会在“想让大图只露右边部分”时写出错误的数值。三个或四个值的语法如right 20px bottom 10px是 CSS 后来的补充用的是“边缘偏移”逻辑意思是“距离右边缘 20px、距离底边缘 10px”这个语义和百分比完全不同别混用。2.4 background-size 的数值计算与选择background-size是背景这块最考验理解深度的属性最常见的三个值要能说清楚差别auto按图片原始尺寸显示超过容器就溢出默认被 repeat 掩盖配no-repeat就会露出裁切感。contain等比缩放让图片完整装进容器可能出现留白。cover等比缩放让图片完全覆盖容器可能超出被裁切。contain和cover的缩放比例是有公式的contain: scale min(容器宽 / 图宽, 容器高 / 图高) cover: scale max(容器宽 / 图宽, 容器高 / 图高)举个例子容器 800×400图片 1000×1000。contain的 scale 是 min(0.8, 0.4)0.4图变成 400×400左右各留 200px 空白cover的 scale 是 max(0.8,0.4)0.8图变成 800×800上下被裁掉各 200px。做全屏 Hero 时选cover几乎是必然的因为留白比裁切难看太多但代价是必须配合background-position: center把主体放在中心否则人物的头可能被裁掉。双值写法background-size: 100% auto也很常见表示宽度撑满、高度按比例自适应适合那种宽度自适应但不想被裁的装饰条。这里有个隐藏坑如果在background简写里写background-size必须放在position后面并用斜杠连接否则属性会被识别成定位的一部分。2.5 attachment、origin、clip 三兄弟background-attachment有三个值。scroll默认意味着背景相对元素固定页面滚动时背景跟着元素一起走。fixed让背景相对视口固定滚动时背景不动产生视差感——这是很多“视差滚动”效果最省事的实现方式。但fixed在移动端尤其是 iOS 上历史遗留问题不少性能和渲染都会打折扣现在越来越多的做法是用多层背景 transform: translateY来实现视差。local则让背景相对元素内容滚动只有元素自己有滚动条时才有意义。background-origin决定背景图的起点参考框默认是padding-box。也就是说图片默认从内边距的外边缘开始铺不会铺到边框下面。如果你希望图片铺满到边框外侧就设成border-box想从内容区开始设成content-box。background-clip决定背景画到哪个边界为止默认border-box。配合background-origin一起用可以做出很实用的效果给元素加一个粗边框然后设background-clip: padding-box背景就不会渗到边框下面半透明边框立刻变得干净不会和背景混色。至于background-clip: text它能做出文字渐变效果配合color: transparent使用我在下面的实战章节里会给出完整代码。2.6 一张表记住常见失效场景现象大概率原因排查动作背景图完全不显示路径错误 / 元素高度为 0打开 F12 看 Network 有没有 404用 outline 看盒子背景色被覆盖没了用了background简写重置检查简写声明里有没有带颜色渐变出现灰带用了transparent插值改成同色 alpha 为 0简写整条失效漏写 position/size 之间的斜杠检查/是否缺失半透明边框发脏背景渗到边框下加background-clip: padding-box移动端背景闪白大图加载慢预加载或换渐变色兜底提示排查背景问题永远先看两件事——元素有没有实际尺寸高度为 0 时背景无从谈起以及 F12 的 Styles 面板里那条 background 声明有没有被斜杠划掉。这两步能解决八成问题。3. 渐变与多重背景把图片资源省下来3.1 线性渐变的角度与色标计算linear-gradient()的第一个参数是方向可以用关键词to right、to bottom left或者角度。角度的规则是0deg指向正上顺时针增加90deg指向正右180deg指向正下270deg指向正左。这和钟表相反也和数学课上的极坐标相反所以第一次接触很容易写反我的习惯是先拿关键词写顺再换成角度微调。色标是渐变的核心。linear-gradient(to right, #f00, #00f)会在两个色标之间均匀插值。加上位置就变成硬边界linear-gradient(to right, #f00 0 50%, #00f 50% 100%)左半红右半蓝中间没有任何过渡。这里的0 50%是双位置语法表示这个颜色从 0 一直持续到 50%等价于写两个色标#f00 0, #f00 50%。这个语法在写条纹图案时能省掉一半代码量。三个以上色标会依次插值。如果给的位置不是递增的浏览器会自动把它修正成前一个色标的位置这个特性可以用来“夹住”某段颜色但可读性差不推荐在团队代码里用。3.2 径向渐变与锥形渐变的适用场景radial-gradient()从中心向外扩散形参结构是形状 尺寸 at 位置。形状可以省略默认椭圆尺寸关键词有closest-side、farthest-side、closest-corner、farthest-corner默认。做光晕、聚光灯、按钮高光最常用的是circle at 50% 0%这种写法也就是从顶部中心向外散开的圆形光。conic-gradient()锥形渐变从正上方12 点方向开始顺时针扫一圈天然适合做圆环进度条、色轮、饼图。做进度环的思路是先铺一层灰底锥形渐变再用第二个锥形渐变覆盖出进度部分最后在中间盖一个纯色圆遮住中心.ring { width: 120px; aspect-ratio: 1; border-radius: 50%; background: conic-gradient(#3b82f6 0 65%, #e5e7eb 65% 100%); position: relative; } .ring::after { content: ; position: absolute; inset: 12px; border-radius: 50%; background: #fff; }纯 CSS 就能跑不需要 SVG也不需要 JS 计算stroke-dasharray。唯一要注意的是进度值变了得改两处65%出现两次实际项目里我会用一个 CSS 变量--p承接写成conic-gradient(#3b82f6 0 var(--p), #e5e7eb var(--p) 100%)JS 只改这一个变量。3.3 多重背景叠加与混合模式前面提过多重背景按书写顺序从上到下堆叠。真正让它变得好玩的是background-blend-mode它控制各背景图层之间如何混合取值有multiply、screen、overlay、soft-light等一整套和图像软件的图层混合模式一一对应。最实用的组合是“照片 品牌色正片叠底”.card-cover { background-image: linear-gradient(#6366f1, #6366f1), url(./cover.jpg); background-size: cover; background-position: center; background-blend-mode: multiply; }multiply会让下方照片的亮部带上品牌色、暗部保持深色整体协调又不会像纯色遮罩那样压死画面。screen则是反过来的提亮效果适合做发光。这里要注意background-blend-mode只在背景图层之间生效不会影响元素上的文字和子元素如果想控制背景和内容的混合得用mix-blend-mode那是另一套逻辑混用时要小心层叠上下文的陷阱。3.4 用渐变手写常用纹理图案这一节全是能直接抄的代码我按图案类型整理。横向细条纹.stripes { background: repeating-linear-gradient( 45deg, #f5f5f5 0 12px, #ebebeb 12px 24px ); }repeating-linear-gradient会自动重复整个色标序列省掉手动算平铺的麻烦。斜条纹配一个缓动足够的循环动画就是经典的“加载进度条”视觉。棋盘格.checker { background-image: linear-gradient(45deg, #ddd 25%, transparent 25% 75%, #ddd 75%), linear-gradient(45deg, #ddd 25%, transparent 25% 75%, #ddd 75%); background-size: 40px 40px; background-position: 0 0, 20px 20px; background-color: #fff; }原理是两层相同的 45 度方块第二层偏移半个格子交错拼出棋盘。透明像素用的是真透明而不是transparent关键字混色安全无灰带。网格线.grid-bg { background-image: linear-gradient(#e2e8f0 1px, transparent 1px), linear-gradient(90deg, #e2e8f0 1px, transparent 1px); background-size: 24px 24px; }两条 1px 的线分别横向纵向配background-size就是一张无限大的设计稿底纹。注意这里的transparent是相邻色标后的插值因为只差 1px肉眼看不出灰带但如果要放在深色底上仍然建议改成rgba(0,0,0,0)的同色写法。4. 高频实战场景从卡片到全屏 Hero4.1 全屏背景与响应式适配全屏 Hero 的标准写法我总结成四句图片走background-image、尺寸cover、定位center、颜色兜底。颜色兜底特别重要因为大图在弱网下加载要几百毫秒这期间如果底色是白的用户会看到一片刺眼的白闪。做法是给同一个元素设一个与图片主色调接近的background-color比如图片是深蓝色的夜景就配#0b1220加载完成前后过渡自然。高度上老办法是height: 100vh但移动端浏览器地址栏收起展开会改变视口高度导致 Hero 区跳动。现在的稳妥写法是用视口单位的新变体.hero { min-height: 100svh; }svh是小视口高度地址栏展开时的最小值lvh是大视口高度dvh是动态高度。三者都支持的现代浏览器里svh做首屏最不容易出 bug因为它在滚动过程中不会变。做兼容时按min-height: 100vh; min-height: 100svh;的顺序写两遍不支持的浏览器会忽略第二条。4.2 文字压在背景图上如何保证可读性这是背景这块最实际的问题。文字和图片打架时我的处理优先级是先加遮罩再调图片定位最后考虑换图。遮罩用前面说的双层渐变方案透明度我一般取 0.4 到 0.6 之间。太浅压不住浅色天空太深照片就废了。如果是渐变方向的遮罩比如从左到右逐渐变暗让左侧文字清晰、右侧照片露出就用linear-gradient(90deg, rgba(0,0,0,.7), rgba(0,0,0,.1))。这种单向遮罩比全屏均匀遮罩更高级因为它保留了一侧的画面信息。第二个手段是给文字本身加轻微描边或阴影但纯阴影在复杂背景上效果有限深色text-shadow: 0 1px 3px rgba(0,0,0,.5)只适合浅景深照片。更可靠的做法是给文字块本身加一个半透明背景容器让文字和图片之间隔一层。至于热词里提到的“字体外描边”-webkit-text-stroke能用但它描的是细边且不支持text-stroke标准写法做粗描边还是得靠text-shadow四向叠加或 SVG。做背景压字时我很少用描边因为描边会让文字变“硬”反而降低质感。4.3 背景切换动画与鼠标交互背景的过渡有个硬限制background-image尤其是url()图片不能参与transition补间两张图片之间切不出平滑动画。想要平滑动效有两条正路。第一条是用background-position做位移。把一张宽图铺在窄容器里鼠标移入时把background-position从left center改成right center配transition: background-position .6s ease就是很顺的“扫图”效果。注意background-position的百分比补间是能做插值的所以这个方案稳。第二条是用伪元素叠两层再改opacity。父元素设背景图 A::before设背景图 B 铺满默认opacity: 0鼠标移入时opacity: 1过渡就很丝滑。这个方案还能顺便做“涟漪光圈扩散”那类效果——用一个径向渐变作为::after的背景从中心向外放大加淡出。.btn { background-image: url(./a.jpg); background-size: cover; position: relative; overflow: hidden; } .btn::before { content: ; position: absolute; inset: 0; background-image: url(./b.jpg); background-size: cover; opacity: 0; transition: opacity .35s ease; } .btn:hover::before { opacity: 1; }至于鼠标移入事件纯 CSS 用:hover就够了注意别用:hover去改会触发重排的属性改背景相关的绘制属性开销最小。4.4 背景相关的性能与加载优化背景图不走img标签所以loadinglazy对它没用浏览器会在 CSS 解析到时就发起请求。首屏的大背景图是绕不开的但可以做两件事一是用image-set()给高分屏提供两倍图避免用一张超大的图硬扛所有设备二是对首屏外的背景图用媒体查询或者 JS 在进入视口后再挂 class 的方式延迟加载。.hero { background-image: image-set( url(./hero.jpg) 1x, url(./hero2x.jpg) 2x ); }image-set()的兼容性现在够用了不支持的老浏览器会忽略整条声明所以记得在它前面留一条普通的url()兜底。另外背景图越少越好——能用渐变换掉的装饰图坚决换掉这是提升加载速度最直接的杠杆。我做过一个落地页把三张纹理图全换成渐变后首屏资源体积少了 180KB。5. 常见问题与排查技巧实录5.1 背景图不显示的排查顺序这个问题我遇到过太多次帮同事看的时候总结出一个固定顺序按这个走基本五分钟内定位。第一步看元素尺寸。给元素临时加outline: 1px solid red或者直接在 F12 里看盒子模型的高亮如果高度是 0背景再正确也看不见。空 div 加背景是新手最经典的翻车点解决办法是给它一个高度或者塞点内容。第二步看 Network。F12 切到 Network 面板刷新筛 Images如果请求是红色的 404那就是路径问题。注意 CSS 里url()的路径参考点是 CSS 文件位置这一点在 2.2 节已经强调过。第三步看声明有没有生效。Styles 面板里那条background如果带删除线说明被优先级更高的规则覆盖了或者是简写语法写错漏斜杠、颜色位置放错。逐条点开看是哪个属性的锅。第四步看层叠。多重背景里写在前面的在上层如果上层是一张不透明的图下层就永远看不见这时候调换顺序即可。5.2 移动端背景固定的坑background-attachment: fixed在桌面端做视差很爽但 iOS 上的表现一直不稳定有的版本会把背景放大、有的会跟着滚动、有的直接在滚动时闪烁。我的建议是移动端直接放弃这个属性。如果设计非要视差用一层position: fixed的容器装背景内容区正常滚动视觉上等价而且稳定得多。桌面端也要注意fixed会让浏览器进入合成层元素一多很吃内存。还有个相关的坑是100vh和 iOS 地址栏的关系前面 4.1 节讲过了这里再补一句如果 Hero 里还有绝对定位的内容用dvh会更好因为内容需要跟着视口变化实时调整svh在地址栏隐藏时会显得矮一截。5.3 渐变断层与打印失效渐变断层通常出现在大尺寸元素和低色深屏幕上从深色过渡到透明时最明显。原因是浏览器在 8 位色深下做插值相邻像素颜色差被量化放大形成一圈圈的“色带”。缓解办法有两个一是给渐变加一点噪点纹理哪怕只有 2% 的透明度也能打散色带二是让渐变两端的颜色差异更小避免跨越很宽的明度范围。打印失效是另一个高频问题。浏览器默认不打印背景色和背景图为的是省墨所以你在屏幕上看到的彩色卡片打印出来是白底。如果确实需要打印背景加print-color-adjust: exact;配合-webkit-前缀的前身但这只是建议值最终仍取决于用户打印设置里的“背景图形”开关别把它当成必然生效的能力。5.4 问题速查表问题现象排查方向解决手段简写整条被划掉语法错误多半是斜杠或颜色位置拆成长属性逐条验证渐变有横向灰带颜色插值空间问题用同色透明替代transparent半透明边框发暗背景渗到边框下background-clip: padding-boxHero 首屏跳一下用了100vh换100svh并保留vh兜底悬浮切换图有闪烁图片未预加载用双伪元素叠加改opacity纹理图拖慢首屏用了图片而非渐变换成repeating-*-gradient打印出来没颜色浏览器默认不打印背景加print-color-adjust: exact并提醒用户开选项多重背景看不到底层层叠顺序写反把想露出的层往后写提示调试背景时最省时间的动作是高频道刷新页面并盯着 Network 面板404 和加载顺序一目了然。别一上来就怀疑浏览器差异十次里有九次是路径和语法。我个人在实际项目里的体会是背景这块真正的分水岭不在于记住了多少属性而在于愿不愿意用“图层”的思维去看它底色是一层照片是一层遮罩是一层纹理又是一层把每个需求落到对应的层上再选对属性去控制这一层代码自然就干净了。还有一个小技巧可以顺手带走——遇到任何需要反复调色的背景先把它抽成一个 CSS 变量放在:root里比如--brand-bg后面无论做主题切换还是深色模式改一处就能全局生效比满文件搜颜色值高效太多。
返回列表