你是不是也遇到过这样一个问题:一个很简单的半透明需求,给容器加了 opacity: .5,结果里面的文字、子元素全部跟着变淡了。想给图片做淡入淡出,结果整段文字齐刷刷透过去,背景透出来的同时字也看不清。最头疼的是,明明在浏览器里看着没问题,放到某个老项目里弹窗定位突然跑偏了。
CSS 图像透明/不透明处理,涉及的不只是 opacity 这一个属性。从颜色模型里的 alpha 通道,到图片格式自带的透明通道,再到 filter、mix-blend-mode、mask 这些能把透明度玩出花的属性,每个方案背后都有完全不同的原理和适用场景。这篇文章就围绕图像透明/不透明这个主题,把原理、实操和坑一次性讲透。不管你是刚接触 CSS 的新手,还是要做复杂动效和遮罩层的老手,这篇都能直接用上。
1. opacity 基础:一个值、三种状态,以及绕不开的层叠陷阱
1.1 opacity 的取值、写法和一个容易忽略的事实
opacity 的取值范围是 0 到 1,0 表示完全透明,1 表示完全不透明,也可以填 0.5、0.86 这种任意小数。语法本身很简单,难的是理解它的底层行为。这个属性会让元素整体在同一个渲染层里“一次性”做透明度合成,不只是背景变了,文本、边框、子元素的背景,全都跟着一起合成为一张图,再整体调整透明度。
css复制img {
opacity: 0.5;
}
.btn {
opacity: 0.8;
transition: opacity 0.2s ease;
}
注意,opacity 的值不是取整的,浏览器会按浮点数做计算。也就是说 opacity: calc(1 - var(--progress)) 这种写法完全合法,这在做滚动消失、入场动画时非常有用。很多原子化 CSS 框架里也能看到 .o-0、.o-50、.o-100 这样的工具类,底层就是一句 opacity: 0.5。
提示:
opacity是会被子元素继承的吗?准确说是“表现上继承”。你给父容器写opacity: .5,子元素再怎么写opacity: 1也救不回来。因为父容器已经把整棵子树合并成一个透明图层了,子元素只能在自己的图层里叠加透明度,根本没法反向恢复到不透明。
1.2 子元素无法反向“增亮”:用错位置的典型案例
我见过不少新人这样写:希望图片半透明,又希望图片上的标题文字保持清晰,于是这样写:
html复制<div class="card">
<img src="cover.jpg" alt="">
<p class="title">卡片标题</p>
</div>
css复制.card {
opacity: 0.5;
}
.card .title {
opacity: 1; /* 试图救回来 */
}
跑一下就会发现,文字依然半透明。原因是 opacity: 0.5 作用于整个 .card 元素,浏览器会把卡片连同内部文字、图片合成到一层,再整体调整透明度。子元素设置 opacity: 1 相当于在已经是一张半透明图片的身份之上再套一层透明度为 1 的透明图层,原图本身已经半透明了,自然救不回来。
正确做法,是把透明度只施加到图片那一层,而不是整个容器:
css复制.card .cover {
opacity: 0.5;
}
.card .title {
opacity: 1;
position: relative;
z-index: 1;
}
这句话几乎可以当成考试重点记下来:opacity 永远施加在元素自身与内部内容合并后的整体上,想要某个局部透明,就把样式放到那个局部上。
1.3 opacity 与层叠上下文:fixed 弹窗为什么突然错位
opacity 小于 1 还有一个副作用,它会触发层叠上下文(stacking context)的创建。层叠上下文一出现,里面 position: fixed 的子元素就不再相对浏览器视口定位了,而是相对这个被 opacity 改变的父元素定位。
曾经做个后台系统的登录弹窗,弹窗本身是 position: fixed,想通过 opacity: 0 隐藏、点击后变为 1,过渡动画也正常。可是在某个页面上,弹窗打开后位置偏偏往右下偏了几百像素,排查了很久,最后发现是这个页面外层容器加了一个透明的 opacity: 0.99,想用来做 GPU 加速。结果整个上下文变化,fixed 弹窗的包含块从视口变成了这个外层容器。
这个坑在轮播图、悬浮层、全局 Loading 里尤其常见。记住了,以后看到“页面有个 fixed 元素被一个半透明祖先包住,定位突然错乱”的问题,优先级最高地检查这个祖先上有没有 opacity、transform、filter、will-change 这类属性。
css复制.wrap {
opacity: 0.99; /* 为了“性能优化”,结果搞崩了 fixed 定位 */
}
1.4 display:none、visibility:hidden、opacity:0 怎么选
做透明隐藏效果时,这三个东西经常被拿来做对比。它们看起来都能让元素“消失”,但行为差别很大:
| 方案 | 是否占位 | 能否触发事件 | 是否支持过渡动画 | 辅助功能表现 |
|---|---|---|---|---|
