具有完美正方形单元格的响应式网格布局

huangapple go评论72阅读模式
英文:

Responsive grid layout with perfectly squared cells

问题

我有一个<section>元素,其大小不能超过100vh,宽度不能超过100vw。理想情况下,它应该遵循其父容器的尺寸。

<section>应该使用CSS变量具有网格布局; var(--cols, 56)var(--rows, 32)

网格中的每个单元格必须是1:1的比例,也就是说单元格的宽度必须始终等于单元格的高度。

<section>元素应该在视口(父元素)增大时增长,除非这会使单元格不是正方形。如果是这种情况,<section>元素应该居中。因此,如果视口宽度变大而视口高度变小,<section>会水平居中。但如果视口高度变大而视口宽度变小,<section>元素会垂直居中。

到目前为止,我有以下内容:

<section #widgets>
  <ng-container *ngFor="let widget of widgetConfig">
    <ng-container [libTileContainer]="widget"></ng-container>
  </ng-container>
</section>
section {
    display: grid;
    gap: 5px;
    grid-template-columns: repeat(
      var(--cols, 56),
      calc(((100vw - (var(--cols, 56) * 5px) - 2rem) / (var(--cols, 56))))
    );
    grid-template-rows: repeat(
      var(--rows, 32), 
      calc(((100vw - (var(--cols, 56) * 5px) - 2rem) / (var(--cols, 56))))
    );
    max-width: 100%;
    max-height: 100%;
  }

这尊重了单元格的正方形性,但它会超出其父元素。计算也仅考虑视口的宽度而不考虑高度。

我该如何实现我想要的效果?

英文:

I have a <section> element which must not be larger than 100vh and no wider than 100vw. Ideally it should follow its parent containers dimensions.

The <section> should have a grid layout using css variables; var(--cols, 56) and var(--rows, 32)

Each cell in the grid must be 1:1 ratio, meaning that the cell width must always === cell height.

The <section> element should grow if the viewport (parent element) grows, unless this makes the cells not square. If this is the case, the <section> element should be centered. So if the viewport width becomes larger and viewport height becomes smaller, the <section> is centered horizontally. But if the viewport height becomes larger and viewport width becomes smaller, the <section> element is centered vertically.

This is what I have so far:

<section #widgets>
  <ng-container *ngFor="let widget of widgetConfig">
    <ng-container [libTileContainer]="widget"></ng-container>
  </ng-container>
</section>
section {
    display: grid;
    gap: 5px;
    grid-template-columns: repeat(
      var(--cols, 56),
      calc(((100vw - (var(--cols, 56) * 5px) - 2rem) / (var(--cols, 56))))
    );
    grid-template-rows: repeat(
      var(--rows, 32), 
      calc(((100vw - (var(--cols, 56) * 5px) - 2rem) / (var(--cols, 56))))
    );
    max-width: 100%;
    max-height: 100%;
  }

This respects the cells squareness, but it bleeds out of it's parent.
The calculations will also only respect viewport width and not height.

How can I achieve what I want?

答案1

得分: 1

要设置网格项的高度和宽度,您可以使用一种策略,根据可用空间内应该容纳多少列和行来计算高度和宽度,然后在这两个属性上使用较小的那个值。

https://developer.mozilla.org/en-US/docs/Web/CSS/min

min() CSS函数允许您将逗号分隔的表达式列表中最小(最负)的值设置为CSS属性值。

在此演示中,我稍微重构了一些选择:

  • 我删除了默认列数的值,以简化代码。无论如何,它可以定义为其他自定义属性。
  • 我使用函数 min 来确定候选的高度和宽度中哪一个较小。
  • 我使用 place-content 来默认居中显示项目。
  • 我使用 grid-auto-rows 来确定行的大小,因为在这里不需要告诉有多少行。
  • 我告诉 div 使用其容器的整个宽度和高度。
<!-- 开始代码片段: js 隐藏: false 控制台: true babel: false -->

<!-- 语言: lang-js -->
const items = 20;
const cols = 5;
const rows = 4;

initSection(items, cols, rows);

function initSection(items, cols, rows){
  const parent = document.querySelector('section');
  for(i=0; i<items; i++){
    const item = document.createElement('div');
    item.innerText = i+1;
    parent.append(item);  
  }
  parent.dataset.col = cols;
  parent.dataset.col = rows;
}

<!-- 语言: lang-css -->
:root{
  --cols: 56;
  --rows: 32;
}

