CSS 小技巧:如何将 img 转换成 background-image

欢迎关注我的公众号:前端侦探

聊聊图片与背景图片

一、img vs background-image

大部分注重内容的图片(比如商品展示、文章配图)都推荐直接使用img标签,好处有很多,比如

  1. img支持天然懒加载,设置loading="lazy"
html 复制代码
<img src='xxx.png' loading="lazy">
  1. img支持各种JS监听,比如加载成功、加载失败
html 复制代码
<img src='xxx.png' onload="" onerror="">
  1. img支持自定义解码,同步还是异步
html 复制代码
<img src='xxx.png' decoding="sync">
  1. 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标签小技巧,你学到了吗?最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发 ❤❤❤

相关推荐
大气层煮月亮2 分钟前
Oracle EBS ERP之报表开发—嵌入Web中的报表预览、报表打印
前端·数据库·oracle
excel6 分钟前
Vue 中 v-show 与 v-if 的全面解析
前端
回忆哆啦没有A梦3 小时前
Vue页面回退刷新问题解决方案:利用pageshow事件实现缓存页面数据重置
前端·vue.js·缓存
A_ugust__4 小时前
vue3+ts 封装跟随弹框组件,支持多种模式【多选,分组,tab等】
前端·javascript·vue.js
林九生4 小时前
【Vue3】v-dialog 中使用 execCommand(‘copy‘) 复制文本失效的原因与解决方案
前端·javascript·vue.js
yi碗汤园4 小时前
【一文了解】C#的StringSplitOptions枚举
开发语言·前端·c#
cxr8286 小时前
BMAD框架实践:掌握story-checklist提升用户故事质量
前端·人工智能·agi·智能体·ai赋能
emma羊羊6 小时前
【xsslabs】第12-19关
前端·javascript·靶场·xss
真的想不出名儿9 小时前
vue项目引入字体
前端·javascript·vue.js
胡楚昊9 小时前
Polar WEB(1-20)
前端