【问题标题】:CSS going-around content flowCSS 环绕内容流
【发布时间】:2019-05-30 03:49:04
【问题描述】:

有没有办法使用 flex 或其他技术在 CSS 中定义内容流,例如内容“之字形”或以这种方式循环:

 -----------------
|A > B > C > D > E|
|J < I < H < G < F|
 -----------------

 ---
|A H|
|B G|
|C F|
|D E|
 ---

假设总是有 2 列或 2 行。我可以将项目分成 2 个并在它们周围创建 2 个包装项目,但我希望它更具动态性。

基本上,如何让第一行向右流动,第二行向左流动?

【问题讨论】:

  • 是的,使用 CSS 网格布局,看看这里:gridbyexample.com/examples
  • @Armel 这就是答案。把它写成答案,并举个例子:)
  • 也许我遗漏了一些东西,但网格流不会自动转换为“来回”类型的布局。
  • 我想你没有注意到我想要的是第一行从左到右,第二行从右到左......
  • 不,没有任何东西可以通过简单的属性来流动。一个需要使用例如order 或类似的,如果内容是动态的,则需要脚本。好吧,Flexbox 有 *-reverse 属性,但是如何使用它取决于标记的外观

标签: css flexbox css-selectors


【解决方案1】:

很明显,实际上并不存在纯粹的可扩展 CSS 解决方案来实现这样的目标,因此您需要编写一些脚本来动态调整某些属性以获得所需的布局。

如果我们假设所有元素都有相同的宽度,我们可以确定每行的元素数量并根据行对元素应用样式。

这是一个基于上一个答案的代码的基本示例:https://stackoverflow.com/a/49046973/8620333

//total number of element
var n_t = $('.item').length;
//full width of element with margin
var w = $('.item').outerWidth(true);
//width of container without padding
var w_c = $('.grid').width();
//nb element per row
var nb = Math.min(parseInt(w_c / w),n_t);
console.log(nb);
$('.item:nth-child(1n+'+(nb+1)+')').addClass('right');
$('.item:nth-child(1n+'+(2*nb+1)+')').removeClass('right');
$('.item:nth-child(1n+'+(3*nb+1)+')').addClass('right');
$('.item:nth-child(1n+'+(4*nb+1)+')').removeClass('right');

window.addEventListener('resize', function(event){
   //only the width of container will change
   w_c = $('.grid').width();
   nb = Math.min(parseInt(w_c / w),n_t);
   $('.item').removeClass('right');   
  $('.item:nth-child(1n+'+(nb+1)+')').addClass('right');
  $('.item:nth-child(1n+'+(2*nb+1)+')').removeClass('right');
  $('.item:nth-child(1n+'+(3*nb+1)+')').addClass('right');
  $('.item:nth-child(1n+'+(4*nb+1)+')').removeClass('right');
});
.grid {
  background-color: #ddd;
  padding: 10px 0 0 10px;
  overflow:hidden;
}

.item {
  width: 80px;
  height: 80px;
  float:left;
  clear:right;
  background-color: red;
  margin: 0 10px 10px 0;
}
.item.right {
  float:right;
  clear:left;
  background:blue;
}

