在Div中居中放置超大图片

206

我一直在尝试使用仅CSS来使一个超大的图片在div内居中。

我们使用的是流式布局,因此图像容器的宽度会随着页面宽度的变化而变化(div的高度固定)。图像位于一个带有内部阴影的div中,使其看起来像是你正在透过页面查看图片。

图像本身已被调整大小以填充周围div的最大可能值(设计中具有max-width值)。

如果图像小于周围的div,则很容易做到:

margin-left: auto;
margin-right: auto;
display: block; 

但是,当图像比 div 更大时,它只是从左边缘开始,并向右偏移(我们使用 overflow: hidden)。

我们可以分配一个 width=100%,但浏览器在调整图像大小方面做得很差,而 Web 设计的中心在于高质量的图像。

有什么想法可以让图像居中,以便 overflow:hidden 均匀地截断两侧?


2
你解决过这个问题吗,@Tom?我现在遇到了同样的问题。 :) - ctrlplusb
我假设你的图像宽度是不同的。 - otherDewi
12个回答

387

尝试类似于这样的代码。它可以将任何巨大的元素在父容器中纵向和横向居中,无论它们的大小如何。

.parent {
    position: relative;
    overflow: hidden;
    //optionally set height and width, it will depend on the rest of the styling used
}

.child {
    position: absolute;
    top: -9999px;
    bottom: -9999px;
    left: -9999px;
    right: -9999px;
    margin: auto;

}

5
太棒了!这适用于任何元素,甚至适用于HTML 5视频...谢谢! - taseenb
3
据我理解:这个方法的原理是创建一个元素(希望它足够大,即29999px x 29999px),然后将图片居中显示在该元素内(margin: auto)。只要图片不超过2*9999px,这种方法就能起作用。 - Michael Krupp
19
为什么不使用“-100%”来代替top/right/bottom/left?这样容器就可以拥有任何宽度。 - Simon
16
我也曾经遇到过 -100% 的问题 ^^. 另外,如果你在图片标签中添加 min-widthmin-height 属性,并将它们的值设为 100%,你基本上就能得到一个 background-size: cover; 的效果 -> jsfiddle - Simon
3
@HarrisonPowers 嗯,父元素当然必须有一个高度,无论是固定的还是动态的。如果其他内容填充了 div 并导致其具有高度(例如文本),它也会起作用。 - hyounis
显示剩余13条评论

228

这是一个旧的问题,但是一个现代的(截至2016年)没有使用弹性盒子或绝对定位的解决方案如下所示。

margin-left: 50%;
transform: translateX(-50%);

.outer {
  border: 1px solid green;
  margin: 20px auto;
  width: 20%;
  padding: 10px 0;
  /*   overflow: hidden; */
}

.inner {
  width: 150%;
  background-color: gold;
  /* Set left edge of inner element to 50% of the parent element */
  margin-left: 50%; 
  /* Move to the left by 50% of own width */
  transform: translateX(-50%); 
}
<div class="outer">
  <div class="inner">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos exercitationem error nemo amet cum quia eaque alias nihil, similique laboriosam enim expedita fugit neque earum et esse ad, dolores sapiente sit cumque vero odit! Ullam corrupti iure eum similique magnam voluptatum ipsam. Maxime ad cumque ut atque suscipit enim quidem. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Excepturi impedit esse modi, porro quibusdam voluptate dolores molestias, sit dolorum veritatis laudantium rem, labore et nobis ratione. Ipsum, aliquid totam repellendus non fugiat id magni voluptate, doloribus tenetur illo mollitia. Voluptatum.</div>
</div>

为什么它起作用?
乍一看,似乎我们先向右移动50%,然后再向左移动50%。 这将导致零移位,那又怎样?
但是这50%并不相同,因为上下文很重要。 如果您使用相对单位,边距将被计算为元素宽度的百分比,而变换将相对于同一个元素的50%。

在添加CSS之前,我们有这种情况。

       +---------------------------+
       | Parent element P of E     |
       |                           |
       +-----------------------------------------+
       | Element E                               |
       +-----------------------------------------+
       |                           |
       +---------------------------+