*{
  box-sizing: border-box;
}

html, body{
  margin: 0;
  overflow: hidden; /* 仅确保内容不会溢出视口 */
}

section::before{
  /* 这里显示相同的动态策略也起作用 */
  --cols: attr(data-cols);
  --rows: attr(data-rows);
  
  position: absolute;    
  content: var(--cols) 'x' var(--rows);
  border: dashed 4px gray;
  padding: 0 .2em;
  font-size: 1.5rem;
  top: .2em;
  left: .4em;
}

section {  
  /*! 这个不起作用 :( */  
  /*
  --cols: attr(data-cols number, 56);
  --row: attr(data-rows number, 32);
  */
      
  --cols: 5;
  --rows: 4;  
  
  --gap: 5px;
  --col-size: calc(((100vw - (var(--cols) * var(--gap))) / (var(--cols))));  
  --row-size: calc(((100vh - (var(--rows) * var(--gap))) / (var(--rows))));    
  --size: min(var(--col-size), var(--row-size));
  
  display: grid;
  place-content: center;
  gap: var(--gap);
  grid-template-columns: repeat( var(--cols), var(--size));
  /* grid-template-rows: repeat( var(--rows), var(--size)); */
  grid-auto-rows: var(--size);
  width: 100vw;
  height: 100vh;      
}

section{
  border: solid red;
}

section div {  
  border: solid;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 2rem;
}

<!-- 语言: lang-html -->

<section data-cols="5" data-rows="4">    
  <!-- 
    <div>1</div>
    ...
  -->
</section>

<!-- 结束代码片段 -->

注意:这是您提供的代码的翻译部分。

英文:

To set the height and width of grid items you might use a strategy that calculates both the height and width according to how many columns and rows are supposed to be fit inside the available space and then just use the lesser of the two on both the properties.

https://developer.mozilla.org/en-US/docs/Web/CSS/min

> The min() CSS function lets you set the smallest (most negative) value
> from a list of comma-separated expressions as the value of a CSS
> property value.

In this demo I slightly refactored some choices:

  • I removed the default value for the number of columns to simplify the
    code.. anyway it could defined as an other custom property
  • I use the function min to determine which one is lower between the
    candidates height and width
  • I used place-content to center the items by default
  • I used grid-auto-rows to determine the size of rows because it's not
    important here to tell also how many
  • I told the div to take the whole width and height of their container

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const items = 20;
const cols = 5;
const rows = 4;
initSection(items, cols, rows);
function initSection(items, cols, rows){
const parent = document.querySelector(&#39;section&#39;);
for(i=0;i&lt;items;i++){
const item = document.createElement(&#39;div&#39;);
item.innerText = i+1;
parent.append(item);  
}
parent.dataset.col = cols;
parent.dataset.col = rows;
}

<!-- language: lang-css -->

:root{
--cols: 56;
--rows: 32;
}
*{
box-sizing: border-box;
}
html, body{
margin: 0;
overflow: hidden; /*just to make sure the content doesn&#39;t overflow the viewport*/
}
section::before{
/*this to show that the same dynamic strategy here worked instead*/
--cols: attr(data-cols);
--rows: attr(data-rows);
position: absolute;    
content: var(--cols) &#39;x&#39; var(--rows);
border: dashed 4px gray;
padding: 0 .2em;
font-size: 1.5rem;
top: .2em;
left: .4em;
}
section {  
/*!this didn&#39;t work :(*/  
/*
--cols: attr(data-cols number, 56);
--row: attr(data-rows number, 32);
*/
--cols: 5;
--rows: 4;  
--gap: 5px;
--col-size: calc(((100vw - (var(--cols) * var(--gap))) / (var(--cols))));  
--row-size: calc(((100vh - (var(--rows) * var(--gap))) / (var(--rows))));    
--size: min(var(--col-size), var(--row-size));
display: grid;
place-content: center;
gap: var(--gap);
grid-template-columns: repeat( var(--cols), var(--size));
/*grid-template-rows: repeat( var(--rows), var(--size));*/
grid-auto-rows: var(--size);
width: 100vw;
height: 100vh;      
}
section{
border: solid red;
}
section div {  
border: solid;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 2rem;
}

<!-- language: lang-html -->

&lt;section data-cols=&quot;5&quot; data-rows=&quot;4&quot;&gt;    
&lt;!-- 
&lt;div&gt;1&lt;/div&gt;
...
--&gt;
&lt;/section&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年1月9日 17:24:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/75055247.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定