body {
  margin:0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grid">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
  <div class="item">D</div>
  <div class="item">E</div>
  <div class="item">F</div>
  <div class="item">G</div>
  <div class="item">H</div>
  <div class="item">I</div>
  <div class="item">J</div>
  <div class="item">K</div>
  <div class="item">L</div>
  <div class="item">M</div>
  <div class="item">N</div>
  <div class="item">O</div>
  <div class="item">P</div>
</div>

这个例子不是一个完美的例子,因为我们有对齐问题,但想法是通过交替行将浮动属性应用于行。我只考虑了 4 行,但我们可以使用如下循环轻松使其动态化:

//total number of element
var n_t = $('.item').length;
//full width of element with margin
var w = $('.item').outerWidth(true);
//width of container without padding
var w_c = $('.grid').width();
//nb element per row
var nb = Math.min(parseInt(w_c / w),n_t);
for(var i=1;i<n_t;i++) {
if(i%2==1)
   $('.item:nth-child(1n+'+(i*nb+1)+')').addClass('right');
else
   $('.item:nth-child(1n+'+(i*nb+1)+')').removeClass('right');
}

window.addEventListener('resize', function(event){
   //only the width of container will change
   w_c = $('.grid').width();
   nb = Math.min(parseInt(w_c / w),n_t);
   $('.item').removeClass('right');
  for(var i=1;i<n_t;i++) {
    if(i%2==1)
      $('.item:nth-child(1n+'+(i*nb+1)+')').addClass('right');
    else
      $('.item:nth-child(1n+'+(i*nb+1)+')').removeClass('right');
  }
});
.grid {
  background-color: #ddd;
  padding: 10px 0 0 10px;
  overflow:hidden;
}

.item {
  width: 80px;
  height: 80px;
  float:left;
  clear:right;
  background-color: red;
  margin: 0 10px 10px 0;
}
.item.right {
  float:right;
  clear:left;
  background:blue;
}

body {
  margin:0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grid">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
  <div class="item">D</div>
  <div class="item">E</div>
  <div class="item">F</div>
  <div class="item">G</div>
  <div class="item">H</div>
  <div class="item">I</div>
  <div class="item">J</div>
  <div class="item">K</div>
  <div class="item">L</div>
  <div class="item">M</div>
  <div class="item">N</div>
  <div class="item">O</div>
  <div class="item">P</div>
</div>

为了更好的对齐,我们可以考虑使用 CSS 网格或 flexbox,但是在这种情况下我们需要调整元素的 order 属性。

使用 CSS 网格:

//total number of element
var n_t = $('.item').length;
//full width of element with margin
var w = $('.item').outerWidth(true);
//width of container without padding
var w_c = $('.grid').width();
//nb element per row
var nb = Math.min(parseInt(w_c / w), n_t);
//nb rows
var nr = n_t / nb;
//order of element
var or = 0;

for (var i = 0; i < nr; i++) {
  if (i % 2 == 0)
    for (var j = 0; j < nb; j++) {
      $('.item').eq(nb * i + j).css('order', or++);
    }
  else
    for (var j = 0; j < nb; j++) {
      $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);
    }
}

window.addEventListener('resize', function(event) {
  //only the width of container will change
  w_c = $('.grid').width();
  nb = Math.min(parseInt(w_c / w), n_t);
  nr = n_t / nb;
  or = 0;
  for (var i = 0; i < nr; i++) {
    if (i % 2 == 0)
      for (var j = 0; j < nb; j++) {
        $('.item').eq(nb * i + j).css('order', or++);
      }
    else
      for (var j = 0; j < nb; j++) {
        $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);
      }
  }
});
.grid {
  background-color: #ddd;
  display: grid;
  grid-template-columns: repeat( auto-fit, 80px);
  padding: 10px 0 0 10px;
}

.item {
  height: 80px;
  background-color: red;
  font-size: 30px;
  color: #fff;
  font-weight: bold;
  margin: 0 10px 10px 0;
}

body {
  margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grid">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
  <div class="item">D</div>
  <div class="item">E</div>
  <div class="item">F</div>
  <div class="item">G</div>
  <div class="item">H</div>
  <div class="item">I</div>
  <div class="item">J</div>
  <div class="item">K</div>
  <div class="item">L</div>
  <div class="item">M</div>
  <div class="item">N</div>
  <div class="item">O</div>
  <div class="item">P</div>
</div>

这种方法可以更好地对齐,但最后一行并不总是好的。

我们可以通过使用grid-column 调整最后一个元素来更正最后一行,如下所示:

//total number of element
var n_t = $('.item').length;
//full width of element with margin
var w = $('.item').outerWidth(true);
//width of container without padding
var w_c = $('.grid').width();
//nb element per row
var nb = Math.min(parseInt(w_c / w), n_t);
//nb rows
var nr = Math.ceil(n_t / nb);
//order of element
var or = 0;

for (var i = 0; i < nr; i++) {
  if (i % 2 == 0)
    for (var j = 0; j < nb; j++) {
      $('.item').eq(nb * i + j).css('order', or++);
    }
  else
    for (var j = 0; j < nb; j++) {
      $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);
      /*fix the last row*/
      if (i == (nr - 1)) {
        $('.item').eq(nb * i + j).css('grid-column', " " + (nb - j));
      }
    }
}

