欢迎关注我的公众号:前端侦探
聊聊图片与背景图片
一、img vs background-image
大部分注重内容的图片(比如商品展示、文章配图)都推荐直接使用img
标签,好处有很多,比如
img
支持天然懒加载,设置loading="lazy"
html
<img src='xxx.png' loading="lazy">
img
支持各种JS
监听,比如加载成功、加载失败
html
<img src='xxx.png' onload="" onerror="">
img
支持自定义解码,同步还是异步
html
<img src='xxx.png' decoding="sync">
img
对SEO更加优化,对可访问性更好
html
<img src='xxx.png' alt="头像">
但是,除了上面这些,在视觉表现上,图片灵活性就不如背景图片了。
比如img
不支持重复平铺、不支持图片叠加等等,也不像普通标签还可以使用伪元素。
这样就导致很多时候,比如要在img
外面嵌套一层容器再额外处理,还是有些不便的,特别是在HTML
不方便修改的情况下。
那么,有没有什么办法,可以将img
转换成background-image
呢? 也就是使用img
标签,但是却可以使用背景图的诸多特性。

二、img 的层级
img
是一个可替换标签,意思就是这个标签的渲染内容是由外部决定的。
这样就导致img
和一般标签有些不同,比如没有伪元素,而且资源的层级是高于一些装饰性属性的,例如背景、内阴影等

比如,我们给一个img
标签添加一个背景
html
<img src="xxx.png" style="background:red">
效果如下

没有看到任何红色背景,说明图片的层级是高于背景的。
如果我们换一个带透明像素的图片,就能看到背景了

所以,下面的问题就是,如何把img
本身的图片给隐藏起来?
三、如何隐藏 img 资源?
当然这里说的隐藏并不是直接隐藏整个img
标签,比如
css
img {
opacity: 0 /*🙅🏻*/
}
这样的话,整个标签都看不见了,背景也看不见了,也就失去了转换的意义。
那还有什么办法可以隐藏呢?这里有两种方式
1. 通过content
替换内容
在之前,可以从可替换标签入手,通过content
属性可以改变可替换元素的内容,就像这样
css
img {
content: url(xx.png)
}
我们给img
标签一个透明的图片内容,就可以替换img
原本的显示了,这里可以用1*1
像素的透明gif
css
img {
content: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
}
但是,这样设置以后,图片完全不可见了。这样因为,此时的img
已经被渲染成1*1
像素了

所以在用这种方式时,必须手动指定宽高,
css
img {
content: url(xxx.png);
width: 300px;
height: 300px;
}
为了方便观察,我们加上边框

这样原始的img
链接已经被隐藏了
2. 通过object-position
偏移
上面的方式比较硬核,还可以设置一张错误的图片,让图片展示直接出错,这样还能使用伪元素。
不过,有个小缺陷是,必须要手动指定图片宽高,可能存在一定的限制。
下面介绍另一个方式,那就是通过object-positon
收到改变图片的显示位置。比如
css
img {
object-position: 100px;
}
效果如下

我们只需要给个足够大的值,图片就完全移出标签外了
css
img {
object-position: 100vw; /**足够大的偏移/
}
这样也能隐藏图片的展示,而且也不影响img
原本尺寸

注意,这里不能用object-position: 100%
来实现,和背景位置比较像,100%
表示居右了,并不是向右偏移自身的100%
。
四、如何通过背景图片显示
再回到img
标签本身,有办法直接通过src
属性直接显示为背景图片吗?
html
<img src="xxx.png">
在之前这篇文章:原子化的未来?了解一下全面进化的CSS attr函数 有提到attr
的新特性。
但是,出于安全考虑,并不能直接显示背景图片
css
img{
background: url(attr(src));
}
其实呢,还可以用image-set
来直接渲染字符串格式的资源,写法如下
css
img {
background: image-set(attr(src));
}
这个技巧是在张鑫旭的这篇文章中学到的:www.zhangxinxu.com/wordpress/2...
效果如下

背景尺寸有些不对,我们调整一下
css
img {
background: image-set(attr(src)) 0 0/100%;
}
这样就通过背景完美替换了图片

不过这个需要用过attr
的新特性,要求兼容性 chrome 133+
更常见的做法是通过自定义属性来实现
html
<img src="xxx.png" style="--bg: url(xxx.png)">
然后直接使用这个变量
css
img {
background: var(--bg) 0 0/100%;
}
五、转换成背景图片后的好处
费了一番功夫转换成了背景图片,有哪些好处呢?下面举几个例子
1. 图片内边框
通常我们使用border
实现的边框都是外边框,无法直接覆盖在图片上,有时候需要一种半透明边框来强化图片的轮廓,比如下面的书封

这时,我们可以通过内阴影来实现这样的效果(内阴影的层级高于背景)
css
img {
background: image-set(attr(src)) 0 0/100%;
border-radius: 8px;
box-shadow: inset 0 0 0 2px rgba(0,0,0,.1)
}
效果如下

2. 图片高光或者水印
有时候书封还需要高光或者水印,这样会更有质感一些,就像这样
这时,我们可以直接给图片叠加一层高光素材就行了
css
img {
background: url("https://imgservices-1252317822.image.myqcloud.com/coco/s03272025/e21047d7.v7q5ko.png") 0 0/100% 100%, image-set(attr(src)) 0 0/100%;
}
效果如下

也可以叠加一层平铺的水印
css
img{
--water: url("data:image/svg+xml,%3Csvg width='150' height='150' style='transform:rotate(-45deg)' xmlns='http://www.w3.org/2000/svg'%3E%3Ctext x='50%25' y='50%25' font-size='14' fill='%23a2a9b6' font-family='system-ui, sans-serif' text-anchor='middle' dominant-baseline='middle'%3E前端侦探%3C/text%3E%3C/svg%3E");
background: var(--water) 50%/28%, image-set(attr(src)) 0 0/100%;
box-shadow: inset 0 0 0 4px rebeccapurple;
}
效果如下

3. 图片缩放效果
可以在不嵌套标签的情况下实现图片缩放效果
css
img{
transition: .3s;
}
img:hover{
background-size: 120%;
}
效果如下

以上所有 demo 可以查看:codepen.io/xboxyan/pen...
六、更多一种选择
其实上面的实现都可以通过嵌套一层标签来实现,这里其实针对的是那种 html
结构不方便修改的情况,比如是框架自己生成的,或者在一些富文本编辑器了,嵌套一层会有更多的麻烦。
这样一个自定义img
标签小技巧,你学到了吗?最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发 ❤❤❤