Centrar imagem de tamanho grande em Div

175

Eu tenho tentado resolver como centralizar uma imagem de grandes dimensões dentro de uma div usando apenas css.

Como estamos usando um layout fluido, a largura dos contêineres da imagem varia conforme a largura da página (a altura da div é fixa). A imagem fica dentro de uma div, com um valor inserido na sombra da caixa para parecer que você está olhando a página na imagem.

A própria imagem foi dimensionada para preencher a div circundante com o maior valor possível (o design possui um max-widthvalor).

É bem fácil de fazer se a imagem for menor que a div circundante:

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

Mas quando a imagem é maior que a div, ela simplesmente começa na borda esquerda e fica fora do centro para a direita (estamos usando overflow: hidden).

Poderíamos atribuir um width=100%, mas os navegadores fazem um péssimo trabalho no redimensionamento de imagens e o design da web se concentra em imagens de alta qualidade.

Alguma idéia de centralizar a imagem para overflow:hiddencortar as duas extremidades uniformemente?

Tom
fonte
2
Você já resolveu esse problema @Tom? Estou enfrentando o mesmo problema no momento. :)
ctrlplusb
Presumo que as larguras da sua imagem variam.
otherDewi

Respostas:

365

Tente algo assim. Isso deve centralizar qualquer elemento enorme no meio vertical e horizontalmente em relação ao pai, não importando os dois tamanhos.

.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;

}
hyounis
fonte
4
maravilhoso: isso funciona com qualquer elemento. mesmo com vídeo html 5 ... obrigado!
Taseenb
3
Pelo que entendi: ele funciona porque cria um elemento (espero) maior que a imagem (2 * 9999px x 2 * 9999px) e centraliza a imagem dentro desse elemento (margem: auto). Portanto, desde que a imagem não exceda 2 * 9999px, ela deverá funcionar.
Michael Krupp
16
Por que não usar -100%para cima / direita / baixo / esquerda? Com isso, o contêiner poderia ter literalmente qualquer largura.
Simon
15
Sim, eu também experimentei o -100%problema ^^. Btw: se você adicionar min-widthe min-heightde 100%você basicamente obter um background-size: cover;comportamento com tags de imagem -> jsFiddle
Simon
3
@HarrisonPowers Bem, os pais precisam ter uma altura, é claro ou dinâmico. Também funcionará se algum outro conteúdo estiver preenchendo a div, fazendo com que ela tenha uma altura (como texto, por exemplo).
hyounis
162

Este é um Q antigo, mas uma solução moderna sem flexbox ou posição absoluta funciona assim.

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

Então, por que isso funciona?
À primeira vista, parece que deslocamos 50% para a direita e depois 50% para a esquerda novamente. Isso resultaria em turno zero, e daí?
Mas os 50% não são os mesmos, porque o contexto é importante. Se você usar unidades relativas, uma margem será calculada como porcentagem da largura do elemento pai , enquanto a transformação será de 50% em relação ao mesmo elemento.

Temos essa situação antes de adicionarmos o CSS

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

Com o estilo adicionado margin-left: 50%, temos

       +-------------------------------------------+
       | Parent element P of E                     |
       |                                           |
       |                     +-----------------------------------------------------------+
       |                     | Element E                                                 |
       |                     +-----------------------------------------------------------+
       |                     |                     |
       +---------------------|---------------------+
       |========= a ========>|

       a is 50% width of P

E os transform: translateX(-50%)turnos de volta para a esquerda

       +-------------------------------------------+
       | Parent element P of E                     |
       |                                           |
+-----------------------------------------------------------+
| Element E                 |                               |
+-----------------------------------------------------------+
|<============ b ===========|                      |
       |                    |                      |
       +--------------------|----------------------+
       |========= a =======>|

       a is 50% width of P
       b is 50% width of E

Infelizmente, isso funciona apenas para centralização horizontal, pois o cálculo da porcentagem da margem é sempre relativo à largura. Ou seja, não apenas margin-lefte margin-right, mas também margin-tope margin-bottomsão calculados em relação à largura.

A compatibilidade do navegador não deve ser problema: https://caniuse.com/#feat=transforms2d

yunzen
fonte
Ele não funciona para alinhamento vertical (quando .inner tem altura maior que .outer)
cronfy
1
@cronfy No. verticalmente não funcionará, porque margin-top: 50%terá 50% da largura . não a altura desejada.
9136 yunzen
2
que é de longe a solução mais elegante
Souhaieb Besbes
procurando por uma solução por um tempo, melhor e mais simples; Felicidades
lauWM
2
Isso é pura mágica de mago. Obrigado! Funciona perfeitamente no Safari e Chrome (com um adicional -webkit-transform: translateX(-50%);para uma boa medida)
Nick Schneble
22