window.addEventListener('resize', function(event) {
  //only the width of container will change
  w_c = $('.grid').width();
  nb = Math.min(parseInt(w_c / w), n_t);
  nr = Math.ceil(n_t / nb);
  $('.item').css('grid-column', 'auto');
  or = 0;
  for (var i = 0; i < nr; i++) {
    if (i % 2 == 0)
      for (var j = 0; j < nb; j++) {
        $('.item').eq(nb * i + j).css('order', or++);
      }
    else
      for (var j = 0; j < nb; j++) {
        $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);
        /*fix the last row*/
        if (i == nr - 1) {
          $('.item').eq(nb * i + j).css('grid-column', " " + (nb - j));
        }
      }
  }
});
.grid {
  background-color: #ddd;
  display: grid;
  grid-template-columns: repeat( auto-fit, 80px);
  grid-auto-flow: dense;
  padding: 10px 0 0 10px;
}

.item {
  height: 80px;
  background-color: red;
  font-size: 30px;
  color: #fff;
  font-weight: bold;
  margin: 0 10px 10px 0;
}

body {
  margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grid">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
  <div class="item">D</div>
  <div class="item">E</div>
  <div class="item">F</div>
  <div class="item">G</div>
  <div class="item">H</div>
  <div class="item">I</div>
  <div class="item">J</div>
  <div class="item">K</div>
  <div class="item">L</div>
  <div class="item">M</div>
  <div class="item">N</div>
  <div class="item">O</div>
  <div class="item">P</div>
</div>

Flexbox 可以更适合第二种情况(列方向)。我们只需做与之前考虑列而不是行相同的事情:

//total number of element
var n_t = $('.item').length;
//full height of element with margin
var w = $('.item').outerHeight(true);
//height of container without padding
var w_c = $('.grid').height();
//nb element per row
var nb = Math.min(parseInt(w_c / w), n_t);
//nb rows
var nr = n_t / nb;
//order of element
var or = 0;

for (var i = 0; i < nr; i++) {
  if (i % 2 == 0)
    for (var j = 0; j < nb; j++) {
      $('.item').eq(nb * i + j).css('order', or++);
    }
  else
    for (var j = 0; j < nb; j++) {
      $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);
    }
}

window.addEventListener('resize', function(event) {
  //only the width of container will change
  w_c = $('.grid').height();
  nb = Math.min(parseInt(w_c / w), n_t);
  nr = n_t / nb;
  or = 0;
  for (var i = 0; i < nr; i++) {
    if (i % 2 == 0)
      for (var j = 0; j < nb; j++) {
        $('.item').eq(nb * i + j).css('order', or++);
      }
    else
      for (var j = 0; j < nb; j++) {
        $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);
      }
  }
});
.grid {
  display: flex;
  height:100vh;
  flex-direction:column;
  flex-wrap:wrap;
  align-items:flex-start;
  align-content:flex-start;
  padding-top: 10px;
  padding-left:10px;
  box-sizing:border-box;
}

.item {
  height: 80px;
  width:80px;
  background-color: red;
  font-size: 30px;
  color: #fff;
  font-weight: bold;
  margin: 0 10px 10px 0;
}

body {
  margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grid">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
  <div class="item">D</div>
  <div class="item">E</div>
  <div class="item">F</div>
  <div class="item">G</div>
  <div class="item">H</div>
  <div class="item">I</div>
  <div class="item">J</div>
  <div class="item">K</div>
  <div class="item">L</div>
  <div class="item">M</div>
  <div class="item">N</div>
  <div class="item">O</div>
  <div class="item">P</div>
</div>

在某些情况下,最后一列还有对齐问题,我们可以通过调整边距来解决:

//total number of element
var n_t = $('.item').length;
//full height of element with margin
var w = $('.item').outerHeight(true);
//height of container without padding
var w_c = $('.grid').height();
//nb element per row
var nb = Math.min(parseInt(w_c / w), n_t);
//nb rows
var nr = Math.ceil(n_t / nb);
//order of element
var or = 0;
for (var i = 0; i < nr; i++) {
  if (i % 2 == 0)
    for (var j = 0; j < nb; j++) {
      $('.item').eq(nb * i + j).css('order', or++);
    }
  else {
    for (var j = 0; j < nb; j++) {
      $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);
    }
    if (i == (nr - 1)) {
      /*we add margin+height of non-existing element*/
      $('.item:last').css('margin-top', ((nb * nr - n_t) * (80 + 10)) + "px")
    }
  }
}