通过添加样式 margin-left: 50% 我们得到

       +---------------------------+
       | Parent element P of E     |
       |                           |
       |             +-----------------------------------------+
       |             | Element E                               |
       |             +-----------------------------------------+
       |             |             |
       +-------------|-------------+
       |>>>>> a >>>>>|
           
       a is 50% width of P

transform: translateX(-50%)则向左移回

       +---------------------------+
       | Parent element P of E     |
       |                           |
+-----------------------------------------+
| Element E         |                     |
+-----------------------------------------+
|<<<<<<<<< b <<<<<<<|              |
       |            |              |
       +------------|--------------+
       |>>>>> a >>>>|
           
       a is 50% width of P
       b is 50% width of E

很不幸,这只适用于水平居中,因为边距百分比计算始终相对于宽度。也就是说,不仅 margin-leftmargin-right,而且 margin-topmargin-bottom 都是根据宽度计算的。
浏览器兼容性应该没有问题: https://caniuse.com/#feat=transforms2d

1
它无法用于垂直对齐(当.inner的高度大于.outer时)。 - cronfy
1
@cronfy 不行,垂直方向不起作用,因为 margin-top: 50% 将是__宽度__的50%,而不是所需的高度。 - yunzen
2
这绝对是最优雅的解决方案。 - Souhaieb Besbes
3
这就是纯魔法。谢谢!在Safari和Chrome中完美运作(为了保险起见,还添加了-webkit-transform: translateX(-50%);)。 - Nick Schneble
@yunzen,感谢您的回复。请确保img的宽度、高度正确,并且缩放比例为1。似乎有时候translateX会导致img显示不对齐像素等问题,它会将img放置在0.5像素处。这只是在Ubuntu20和Chrome94上进行的测试。 - yurenchen
显示剩余8条评论

22
将一个大的 div 放置在另一个 div 中,将其居中,然后将图像居中放置在该 div 中。
这样可以使其水平居中:
HTML:
<div class="imageContainer">
  <div class="imageCenterer">
    <img src="http://placekitten.com/200/200" />
  </div>
</div>

CSS:
.imageContainer {
  width: 100px;
  height: 100px;
  overflow: hidden;
  position: relative;
}
.imageCenterer {
  width: 1000px;
  position: absolute;
  left: 50%;
  top: 0;
  margin-left: -500px;
}
.imageCenterer img {
  display: block;
  margin: 0 auto;
}

示例:http://jsfiddle.net/Guffa/L9BnL/

为了使其垂直居中,您可以对内部div使用相同的方法,但需要使用图像的高度来将其绝对定位在内部。


最终结果是图像的左边缘居中于“imageContainer”中。正如我在上面评论的那样,我们的图像容器div具有流体宽度和固定高度。 - Tom
1
@Tom:如果我在容器上删除width设置,它将成为父级的宽度,并且即使页面比图像更窄,图像也会居中,即左侧边缘和右侧边缘的一样多将被隐藏:http://jsfiddle.net/Guffa/L9BnL/2/ - Guffa
谢谢你的启发。我在 .imageCenter 中使用 position: relative。这样我就不需要为父容器设置 position: relative。 - user3153298
这个问题有很多巧妙的技巧,它们都有各自的缺点。有些方法“更好”,但并不容易理解。这个解决方案非常合理,图像通常居中于一个被裁剪的更宽的 div 中。感觉不太对,但它绝对可以在不违反网络物理规则的情况下正常工作。尽管它有点不太光彩,但我选择了这个选项而不是其他几个非常难以理解的技巧。 - Radley Sustaire

10

虽然我有些晚了,但我发现这种方法非常直观。 https://codepen.io/adamchenwei/pen/BRNxJr

CSS

.imageContainer {
  border: 1px black solid;

  width: 450px;
  height: 200px;
  overflow: hidden;
}
.imageHolder {
  border: 1px red dotted;

  height: 100%;
  display:flex;
  align-items: center;
}
.imageItself {
  height: auto;
  width: 100%;
  align-self: center;

}

HTML

