一种图片标签contain效果的实现方式

这是我在给博客增加“图片点击预览”的时候,碰到的一个小问题,在此记录。

背景

我希望博客内的图片点击时,无论在手机竖屏还是电脑横屏的情况下,都能在屏幕中间特定一个区域 wrap 容器内—“贴边且完整嵌入”,如果用过往一些布局术语讲,应该是叫让图片元素在容器内 “contain 贴边“布局。

所谓图片 contain 贴边是指:

一张图,无论其宽高比例如何,我们都把他完整放到他的父容器中展示(即要把图片全貌展示到容器内部)。 但又有另外一个要求:“贴边”,即放到容器内部的同时,图片较长的那个边要贴到父容器上—也就是说我们希望尽可能让图片在容器内保持尺寸最大来展示。

例如,如下这样竖着的字的一张图:

我期望他在我的父容器内,这样贴边 contain 布局(其中红圈的区域,就是我所谓的 wrap 容器):

而如下这样横着的图片:

我希望他能在我的的父容器内,这样 contain 贴边布局,上下留白:

总体上 html 标签结构如下:

1
2
3
4
<div class="wrap" style="width: 80%; height: 80%;">
<img src="mypic.com/abc.png" />
</div>
<!-- 其中 wrap 元素的宽高是整个屏幕的 80%。也可以写成绝对 px 像素。 -->

background 和 object-fit 无法满足诉求

有同学可能认为目前已经有 background-size: contain,或者 img 标签也可以设置 object-fit: contain 来实现内部内容的 contain 效果。

然而上述 2 个方案均无法让我给图片自身增加 ”描边“ 和 ”阴影效果“, 即上图中图片周围发光效果。因为 background 方案是将图片变成了 div 元素的背景,已经无法再对背景图做其他样式修饰;而 object-fit 则是把 img 标签当成了父容器,也无法再对容器内图片真实的”边“增加描边。

max-width + max-height 不可行

如果通过给 img 标签设置 max-width: 100% 和 max-height: 100% 是否可行呢。

这个确实可以让过大的图片,缩到我的容器内展示,且能变成 contain 效果。然而如果是一个非常小的图,例如 20px * 30px 的图,我的期望是他至少有一条边也要贴到容器的边上—–也就是说此时图片需要放大并贴边展示。

基于“贴边” 且需要给 img 图片自身加阴影的诉求,我暂时没有找到良好而简洁的方案。于是转而采用 js 实现。

js 实现

思路:

  1. 先将图片的原始尺寸的一条边拉伸到容器该方向的贴边大小。
  2. 图片等比例拉伸后,检查图片另外一条边长度是否超出容器边界,若超出了则采用超出的这条边的容器尺寸进行图片缩放;若没超出,则采用图片的第一条边进行贴边缩放。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const wrapEle = document.querySelector(".wrap");
const imgEle = document.querySelector(".wrap img");
// 拿出图片原始尺寸
const nHeight = imgEle.naturalHeight;
const nWidth = imgEle.naturalWidth;
// 拿出容器尺寸
const wrapWidth = wrapEle.offsetWidth;
const wrapHeight = wrapEle.offsetHeight;
// 1. 求图片宽度对齐后的自动高
const imgResizeHeight = (wrapWidth * nHeight) / nWidth;
if (imgResizeHeight > wrapHeight) {
// 竖向贴边
imgEle.style.height = imgEle.offsetHeight + "px";
imgEle.style.width = "auto";
} else {
// 横向贴边
imgEle.style.width = imgEle.offsetWidth + "px";
imgEle.style.height = "auto";
}

效果

实际项目中,我的 wrap div 并不需要展示出来(只是计算时候有那么个容器的概念而已),最终实现的效果是让图片预览时能在浏览器中央一块区域“贴边 contain 展示”。

效果如下:

横向图: