Сделать гибкие элементы перекрывающимися

Я хотел бы показать горизонтальный ряд неизвестного количества игральных карт. Для этого их придется перекрывать, если их слишком много. У меня возникли проблемы с тем, чтобы убедить гибкую коробку перекрывать карты, не уменьшая их. В приведенном ниже примере карты сжимаются. Я пробовал flex-shrink: 0, но тогда max-width не уважали.

.cards {
  display: flex;
  max-width: 300px;
}

.card {
  width: 50px;
  height: 90px;
  border: 1px solid black;
  border-radius: 3px;
  background-color: rgba(255, 0, 0, 0.4);
}
<div class='cards'>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
</div>


person Michael Arrison    schedule 11.05.2017    source источник
comment
Зачем использовать flex, если вы хотите, чтобы они перекрывались, разве смысл flex не в том, что он заполняет пространство?   -  person Pete    schedule 11.05.2017
comment
Можете ли вы просто использовать отрицательную маржу для перекрытия?   -  person StefanBob    schedule 11.05.2017
comment
Отрицательная маржа будет работать для данного количества карт. Но это должно было бы измениться для каждой карты и для каждого общего количества карт. Я ищу что-то более гибкое.   -  person Michael Arrison    schedule 11.05.2017
comment
Можете ли вы уточнить, чего вы пытаетесь достичь? Что слишком много? Как вы хотите, чтобы они точно перекрывались. Что вы имеете в виду в ответ на @StefanBob с это должно измениться для каждой карты и для каждого количества общих карт. Я ищу что-то более гибкое.? это должно измениться как? И почему? Какая там логика? Что значит более гибкий?   -  person Michael Coker    schedule 11.05.2017
comment
@MichaelCoker Я пытаюсь развернуть карты, как показано на этом рисунке: shpgames. com/zero-mod/fan.jpg (но без кривой). Карточки никогда не должны изменять размер — они всегда должны оставаться 50 x 90 пикселей. Таким образом, если максимальная ширина контейнера составляет 300 пикселей, вы можете разместить до 6 карт без перекрытия. После добавления седьмого будет небольшое перекрытие. И если бы карт было 50, вы бы увидели только 6 пикселей каждой из-за перекрытия. Поэтому я надеюсь, что гибкий контейнер сможет позаботиться об этом расчете.   -  person Michael Arrison    schedule 11.05.2017


Ответы (8)


Вот как бы я это сделал с помощью flexbox.

.cards {
  display: flex;
  align-content: center;
  max-width: 35em;
}

.cardWrapper {
  overflow: hidden;
}

.cardWrapper:last-child, .cardWrapper:hover {
    overflow: visible;
}

.card {
    width: 10em;
    min-width: 10em;
    height: 6em;
    border-radius: 0.5em;
    border: solid #666 1px;
    background-color: #ccc;
    padding: 0.25em;
  
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
<div class="cards">
  <div class="cardWrapper">
    <div class="card">card 1 blah blah blah</div>
  </div>
  <div class="cardWrapper">
    <div class="card">card 2 blah blah blah</div>
  </div>
  <div class="cardWrapper">
    <div class="card">card 3 blah blah blah</div>
  </div>
  <div class="cardWrapper">
    <div class="card">card 4 blah blah blah</div>
  </div>
  <div class="cardWrapper">
    <div class="card">card 5 blah blah blah</div>
  </div>
</div>

Обратите внимание, что с технической точки зрения карты не перекрываются, они просто обрезаются. Но они выглядят как перекрывающиеся. Хитрость заключается в том, чтобы обернуть каждую карту в другой элемент с помощью overflow: hidden.

Элементы упаковки сжимаются, чтобы соответствовать доступному пространству, и в этом пространстве отображается максимально возможное количество карточек.

Я включаю правило :hover только для того, чтобы показать, как вы можете полностью отобразить карту из середины «стопки», но в реальном проекте я, вероятно, сделал бы это для выбранных карт, а не для тех, которые находятся под курсором.

person FTWinston    schedule 30.04.2018

Вы можете сделать элементы в гибком макете перекрывающимися, используя transform: translateX(-10px), но это не будет относиться к макету, который вы пытаетесь получить. Я не думаю, что вы можете сделать это во flexbox. Но вы можете легко сделать это с помощью JS.

var parentEl = document.getElementById("cards");

function sortCards() {
  var cards = document.getElementsByClassName("card"),
      cw = parentEl.clientWidth,
      sw = parentEl.scrollWidth,
      diff = sw - cw,
      offset = diff / (cards.length - 1);

  for (var i = 1; i < cards.length; i++) {
    cards[i].style.transform = "translateX(-" + offset * i + "px)";
  }
}

sortCards();

var b = document.getElementById("button");
b.addEventListener("click", function() {
  var div = document.createElement("div");
  div.classList.add("card");
  parentEl.appendChild(div);
  sortCards();
});
.cards {
  display: flex;
  max-width: 300px;
}

.card {
  height: 90px;
  border: 1px solid black;
  border-radius: 3px;
  background-color: rgba(255, 0, 0, 0.4);
  flex: 0 0 50px;
  background: red;
  transition: transform .25s;
}
<div><button id="button">addcards</button></div>
<div class='cards' id="cards">
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
</div>

person Michael Coker    schedule 11.05.2017
comment
Хотя это и не флекс, это отличное решение. Он выполняет все требования переменного количества карт в заданном пространстве. Если у кого-то все еще есть гибкое решение, я бы хотел его услышать, но на данный момент это достаточно хороший принятый ответ. - person Michael Arrison; 11.05.2017
comment
@MichaelArrison кто-то другой ответил, сказав, что вы можете сделать так, чтобы элементы перекрывались с помощью margin - вы можете сделать это и с transform: translateX(), как я сделал здесь, но это не будет соответствовать вашему макету, поэтому я его не использовал. Вам понадобится немного математики (я думаю), чтобы сделать это. Но я обновил свой ответ, чтобы использовать flex и transform, поскольку у вас было это в исходном вопросе. - person Michael Coker; 11.05.2017
comment
Почему в вашем решении цикл for не начинается с i=1? - person BoltKey; 23.12.2019
comment
@BoltKey, потому что я в основном пишу CSS, и именно так я по умолчанию использую цикл JS :) Обновил ответ с вашим предложением, спасибо! - person Michael Coker; 27.12.2019

Вы можете сделать это, используя только css, используя сетку вместо flex.

.hand{
  width: 50%;
  margin-left: auto;
  margin-right: auto;
  justify-content: center;
  display: grid;
  grid-template-columns: repeat(auto-fit,  minmax(10px, max-content)) ;
}
.card{
  width: 3em;
  height: 2.4em;
  padding: 3px;
  margin: 2px;
  background-color: lightgreen;
  border-style: solid;
  transform: rotate(3deg);  /*makes it easier to see the overlap*/
}
<div class="hand"> 
  <div class="card"> card </div>
  <div class="card"> card </div>
  <div class="card"> card </div>
  <div class="card"> card </div>
  <div class="card"> card </div>
  <div class="card"> card </div>
  <div class="card"> card </div>
  <div class="card"> card </div>
</div>

person Max Garber    schedule 29.10.2018

Гибкий контейнер предназначен для выравнивания элементов по осям X и Y.

Вы спрашиваете о выравнивании по оси Z.

Flexbox не предназначен для выравнивания по оси Z (перекрытия).

Любое перекрытие должно происходить из-за отрицательных полей, абсолютного позиционирования, CSS Grid Layout, JavaScript или чего-то еще. Свойство z-index также может сыграть свою роль.

Вот простой пример использования CSS Grid:

.cards {
  display: grid;
  grid-template-columns: repeat(30, 10px);
  grid-template-rows: 90px;
  max-width: 300px;
}

.card {
  grid-row-start: 1;
  background-color: lightgreen; 
  border: 1px solid black;
}

.card:nth-child(1) { grid-column: 1 / 6; }
.card:nth-child(2) { grid-column: 4 / 9; }
.card:nth-child(3) { grid-column: 7 / 12; }
.card:nth-child(4) { grid-column: 10 / 15; }
.card:nth-child(5) { grid-column: 13 / 18; }
.card:nth-child(6) { grid-column: 16 / 21; }
.card:nth-child(7) { grid-column: 19 / 24; }
.card:nth-child(8) { grid-column: 22 / 27; }
.card:nth-child(9) { grid-column: 25 / 30; }
<div class='cards'>
  <div class='card'>1</div>
  <div class='card'>2</div>
  <div class='card'>3</div>
  <div class='card'>4</div>
  <div class='card'>5</div>
  <div class='card'>6</div>
  <div class='card'>7</div>
  <div class='card'>8</div>
  <div class='card'>9</div>