window.addEventListener('resize', function(event) {
  //only the width of container will change
  w_c = $('.grid').height();
  nb = Math.min(parseInt(w_c / w), n_t);
  nr = Math.ceil(n_t / nb);
  or = 0;
  $('.item').css('margin-top', 0); /*reset the margin*/
  for (var i = 0; i < nr; i++) {
    if (i % 2 == 0)
      for (var j = 0; j < nb; j++) {
        $('.item').eq(nb * i + j).css('order', or++);
      }
    else {
      for (var j = 0; j < nb; j++) {
        $('.item').eq(nb * i + (nb - j - 1)).css('order', or++);
      }
      if (i == (nr - 1)) {
        /*we add margin+height of non-existing element*/
        $('.item:last').css('margin-top', ((nb * nr - n_t) * (80 + 10)) + "px")
      }
    }
  }
});
.grid {
  display: flex;
  height: 100vh;
  flex-direction: column;
  flex-wrap: wrap;
  align-items: flex-start;
  align-content: flex-start;
  padding-top: 10px;
  padding-left: 10px;
  box-sizing: border-box;
}

.item {
  height: 80px;
  width: 80px;
  background-color: red;
  font-size: 30px;
  color: #fff;
  font-weight: bold;
  margin: 0 10px 10px 0;
}