Coloque uma div grande dentro da div, centralize isso e o centro da imagem dentro dessa div.

Isso centraliza horizontalmente:

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;
}

Demonstração: http://jsfiddle.net/Guffa/L9BnL/

Para centralizá-lo verticalmente também, você pode usar o mesmo para a div interna, mas você precisaria da altura da imagem para colocá-la absolutamente dentro dela.

Guffa
fonte
Isso acabou com a borda esquerda da imagem centralizada no "imageContainer". Como comentei acima, nossa div de contêiner de imagem é a largura do fluido e a altura fixa.
Tom
1
@ Tom: Se eu remover a widthconfiguração do contêiner, ela terá a largura do pai e a imagem será centralizada mesmo que a página seja mais estreita que a imagem, ou seja, tanto a borda esquerda quanto a borda direita estarão ocultas : jsfiddle.net/Guffa/L9BnL/2
Guffa
Obrigado pela inspiração. Uso posição: relativa para .imageCenter. Dessa forma, não preciso da posição: relativa para o contêiner pai.
user3153298
Há muitos truques inteligentes nessa questão e todos eles têm suas desvantagens. Alguns funcionam "melhor", mas não são nada fáceis de entender. Esta solução faz todo o sentido, pois a imagem está sendo centralizada normalmente em uma div maior que está sendo cortada. Parece errado, mas definitivamente funciona sem violar as leis da física da web. Eu escolhi essa opção entre vários outros truques muito difíceis de entender, mesmo que pareçam um pouco sujos.
Radley Sustaire
9

Tarde do jogo, mas achei esse método extremamente intuitivo. 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>
Ezeewei
fonte
3
Agradável! Também pode ser simplificado. O truque é feito display: flexno contêiner pai e align-self: centerna imagem. Aqui está uma versão otimizada: codepen.io/anon/pen/dWKyey
caiosm1005
5

Não use largura ou altura fixa ou explícita na tag da imagem. Em vez disso, codifique-o:

      max-width:100%;
      max-height:100%;

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

leatherjacket4
fonte
1
Isso deixa espaço em torno da imagem que acredito que eles estão tentando evitar.
Eoin
3

eu sou um grande fã de transformar uma imagem em segundo plano de um div / node - então você pode usar o background-position: centeratributo para centralizá-la, independentemente do tamanho da tela

Shain Lafazan
fonte
9
Esta não é uma maneira acessível de usar imagens. Pode funcionar bem para imagens decorativas, mas qualquer imagem informativa precisa de texto alternativo.
user2532030
0

A largura e a altura são apenas por exemplo:

parentDiv{
    width: 100px;
    height: 100px;
    position:relative; 
}
innerDiv{
    width: 200px;
    height: 200px;
    position:absolute; 
    margin: auto;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
}

Ele deve funcionar para você se a parte superior esquerda e a parte superior da sua divisão pai não estiverem na parte superior e esquerda da janela da tela. Funciona para mim.

Панайот Толев
fonte
Não parecia fazer a diferença. Não tenho certeza se tem alguma coisa devido ao fato de que a largura é fluida, enquanto a altura é fixa no que seria a div principal.
Tom
2
Essa técnica funciona se a imagem for menor que o contêiner.
otherDewi
0

Eu achei que era uma solução mais elegante, sem flex:

.wrapper {
    overflow: hidden;
}
.wrapper img {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* height: 100%; */ /* optional */
}
Cezar D.
fonte
0

Com base na grande resposta de @ yunzen:

Acho que muitas pessoas que pesquisam esse tópico estão tentando usar uma imagem grande como imagem de plano de fundo "herói", por exemplo, em uma página inicial. Nesse caso, eles geralmente desejam que o texto apareça sobre a imagem e aumente sua escala em dispositivos móveis.

Aqui está o CSS perfeito para uma imagem de fundo (use-a na <img>tag):

/* 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;
Dave Koo
fonte
-1

Uma opção que você pode usar é object-fit: coverse comportar um pouco background-size: cover. Mais sobre ajuste de objeto .

ignore todas as JS abaixo, isso é apenas para a demonstração.

A chave aqui é que você precisa definir a imagem dentro de um wrapper e fornecer as seguintes propriedades.

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

Criei uma demonstração abaixo, onde você altera a altura / largura do invólucro e vê em jogo. A imagem será sempre centralizada na vertical e na horizontal. Ele ocupará 100% de sua largura e altura pai e não será esticado / esmagado. Isso significa que a proporção da imagem é mantida. As alterações são aplicadas ao aumentar / diminuir o zoom.

A única desvantagem object-fité que ele não funciona no 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>

volt
fonte