itsource

너비가 다른 임의의 수의 요소를 래핑을 사용하여 그리드에 정렬

mycopycode 2022. 8. 17. 23:56
반응형

너비가 다른 임의의 수의 요소를 래핑을 사용하여 그리드에 정렬

편집

아래 @user943702의 답변을 받았습니다.아래 스니펫과 같이 Vue 구현과 연동하기 위해 약간 수정이 필요했습니다.

const theElements = [{
  name: "ele1",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }, {
    name: 4
  }, {
    name: 5
  }]
}, {
  name: "ele2",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele3",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele4",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele5",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele6",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele7",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele8",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele9",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele10",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele11",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }, {
    name: 4
  }, {
    name: 5
  }]
}, {
  name: "ele12",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}];

new Vue({
  el: '#ele-grid',
  data: {
    elements: theElements
  },
  methods: {
    // find the first grid line excess {max}
    // return index; -1 means no overflow
    firstoverflowline: function(cols, max) {
      var sum = 0;
      for (var i = 0; i<cols.length; ++i) {
        sum += cols[i];
        if (sum >= max)
          return i;
      }
      return -1;
    },
    // compute max no of columns in grid
    // use by `grid-template-columns:repeat(<max>, max-content)`
    computegridlines: function(container) {
      var cols = getComputedStyle(container).gridTemplateColumns.split(/\s+/).map(parseFloat);
      var x = this.firstoverflowline(cols, parseFloat(getComputedStyle(container).width));
      if (x == -1) return;
      container.style.gridTemplateColumns = `repeat(${x}, max-content)`;
      this.computegridlines(container);
    },
    // polyfill `width:max-content`
    maxcontent: function(container) {
      var items = Array.from(container.children);
      for(var i = 0; i < items.length; i++) {
      	var item = items[i];
        item.style.display = "flex";
        item.style.flexFlow = "column";
        item.style.alignItems = "start";
        var max = Array.from(item.children).reduce(function(max,item) {
          var {left, right} = item.getBoundingClientRect();
          return Math.max(max, right - left);
        }, 0);
        item.style.width = `${max}px`;
      }
    },
    // flex-grid-ify a container
    flexgrid: function(container) {
      container.style.display = `grid`;
      container.style.gridTemplateColumns = `repeat(${container.children.length}, max-content)`;
      this.computegridlines(container);
      this.maxcontent(container);
    }
  },
  mounted: function() {
    var container = document.getElementById('ele-grid');
    var _this = this;
    this.flexgrid(container);
    window.onresize = function(e) { _this.flexgrid(container); }
  }
});
#ele-grid {
  width:100vw;
}

.ele-card {
  border: 1px solid black;
  background: cyan;
  margin: 5px 3px;
}
.ele-card .children {
  display: flex;
  flex-wrap: nowrap;
  padding: 5px;
}
.ele-card .child {
  margin: 0 5px;
  width: 30px;
  height: 30px;
  text-align: center;
  line-height: 30px;
  border: 1px solid black;
  background: magenta;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.min.js"></script>
<div id="ele-grid">
  <div class="ele-card" v-for="ele in elements" :key="ele.name">
      <div class="element">{{ele.name}}</div>
      <div class="children">
        <div class="child" v-for="child in ele.children" :key="child.name">{{child.name}}</div>
      </div>
  </div>
</div>


다른 폭을 가질 수 있는 요소의 개수를 알 수 없습니다.이 요소들을 그리드에 정렬하여 왼쪽이 각 열에 정렬되도록 합니다.또한 창이 작을 때 요소를 랩하고 그리드를 유지했으면 합니다.아래 이미지에서 내가 원하는 것을 조롱했다.

여기에 이미지 설명 입력

여기에 이미지 설명 입력

VueJs 2를 사용하여 요소를 그리드에 채우고 CSS Flexbox를 사용하여 다음 CSS를 사용하여 요소를 정리합니다.다음은 현재 기능하는 방법의 일부입니다.

const theElements = [{
  name: "ele1",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }, {
    name: 4
  }, {
    name: 5
  }]
}, {
  name: "ele2",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele3",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele4",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele5",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele6",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele7",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele8",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele9",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele10",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}, {
  name: "ele11",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }, {
    name: 4
  }, {
    name: 5
  }]
}, {
  name: "ele12",
  children: [{
    name: 1
  }, {
    name: 2
  }, {
    name: 3
  }]
}];

