Flexbox:包装/“蛇包装"上的反向 [英] Flexbox: Reverse Direction on Wrap / "Snake Wrap"

查看:24
本文介绍了Flexbox:包装/“蛇包装"上的反向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想找到一种方法,将单个容器中的元素换行到与前一行相反方向的新行,就像一条蛇向后弯曲.我无法使用 flexbox 以及 flex-direction 和 flex-wrap 属性的任何组合来实现这一结果.

flexbox 结果与期望结果的图像比较:

这是一个显示 flexbox 结果与期望结果(伪造)的片段:

body {字体:400 14px 'Arial',无衬线;}.标题 {字体大小:1em;填充:20px 0;}.title:第一个孩子{填充:0 0 20px;}.flex-container, .fake-container, .fake-row {宽度:500px;背景:橙色;显示:弹性;}.flex 容器 {flex-flow:行包装;/* 行/反向 & 的任意组合包装/反转 */}.物品 {显示:内联块;背景:紫色;宽度:80px;最大宽度:80px;高度:80px;边距:10px;行高:80px;白颜色;字体粗细:粗体;字体大小:2em;文本对齐:居中;}.假容器{flex-flow: 列 nowrap;}.假行{flex-flow:行包装;高度:100px;}.fake-row:nth-child(2) {flex-flow: row-reverse wrap;}

<div class="title">Flexbox 结果:</div><div class="flex-container"><div class="item">1</div><div class="item">2</div><div class="item">3</div><div class="item">4</div><div class="item">5</div><div class="item">6</div><div class="item">7</div>

<div class="title">想要的结果(伪造):</div><div class="fake-container"><div class="fake-row"><div class="item">1</div><div class="item">2</div><div class="item">3</div><div class="item">4</div><div class="item">5</div>

<div class="fake-row"><div class="item">6</div><div class="item">7</div>

尽管我很想找到一个纯 CSS 解决方案来实现这种蛇形包裹",但我开始怀疑它的可能性.我确信可以使用 javascript 解决方案,但我不确定如何在不使事情过于复杂的情况下继续创建一个.

有什么简单的解决方案吗?感谢您抽出宝贵时间.

解决方案

一个 CSS 网格解决方案 类似于这个每行使用固定数量的元素(也就是固定数量的列).

.flex-container {宽度:500px;背景:橙色;显示:网格;网格模板列:重复(5,1fr);/*定义列数*/网格自动流:密集;/* 这对于填满所有空间很重要*/网格间隙:20px;填充:10px;}.物品 {背景:紫色;高度:80px;行高:80px;白颜色;字体粗细:粗体;字体大小:2em;文本对齐:居中;}.item:nth-child(10n + 6) {grid-column:5}.item:nth-child(10n + 7) {grid-column:4}.item:nth-child(10n + 8) {grid-column:3}.item:nth-child(10n + 9) {grid-column:2}/*.item:nth-child(10n + 10) {grid-column:1} 不需要*//* 对于 N = 列数.item:nth-child((2xN)n + (N+1)) { grid-column:N;}.item:nth-child((2xN)n + (N+2)) { grid-column:(N-1);}.....item:nth-child((2xN)n + (2xN)) { grid-column:1;}*/.item:before {内容:计数器(数量);反增量:数量;}身体 {字体:400 14px 'Arial',无衬线;计数器重置:数量;}

<div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div>

如果你想要一个通用的解决方案,这里是一个使用 JS 和 flexbox 的解决方案.诀窍是根据每个项目的行位置设置 order 属性.我将依靠这个旧答案来完成大部分计算,以便找到行/列数:>

//元素总数var elems = document.querySelectorAll('.item');var n_t = elems.length;//元素的宽度var w = parseInt(document.querySelector('.item').offsetWidth);//带边距的元素全宽var m = document.querySelector('.item').currentStyle ||window.getComputedStyle(document.querySelector('.item'));w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);//容器宽度var w_c = parseInt(document.querySelector('.flex-container').offsetWidth);//容器填充var c = document.querySelector('.flex-container').currentStyle ||window.getComputedStyle(document.querySelector('.flex-container'));var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);var 调整 = 函数(){//只有容器的宽度会改变w_c = parseInt(document.querySelector('.flex-container').offsetWidth);//列数nb = Math.min(parseInt((w_c - p_c)/w),n_t);//行数nc = Math.ceil(n_t/nb);for(var j = 0;j<nb;j++) {for(var i = 0;i<nc;i++) {if(j + i*nb >= n_t)/* 如果我们达到元素的数量,我们就退出*/休息如果(i%2!=1)elems[j + i*nb].style.order=j + i*nb;/* 正常流 */别的elems[j + i*nb].style.order=(nb - j) + i*nb;/* 逆流 */}}}调整()window.addEventListener('resize', function(){adjust()})