body {
  margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grid">
  <div class="item">A</div>
  <div class="item">B</div>
  <div class="item">C</div>
  <div class="item">D</div>
  <div class="item">E</div>
  <div class="item">F</div>
  <div class="item">G</div>
  <div class="item">H</div>
  <div class="item">I</div>
  <div class="item">J</div>
  <div class="item">K</div>
  <div class="item">L</div>
  <div class="item">M</div>
  <div class="item">N</div>
  <div class="item">O</div>
  <div class="item">P</div>
</div>

【讨论】:

    【解决方案2】:

    以下解决方案不使用 JavaScript,并且具有一定的可扩展性。我使用display: flex 以便可以使用order 属性。

    基本思想是将order: 1分配给最后一项,order: 2分配给倒数第二项,依此类推。项目的前半部分具有order: -1,并且具有order: 0 的伪元素用作分隔符。棘手的部分是您找出项目的“前半部分”:

    .demo {
      display: flex;
      flex-direction: row;
      flex-wrap: wrap;
      background: #EEE;
    }
    .demo > * {
      margin: .5em;
      width: 4em;
      height: 4em;
      background: #0CF;
    }
    
    /*
     * the example work for a list of 20 elements
     * for additional elements extend the repeating selectors
     */
    
    /* all items ordered backwards */
    
    .demo > :nth-last-child(1)  { order: 1; }
    .demo > :nth-last-child(2)  { order: 2; }
    .demo > :nth-last-child(3)  { order: 3; }
    .demo > :nth-last-child(4)  { order: 4; }
    .demo > :nth-last-child(5)  { order: 5; }
    .demo > :nth-last-child(6)  { order: 6; }
    .demo > :nth-last-child(7)  { order: 7; }
    .demo > :nth-last-child(8)  { order: 8; }
    .demo > :nth-last-child(9)  { order: 9; }
    .demo > :nth-last-child(10) { order: 10; }
    
    /* first half items are source ordered */
    
    .demo> :nth-child(-n+0):nth-last-child(n+1),
    .demo> :nth-child(-n+1):nth-last-child(n+2),
    .demo> :nth-child(-n+2):nth-last-child(n+3),
    .demo> :nth-child(-n+3):nth-last-child(n+4),
    .demo> :nth-child(-n+4):nth-last-child(n+5),
    .demo> :nth-child(-n+5):nth-last-child(n+6),
    .demo> :nth-child(-n+6):nth-last-child(n+7),
    .demo> :nth-child(-n+7):nth-last-child(n+8),
    .demo> :nth-child(-n+8):nth-last-child(n+9),
    .demo> :nth-child(-n+9):nth-last-child(n+10),
    .demo> :nth-child(-n+10):nth-last-child(n+11) {
      order: -1;
    }
    
    /* the separator uses flex-basis trick and ordered between the two halves */
    
    .demo::after {
      content: "";
      flex-basis: 100%;
      order: 0;
    }
    <div class="demo">
      <div>1</div>
      <div>2</div>
      <div>3</div>
      <div>4</div>
      <div>5</div>
      <div>6</div>
      <div>7</div>
      <div>8</div>
      <div>9</div>
      <div>10</div>
    </div>

    对于两列布局,在父级上指定flex-direction: column; height: 25em(高度必须固定)。

    【讨论】:

    • 这个解决方案太疯狂了,我喜欢它
    【解决方案3】:

    我可以使用 Flexbox 和一些 JavaScript 来做到这一点(我无法单独使用 CSS):

    var reverseBoxes = function () {
    
      var flexItems = document.querySelectorAll(".child"),
          flexItemsCount = flexItems.length,
          reverseAt = flexItems.length / 2,
          breakPoint = 480;
    
      for (var i = reverseAt; i < flexItemsCount; i++) {
        flexItems[i].style.order = flexItemsCount - i;
      }
    
      for (var j = 0; j < flexItemsCount; j++) {
        if (window.innerWidth > breakPoint) {
          flexItems[j].style.width = (100 / flexItemsCount) * 2 - 2 + "%";
          flexItems[j].style.height = "auto";
        } else {
          flexItems[j].style.height = (100 / flexItemsCount) * 2 - 2 + "%";
          flexItems[j].style.width = "auto";
        }
      }
    
    }
    
    reverseBoxes();
    window.addEventListener("resize", reverseBoxes);
    body {
      font-family: Arial, sans-serif;
      font-size: 18px;
      margin: 0;
      padding: 0;
    }
    
    .parent {
      display: flex;
      flex-wrap: wrap;
      list-style-type: none;
      padding: 0;
      height: 100vh;
    }
    
    .child {
      margin: 1%;
      text-align: center;
      background: #069;
      color: #fff;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    @media only screen and (max-width: 480px) {
      .parent {
        flex-direction: column;
      }
      .child {
        width: 48%;
      }
    }
    <div class="parent">
      <div class="child">A</div>
      <div class="child">B</div>
      <div class="child">C</div>
      <div class="child">D</div>
      <div class="child">E</div>
      <div class="child">F</div>
      <div class="child">G</div>
      <div class="child">H</div>
      <div class="child">I</div>
      <div class="child">J</div>
    </div>

    这是你要找的东西吗?

    【讨论】:

      【解决方案4】:

      这里有一个技巧可以帮助您选择一半的项目。默认使用float:left,并将float: right设置为被选为后半部分的项目。

      缺点是如果你需要支持很多项目,你需要定义很多规则。

      .box{
        width: 160px;
      }
      
      .item{
        width: 40px;
        float: left;
      }
      
      /* selecting half or more items. Up to 6 */
      .item:first-child:last-child,
      .item:nth-child(n+2):nth-last-child(-n+2),
      .item:nth-child(n+3):nth-last-child(-n+3),
      .item:nth-child(n+4):nth-last-child(-n+4),
      .item:nth-child(n+5):nth-last-child(-n+5),
      .item:nth-child(n+6):nth-last-child(-n+6) {
        float: right;
      }
      <div class="box">
        <div class="item">A</div>
        <div class="item">B</div>
        <div class="item">C</div>
        <div class="item">D</div>
        <div class="item">E</div>
        <div class="item">F</div>
        <div class="item">G</div>
        <div class="item">H</div>
      </div>

      纵向场景可能是:

      .box {
        margin-top: 100px;
        width: 160px;
        transform: rotate(90deg);
      }
      
      .item {
        width: 40px;
        float: left;
        transform: rotate(-90deg);
      }
      
      
      /* selecting half or more items. Up to 6 */
      
      .item:first-child:last-child,
      .item:nth-child(n+2):nth-last-child(-n+2),
      .item:nth-child(n+3):nth-last-child(-n+3),
      .item:nth-child(n+4):nth-last-child(-n+4),
      .item:nth-child(n+5):nth-last-child(-n+5),
      .item:nth-child(n+6):nth-last-child(-n+6) {
        float: right;
      }
      <div class="box">
        <div class="item">A</div>
        <div class="item">B</div>
        <div class="item">C</div>
        <div class="item">D</div>
        <div class="item">E</div>
        <div class="item">F</div>
        <div class="item">G</div>
        <div class="item">H</div>
      </div>

      【讨论】:

      • @AdityaGupta 是的,它不支持无限数量的项目,但他可能需要最多 20 个,并且可以覆盖它...
      • 没关系,只需阅读 cmets。他正在通过代码生成行。他可以在他的情况下使用“flex-box”。
      猜你喜欢
      • 2018-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-04
      • 1970-01-01
      • 2017-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多