new Vue({
  el: '#ele-grid',
  data: {
    elements: theElements
  }
});
#ele-grid {
  display: flex;
  flex-wrap: wrap;
}
.ele-card {
  border: 1px solid black;
  background: cyan;
  margin: 5px 3px;
}
.ele-card .children {
  display: flex;
  flex-wrap: nowrap;
  padding: 5px;
}
.ele-card .child {
  margin: 0 5px;
  width: 30px;
  height: 30px;
  text-align: center;
  line-height: 30px;
  border: 1px solid black;
  background: magenta;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.min.js"></script>
<div id="ele-grid">
  <div class="ele-card" v-for="ele in elements" :key="ele.name">
    <div class="element">{{ele.name}}</div>
    <div class="children">
      <div class="child" v-for="child in ele.children" :key="child.name">{{child.name}}</div>
    </div>
  </div>
</div>

이것은 거의 기능합니다.창의 크기를 조정할 때 요소는 각각 고유한 너비와 랩을 가집니다.그러나 요소는 그리드에 정렬되지 않습니다.

CSS 그리드를 사용하는 것도 검토했습니다만, 각 요소의 너비 또는 열의 수를 지정해야 합니다.둘 다 임의여야 합니다.

CSS 또는 JavaScript를 사용하는 솔루션(JQuery가 아닌)은 모두 사용할 수 있습니다.서드파티 라이브러리는 포함하지 않는 것이 좋지만, 그것이 유일한 옵션이라면 검토해 보겠습니다.

편집:

  • @user943702가 지적한 바와 같이 각 열의 불필요한 공간을 제거하기 위해 이 속성을 사용할 수 있습니다(이 속성은 요소당 폭 값이고 이것은 열당 폭 값이지만 설명에 나오는 것과 혼동하지 마십시오).
  • 공간 분배의 경우: I've selected to set it to center라는 편리한 속성이 있습니다.다른 값 중에서도 다음과 같이 설정할 수 있습니다.

    space-between; /* The first item is flush with the start,the last is flush with the end */
    space-around;  /* Items have a half-size space on either end */
    space-evenly;  /* Items have equal space around them */
    stretch;       /* Stretch 'auto'-sized items to fitthe container */
    

스크립트에 들어가기 전에 몇 가지 주의사항이 있습니다.

  • 할 수 .css 을 、 css 、 you you you you 。@media query되지만 각 이므로 JavaScript를

편집: 이 스크립트는 다른 디바이스 폭으로 커스터마이즈하려고 하면 할수록 개개의 요소의 폭이 예기치 않게 변경되었을 때 잡힐 위험이 커집니다.

const theElements = [{  name: "ele1",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }, {    name: 4  }, {    name: 5  }]}, {  name: "ele2",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele3",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele4",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele5",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele6",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele7",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele8",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele9",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele10",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele11",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }, {    name: 4  }, {    name: 5  }]}, {  name: "ele12",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}];

new Vue({
  el: '#ele-grid',
  data: {
    elements: theElements
  }
});
@media (min-width: 1020px) {
	#ele-grid {
		display:grid;
		grid-template-columns:repeat(5, 1fr); 	
        justify-content: center;
	}
}
@media (min-width:400px) and (max-width: 1020px) {
	#ele-grid {
		display:grid;
		grid-template-columns:max-content max-content max-content; 	
	}
}
@media (max-width: 400px) {
	#ele-grid {
		display:grid;
		grid-template-columns:max-content; 	
	}
}
.ele-card {
  margin: 5px 3px;
}
.ele-card .children {
  display: flex;
  flex-wrap: nowrap;
  padding: 5px;
}
.ele-card .child {
  margin: 0 5px;
  width: 30px;
  height: 30px;
  text-align: center;
  line-height: 30px;
  border: 1px solid black;
  background: magenta;
}
.wrapper{
	border: 1px solid black;
  background: cyan;
  display:inline-block;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.min.js"></script>

<div id="ele-grid">
  <div class="ele-card" v-for="ele in elements" :key="ele.name">
  	<div class="wrapper">
	    <div class="element">{{ele.name}}</div>
	    <div class="children">
	      <div class="child" v-for="child in ele.children" :key="child.name">{{child.name}}</div>
	    </div>
	  </div>
	</div>
</div>

  • 필요한 폭을 얻기 위해 아쉽게도 다른 브라우저에서는 아직 지원되지 않는 우수한 -moz-max-content 속성이 있기 때문에 자녀용 래퍼를 추가하여 만들었습니다.display:inline-block의도된 행동을 가지고 있다.
  • 사용 중이고 css column 또는 vertical을 사용할 수 있습니다.flex대신 요소가 위에서 아래로 정렬되어 전체 레이아웃이 변경됩니다.

이것은 css용, JavaScript용입니다.

  • 간단히 말해 이 스크립트는 최대 열(여기서는 10개까지 늘릴 수 있음)을 가진 레이아웃을 취하여 스크롤하지 않고 적합한지, 감소하지 않는지를 확인합니다.
  • 이 스크립트에서는 크기 조정 이벤트를 사용하여 요소가 응답합니다.

const theElements = [{  name: "ele1",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }, {    name: 4  }, {    name: 5  }]}, {  name: "ele2",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele3",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele4",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele5",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele6",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele7",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele8",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele9",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele10",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}, {  name: "ele11",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }, {    name: 4  }, {    name: 5  }]}, {  name: "ele12",  children: [{    name: 1  }, {    name: 2  }, {    name: 3  }]}];