<div class="imageContainer">
  <div class="imageHolder">
    <img class="imageItself" src="http://www.fiorieconfetti.com/sites/default/files/styles/product_thumbnail__300x360_/public/fiore_viola%20-%202.jpg" />
  </div>
</div>

4
好的!它也可以更加简化。诀窍是在父容器上使用 display: flex,并在图像上使用 align-self: center。这是一个优化后的版本:https://codepen.io/anon/pen/dWKyey - caiosm1005

7

通过设置一些灵活性和隐藏溢出,这很简单。

在此输入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <style>
        div {
            height: 150px;
            width: 150px;
            border: 2px solid red;
        
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
        }
    </style>
</head>
<body>
    <div>
        <img src="sun.jpg" alt="">
    </div>
</body>
</html>

6

我非常喜欢将一张图片作为一个div/node的背景。这样,你就可以使用background-position: center属性来使其在任何屏幕大小下居中。


11
这不是使用图片的可访问方式。对于装饰性图片可能没问题,但任何传达信息的图片都需要提供替代文本。 - user2532030
此外,每当您使用背景图像时,它都会闪烁着默认的背景颜色,直到背景图像完全加载完成,而且没有预加载的方法。 - David Spector

5
不要给图像标签设置固定或显式的宽度或高度。相反,使用以下代码:
      max-width:100%;
      max-height:100%;

ex: http://jsfiddle.net/xwrvxser/1/


1
这样会在图像周围留下空间,我相信他们试图避免这种情况。 - Eoin

1
我发现这是一种更优雅的解决方案,不需要使用弹性布局:
.wrapper {
    overflow: hidden;
}
.wrapper img {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* height: 100%; */ /* optional */
}

1

在 @yunzen 的优秀回答基础上进行改进:

我猜想很多搜索这个话题的人都试图将一张大图片作为“英雄”背景图,例如在首页上。在这种情况下,他们通常希望文字出现在图片上,并且在移动设备上可以缩放。

这是完美的 CSS 代码用于此类背景图(将其用于 <img> 标签):

/* Set left edge of inner element to 50% of the parent element */
margin-left: 50%;

/* Move to the left by 50% of own width */
transform: translateX(-50%);

/* Scale image...(101% - instead of 100% - avoids possible 1px white border on left of image due to rounding error */
width: 101%;

/* ...but don't scale it too small on mobile devices - adjust this as needed */
min-width: 1086px;

/* allow content below image to appear on top of image */
position: absolute;
z-index: -1;

/* OPTIONAL - try with/without based on your needs */
top: 0;

/* OPTIONAL - use if your outer element containing the img has "text-align: center" */
left: 0;

1

您可以使用的一种选项是object-fit: cover,它的行为有点像background-size: cover。更多信息请参见object-fit

忽略下面所有的JS,那只是用于演示的。

关键在于您需要在包装器内设置图像并给它以下属性。

.wrapper img {
  height: 100%;
  width: 100%;
  object-fit: cover;
}

我已经创建了一个演示,在其中您可以更改包装器的高度/宽度并查看其效果。图像将始终垂直和水平居中。它将占用其父元素的100%宽度和高度,并且不会被拉伸/压缩。这意味着图像的纵横比得以保持。通过缩放来应用更改。
唯一的缺点是,object-fit 在IE11上无法使用

// for demo only
const wrapper = document.querySelector('.wrapper');
const button = document.querySelector('button');
const widthInput = document.querySelector('.width-input');
const heightInput = document.querySelector('.height-input');


const resizeWrapper = () => {
  wrapper.style.width = widthInput.value + "px";
  wrapper.style.height = heightInput.value + "px";
}

button.addEventListener("click", resizeWrapper);
.wrapper {
  overflow: hidden;
  max-width: 100%;
  margin-bottom: 2em;
  border: 1px solid red;
}

.wrapper img {
  display: block;
  height: 100%;
  width: 100%;
  object-fit: cover;
}
<div class="wrapper">
  <img src="https://i.imgur.com/DrzMS8i.png">
</div>


<!-- demo only -->
<lable for="width">
  Width: <input name="width" class='width-input'>
</lable>
<lable for="height">
  height: <input name="height" class='height-input'>
</lable>
<button>change size!</button>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接