</div>

Карточки перекрываются с помощью размещения на основе строк. В этом случае свойство grid-column используется для перекрытия дорожек столбцов.

Тем не менее, некоторые сценарии потребуются, если количество карточек будет меняться динамически, так как величина перекрытия должна быть разной, чтобы все карточки точно поместились в контейнере фиксированной ширины.

person Michael Benjamin    schedule 11.05.2017
comment
Хотя одна карта должна быть поверх другой, это не главное. Важной проблемой является их перекрытие по оси x. Flex-контейнеры имеют множество вариантов выравнивания по оси X. Но я просто не уверен, есть ли у него какие-либо перекрытия. - person Michael Arrison; 11.05.2017
comment
Я думаю, что с помощью jquery вы можете поместить формулу на поле слева от второй карты и далее, которая будет динамически вычислять ее и перекрывать карты. Если хочешь, я могу привести тебя в пример. - person Nasir T; 11.05.2017
comment
@NasirT поднимает хороший вопрос, хотя его можно упростить до чистого CSS. Правило вроде .card:nth-child(n+2) { margin-left: -30px; } было бы весьма эффективно, если бы количество карт было известно заранее. - person Michael Arrison; 11.05.2017

Поздно на вечеринку, но это было моим решением для подобной ситуации:

Есть предложение упомянуть отрицательные поля, и это правда, что, когда никакие другие адаптации не выполняются, карты всегда будут перекрываться с: margin-left: -30px; (Ширина перекрытия выбрана несколько произвольно. Вы можете выбрать ширину карты как максимальную.)

Что я здесь изменил, так это добавил в микс justify-content: space-evenly;. Это распределяет карты по доступному пространству во гибком контейнере и позволяет картам перекрываться только тогда, когда становится слишком тесно. Это может быть решением, если вы согласны с тем, что карты распределяются таким образом, когда они не перекрываются.

NB. Поскольку отрицательное поле смещает карты влево, я также добавил padding-left той же ширины, что и отрицательное поле, во флекс-контейнер.

НБ2. Я изменил непрозрачность исходного примера, потому что в противном случае карты становятся прозрачными.

    .cards {
      display: flex;
      max-width: 300px;
      padding-left: 30px;
      justify-content: space-evenly;
    }

    .card {
      width: 50px;
      height: 90px;
      border: 1px solid black;
      border-radius: 8px;
      background-color: rgb(255, 100, 100);
      margin-left: -30px;
    }
    <div class='cards'>
      <div class='card'></div>
      <div class='card'></div>
      <div class='card'></div>
      <div class='card'></div>
      <div class='card'></div>
      <div class='card'></div>
      <div class='card'></div>
      <div class='card'></div>
    </div>

person Bruno de Vries    schedule 03.01.2021

Я придумал универсальное решение на основе CSS. Однако с некоторыми оговорками:

  1. Последняя карта переполняет контейнер .cards.
  2. Дочерний элемент .card необходим для создания эффекта перекрытия.

.cards {
  display: flex;
  max-width: 300px;
}

.card {
  position: relative;
  flex-basis: 50px;
  height: 90px;
}

.card::before {
  content: '';
  display: block;
  position: absolute;
  width: 50px;
  height: 100%;
  border: 1px solid black;
  border-radius: 3px;
  background-color: rgba(255, 0, 0, 0.4);
  box-sizing: border-box;
}
<div class='cards'>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
</div>

person Nate Whittaker    schedule 28.03.2018

Вы можете сделать это с помощью свойства margin и применить к нужным элементам вручную или с помощью JS.

.cards {
  display: flex;
  max-width: 300px;
}

.overl {
margin-left:-10px;
}

.card {
  width: 50px;
  height: 90px;
  border: 1px solid black;
  border-radius: 3px;
  background-color: rgba(255, 0, 0, 0.4);
}
<div class='cards'>
  <div class='card'></div>
  <div class='card overl'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
  <div class='card'></div>
</div>

person Sam    schedule 11.05.2017

Это накладывает правый дочерний элемент на левый дочерний элемент, исключая первый.

.overlapped {
  > * + * {
    postion: absolute;
    margin-left: -7px;
  }
}

Ваш контейнер также должен иметь строку flex и flex-direction.

person 1P0    schedule 24.12.2019