new Vue({
  el: '#ele-grid',
  data: {
    elements: theElements
  }
});
function resizeHandler(){
	colStart=10; 		// max number of columns to start with
	allCards= document.getElementsByClassName('wrapper');
	totalWidth=0;
	maxWidTab=[];
	for (i=colStart;i>0;i--){
		for(j=0;j<i; j++){									//initializing and resetting
			maxWidTab[j]=0;
		}
		for (j=0; j<allCards.length; j++){
			cellWidth=parseInt(getComputedStyle(allCards[j]).width);		//parseInt to remove the tailing px
			maxWidTab[j%i]<cellWidth?maxWidTab[j%i]=cellWidth:'nothing to be done';
		}
		for(j=0;j<i; j++){									//sum to see if fit
			totalWidth+=maxWidTab[j]+2+6		//borders and margins
		}
		if (totalWidth<innerWidth){
			grEl = document.getElementById("ele-grid");
			grEl.style.gridTemplateColumns="repeat("+i+", max-content)";
			/*console.log(i);*/
			break;
		}else{
				totalWidth=0;							//resetting
		}
	}
}
	window.addEventListener("resize",resizeHandler);
	document.addEventListener ("DOMContentLoaded",resizeHandler);
#ele-grid {
	display:grid;
    justify-content: center;
	grid-template-columns:repeat(10, max-content); 	/* starting by 10 columns*/
}
.ele-card {

  margin: 5px 3px;
}
.ele-card .children {
  display: flex;
  flex-wrap: nowrap;
  padding: 5px;
}
.ele-card .child {
  margin: 0 5px;
  width: 30px;
  height: 30px;
  text-align: center;
  line-height: 30px;
  border: 1px solid black;
  background: magenta;
}
.wrapper{
	border: 1px solid black;
  background: cyan;
  display:inline-block;
}

</style>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.min.js"></script>