.flex-container {背景:橙色;显示:弹性;弹性包裹:包裹;}.物品 {背景:紫色;高度:80px;宽度:80px;边距:10px;行高:80px;网格间隙:20px;白颜色;字体粗细:粗体;字体大小:2em;文本对齐:居中;}.item:before {内容:计数器(数量);反增量:数量;}身体 {字体:400 14px 'Arial',无衬线;计数器重置:数量;}

<div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div>

在某些情况下,最后一行没有装满物品时会出现一些小问题.您可以通过向最后一个元素添加一些边距来纠正此问题

//元素总数var elems = document.querySelectorAll('.item');var n_t = elems.length;//元素的宽度var w = parseInt(document.querySelector('.item').offsetWidth);//带边距的元素全宽var m = document.querySelector('.item').currentStyle ||window.getComputedStyle(document.querySelector('.item'));w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);//容器宽度var w_c = parseInt(document.querySelector('.flex-container').offsetWidth);//容器填充var c = document.querySelector('.flex-container').currentStyle ||window.getComputedStyle(document.querySelector('.flex-container'));var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);var 调整 = 函数(){//只有容器的宽度会改变w_c = parseInt(document.querySelector('.flex-container').offsetWidth);//列数nb = Math.min(parseInt((w_c - p_c)/w),n_t);//行数nc = Math.ceil(n_t/nb);for(var j = 0;j<nb;j++) {for(var i = 0;i<nc;i++) {if(j + i*nb >= n_t)/* 如果我们达到元素的数量,我们就退出*/休息elems[j + i*nb].style.marginLeft='10px';/*我们保留边距*/如果(i%2!=1)elems[j + i*nb].style.order=j + i*nb;/* 正常流 */别的 {elems[j + i*nb].style.order=(nb - j) + i*nb;/* 逆流 *//*边距修复*/if(i == (nc - 1) && (j + i*nb == (n_t - 1)) && j<(nb-1)) {elems[j + i*nb].style.marginLeft=((nb*nc - n_t)*w + 10) + 'px';}}}}}调整()window.addEventListener('resize', function(){adjust()})

.flex-container {背景:橙色;显示:弹性;弹性包裹:包裹;}.物品 {背景:紫色;高度:80px;宽度:80px;边距:10px;行高:80px;网格间隙:20px;白颜色;字体粗细:粗体;字体大小:2em;文本对齐:居中;}.item:before {内容:计数器(数量);反增量:数量;}身体 {字体:400 14px 'Arial',无衬线;计数器重置:数量;}

<div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div>

<小时>

您只需将 if(i%2!=1) 更改为 if(i%2!=0)

即可轻松切换到另一个方向>

//元素总数var elems = document.querySelectorAll('.item');var n_t = elems.length;//元素的宽度var w = parseInt(document.querySelector('.item').offsetWidth);//带边距的元素全宽var m = document.querySelector('.item').currentStyle ||window.getComputedStyle(document.querySelector('.item'));w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);//容器宽度var w_c = parseInt(document.querySelector('.flex-container').offsetWidth);//容器填充var c = document.querySelector('.flex-container').currentStyle ||window.getComputedStyle(document.querySelector('.flex-container'));var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);var 调整 = 函数(){//只有容器的宽度会改变w_c = parseInt(document.querySelector('.flex-container').offsetWidth);//列数nb = Math.min(parseInt((w_c - p_c)/w),n_t);//行数nc = Math.ceil(n_t/nb);for(var j = 0;j<nb;j++) {for(var i = 0;i<nc;i++) {if(j + i*nb >= n_t)/* 如果我们达到元素的数量,我们就退出*/休息elems[j + i*nb].style.marginLeft='10px';/*我们保留边距*/如果(i%2!=0)elems[j + i*nb].style.order=j + i*nb;/* 正常流 */别的 {elems[j + i*nb].style.order=(nb - j) + i*nb;/* 逆流 *//*边距修复*/if(i == (nc - 1) && (j + i*nb == (n_t - 1)) && j<(nb-1)) {elems[j + i*nb].style.marginLeft=((nb*nc - n_t)*w + 10) + 'px';}}}}}调整()window.addEventListener('resize', function(){adjust()})

.flex-container {背景:橙色;显示:弹性;弹性包裹:包裹;}.物品 {背景:紫色;高度:80px;宽度:80px;边距:10px;行高:80px;网格间隙:20px;白颜色;字体粗细:粗体;字体大小:2em;文本对齐:居中;}.item:before {内容:计数器(数量);反增量:数量;}身体 {字体:400 14px 'Arial',无衬线;计数器重置:数量;}