<div id="ele-grid">
  <div class="ele-card" v-for="ele in elements" :key="ele.name">
  	<div class="wrapper">
	    <div class="element">{{ele.name}}</div>
	    <div class="children">
	      <div class="child" v-for="child in ele.children" :key="child.name">{{child.name}}</div>
	    </div>
	  </div>
	</div>
</div>

CSSgrid-template-columns다음과 같은 콘텐츠 인식 값을 지원합니까?max-content유일한 문제는 몇 개의 컬럼이 있어야 하는가 하는 것입니다.

나는 최대 열 수를 탐색하는 알고리즘을 쓴다.구현에는 JS가 포함되며 CSS 그리드를 지원하려면 브라우저가 필요합니다.데모는 이쪽에서 보실 수 있습니다(Pug를 사용하여 고객님과 동일한 소스 구조를 만들고 스타일링도 고객님과 동일하기 때문에 구현인 JS패널에 집중할 수 있습니다).

데모에서 뷰포트 크기를 변경하면 그리드 항목이 다시 흐릅니다.를 호출하여 다른 중요한 순간에 수동으로 재플로우를 트리거할 수 있습니다.flexgrid(container)예를 들어, 항목을 비동기적으로 로드한 후 다시 플로우합니다.소스 구조가 변경되지 않는 한 항목의 치수 속성을 변경할 수 있습니다.

알고리즘은 다음과 같습니다.

Step1) 컨테이너를 그리드 포맷 컨텍스트로 설정하고 모든 그리드 항목을 한 행에 레이아웃하고 각 열 너비를 다음과 같이 설정합니다.max-content

|---container---|
|aaaaa|bbb|ccc|ddd|eee|fff|ggggg|hhh|iii|

스텝 2) 첫 번째 오버플로 그리드 선 찾기

|---container---|
|aaaaa|bbb|ccc|ddd|eee|fff|ggggg|hhh|iii|
                  ^overflowed

순서 3) 삭감grid-template-columns우리의 경우 3. 그 이후로grid-row디폴트autoCSS 엔진은 마지막 열 그리드 선을 넘어 다음 행에 그리드 항목을 레이아웃합니다.나는 이것을 "포장"이라고 한다.또한 그리드 항목은 다음과 같은 이유로 자동 확장됩니다.grid-template-columns:max-content(예: "ddd"는 첫 번째 열의 가장 넓은 콘텐츠 길이로 확장됩니다.)

|---container---|
|aaaaa|bbb|ccc|
|ddd  |eee|fff|
|ggggg|hhh|iii|

모든 열 그리드 라인이 "내부" 컨테이너에 있으므로 이 작업을 완료했습니다.일부의 경우, 새로운 오버플로우 그리드 라인이 "랩핑" 후에 도입되고 있습니다.모든 그리드 라인이 "내부" 컨테이너에 배치될 때까지 2&3단계를 반복해야 합니다.

#layout in one row
|---container---|
|aaaaa|bbb|ccc|ddd|eee|fff|ggggggg|hhhhh|iii|

#find the first overflowed grid line
|---container---|
|aaaaa|bbb|ccc|ddd|eee|fff|ggggggg|hhhhh|iii|
                  ^overflowed

#reduce `grid-template-columns`
|---container---|
|aaaaa  |bbb  |ccc|
|ddd    |eee  |fff|
|ggggggg|hhhhh|iii|

#find the first overflowed grid line
|---container---|
|aaaaa  |bbb  |ccc|
|ddd    |eee  |fff|
|ggggggg|hhhhh|iii|
                  ^overflowed

#reduce `grid-template-columns`
|---container---|
|aaaaa  |bbb  |
|ccc    |ddd  |
|eee    |fff  |
|ggggggg|hhhhh|
|iii    |

#find the first overflowed grid line
#None, done.

언급URL : https://stackoverflow.com/questions/47676820/align-arbitrary-number-of-elements-with-different-widths-to-a-grid-with-wrapping

반응형