<div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div>

I would like to find a way to have elements in a single container wrap to a new line going the opposite direction of the previous line, like a snake curving back on itself. I have not been able to achieve this result with flexbox and any combination of flex-direction and flex-wrap properties.

Image comparison of flexbox result vs desired result:

And here is a snippet showing the flexbox result vs desired result (faked):

body {
  font: 400 14px 'Arial', sans-serif;
}

.title {
  font-size: 1em;
  padding: 20px 0;
}

.title:first-child {
  padding: 0 0 20px;
}

.flex-container, .fake-container, .fake-row {
  width: 500px;
  background: orange;
  display: flex;
}

.flex-container {
  flex-flow: row wrap; /* any combination of row/reverse & wrap/reverse */
}

.item {
  display: inline-block;
  background: purple;
  width: 80px;
  max-width: 80px;
  height: 80px;
  margin: 10px;
  line-height: 80px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}

.fake-container {
  flex-flow: column nowrap;
}

.fake-row {
  flex-flow: row wrap;
  height: 100px;
}

.fake-row:nth-child(2) {
  flex-flow: row-reverse wrap;
}

<div class="title">Flexbox result:</div>
<div class="flex-container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
</div>

<div class="title">Desired result (faked):</div>
<div class="fake-container">
  <div class="fake-row">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
  </div>
  <div class="fake-row">
    <div class="item">6</div>
    <div class="item">7</div>
  </div>
</div>

As much as I would love to find a pure CSS solution to achieve this "snake wrap", I am starting to doubt its possibility. I'm sure a javascript solution could be employed, but am unsure how to proceed in creating one without making things overly-complicated.

Is there any simple solution out there? Thank you for your time.

解决方案

A CSS grid solution similar to this one that works with a fixed number of elements per row (aka fixed number of columns).

.flex-container {
  width: 500px;
  background: orange;
  display: grid;
  grid-template-columns:repeat(5,1fr); /*define the number of column*/
  grid-auto-flow:dense; /* this is important to fill all the space*/
  grid-gap:20px;
  padding:10px;
}

.item {
  background: purple;
  height: 80px;
  line-height: 80px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}

.item:nth-child(10n + 6)  {grid-column:5}
.item:nth-child(10n + 7)  {grid-column:4}
.item:nth-child(10n + 8)  {grid-column:3}
.item:nth-child(10n + 9)  {grid-column:2}
/*.item:nth-child(10n + 10) {grid-column:1} not needed*/
/* For N = number of columns 
  .item:nth-child((2xN)n + (N+1)) { grid-column:N; }
  .item:nth-child((2xN)n + (N+2)) { grid-column:(N-1); }
  ....
  .item:nth-child((2xN)n + (2xN)) { grid-column:1; }

*/

.item:before {
  content:counter(num);
  counter-increment:num;
}
body {
  font: 400 14px 'Arial', sans-serif;
  counter-reset:num;
}

<div class="flex-container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

If you want a generic solution here is one using JS and flexbox. The trick is to set the order property based on the row position of each item. I will rely on this old answer to do most of the calculation in order to find the number of rows/columns:

//total number of element
var elems = document.querySelectorAll('.item');
var n_t = elems.length;
//width of an element
var w = parseInt(document.querySelector('.item').offsetWidth);
//full width of element with margin
var m = document.querySelector('.item').currentStyle || window.getComputedStyle(document.querySelector('.item'));
w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);
//width of container
var w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
//padding of container
var c = document.querySelector('.flex-container').currentStyle || window.getComputedStyle(document.querySelector('.flex-container'));
var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);


var adjust = function(){
   //only the width of container will change
   w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
   //Number of columns
   nb = Math.min(parseInt((w_c - p_c) / w),n_t);
   //Number of rows
   nc = Math.ceil(n_t/nb);
   for(var j = 0;j<nb;j++) {
     for(var i = 0;i<nc;i++) {
       if(j + i*nb >= n_t) /* we exit if we reach the number of elements*/
        break
       if(i%2!=1) 
         elems[j + i*nb].style.order=j + i*nb; /* normal flow */
       else
         elems[j + i*nb].style.order=(nb - j) + i*nb; /* opposite flow */
     }
    }
}

adjust()
window.addEventListener('resize', function(){adjust()})

.flex-container {
  background: orange;
  display: flex;
  flex-wrap:wrap;
}

.item {
  background: purple;
  height: 80px;
  width:80px;
  margin:10px;
  line-height: 80px;
  grid-gap:20px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}


.item:before {
  content:counter(num);
  counter-increment:num;
}
body {
  font: 400 14px 'Arial', sans-serif;
  counter-reset:num;
}

<div class="flex-container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

There is a little issue with the last line in some cases when it's not full of item. You can rectify this by adding some margin to the last element

//total number of element
var elems = document.querySelectorAll('.item');
var n_t = elems.length;
//width of an element
var w = parseInt(document.querySelector('.item').offsetWidth);
//full width of element with margin
var m = document.querySelector('.item').currentStyle || window.getComputedStyle(document.querySelector('.item'));
w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);
//width of container
var w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
//padding of container
var c = document.querySelector('.flex-container').currentStyle || window.getComputedStyle(document.querySelector('.flex-container'));
var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);


var adjust = function(){
   //only the width of container will change
   w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
   //Number of columns
   nb = Math.min(parseInt((w_c - p_c) / w),n_t);
   //Number of rows
   nc = Math.ceil(n_t/nb);
   for(var j = 0;j<nb;j++) {
     for(var i = 0;i<nc;i++) {
       if(j + i*nb >= n_t) /* we exit if we reach the number of elements*/
        break
       elems[j + i*nb].style.marginLeft='10px'; /*we rest the margin*/
       if(i%2!=1) 
         elems[j + i*nb].style.order=j + i*nb; /* normal flow */
       else {
         elems[j + i*nb].style.order=(nb - j) + i*nb; /* opposite flow */
         /*margin fix*/
         if(i == (nc - 1) && (j + i*nb == (n_t - 1)) && j<(nb-1)) {
            elems[j + i*nb].style.marginLeft=((nb*nc - n_t)*w + 10) + 'px';
         }
       }
     }
    }
}

adjust()
window.addEventListener('resize', function(){adjust()})

.flex-container {
  background: orange;
  display: flex;
  flex-wrap:wrap;
}

.item {
  background: purple;
  height: 80px;
  width:80px;
  margin:10px;
  line-height: 80px;
  grid-gap:20px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}


.item:before {
  content:counter(num);
  counter-increment:num;
}
body {
  font: 400 14px 'Arial', sans-serif;
  counter-reset:num;
}

<div class="flex-container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>


You can easily switch to the other direction by simply changing if(i%2!=1) to if(i%2!=0)

//total number of element
var elems = document.querySelectorAll('.item');
var n_t = elems.length;
//width of an element
var w = parseInt(document.querySelector('.item').offsetWidth);
//full width of element with margin
var m = document.querySelector('.item').currentStyle || window.getComputedStyle(document.querySelector('.item'));
w = w + parseInt(m.marginLeft) + parseInt(m.marginRight);
//width of container
var w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
//padding of container
var c = document.querySelector('.flex-container').currentStyle || window.getComputedStyle(document.querySelector('.flex-container'));
var p_c = parseInt(c.paddingLeft) + parseInt(c.paddingRight);


var adjust = function(){
   //only the width of container will change
   w_c = parseInt(document.querySelector('.flex-container').offsetWidth);
   //Number of columns
   nb = Math.min(parseInt((w_c - p_c) / w),n_t);
   //Number of rows
   nc = Math.ceil(n_t/nb);
   for(var j = 0;j<nb;j++) {
     for(var i = 0;i<nc;i++) {
       if(j + i*nb >= n_t) /* we exit if we reach the number of elements*/
        break
       elems[j + i*nb].style.marginLeft='10px'; /*we rest the margin*/
       if(i%2!=0) 
         elems[j + i*nb].style.order=j + i*nb; /* normal flow */
       else {
         elems[j + i*nb].style.order=(nb - j) + i*nb; /* opposite flow */
         /*margin fix*/
         if(i == (nc - 1) && (j + i*nb == (n_t - 1)) && j<(nb-1)) {
            elems[j + i*nb].style.marginLeft=((nb*nc - n_t)*w + 10) + 'px';
         }
       }
     }
    }
}

adjust()
window.addEventListener('resize', function(){adjust()})

.flex-container {
  background: orange;
  display: flex;
  flex-wrap:wrap;
}

.item {
  background: purple;
  height: 80px;
  width:80px;
  margin:10px;
  line-height: 80px;
  grid-gap:20px;
  color: white;
  font-weight: bold;
  font-size: 2em;
  text-align: center;
}


.item:before {
  content:counter(num);
  counter-increment:num;
}
body {
  font: 400 14px 'Arial', sans-serif;
  counter-reset:num;
}

<div class="flex-container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

这篇关于Flexbox:包装/“蛇包装"上的反向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