有没有办法在 CSS/HTML/JS 中以 triangular/pyramid 的方式生成六边形?

Is there a way to generate Hexagons in a triangular/pyramid fashion in CSS/HTML/JS?

我正试图开始我的一个个人项目,我想在特定的 Pattern 中生成六边形,并且每个新行都是根据用户输入生成的。我浏览了所有 Whosebug 的答案,每个人似乎都直接在网站上制作了整个网格或只是 svg 图案。我的问题是我想为每个六边形制作动画并需要访问每个六边形元素。有没有办法在 CSS/HTML/JS 中或通常使用其他语言来做到这一点。

您可以结合使用 CSS 剪辑路径和对几何的基本了解来获得正确的位置。假设要定义单个六边形的宽度,那么高宽比为1.157...(准确的说是2 / Math.sqrt(3)),但是为了方便我们将其四舍五入为1.2计算。

六边形(长对角线垂直)的CSS剪切路径是:

clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);

然后就是应用这些计算来获得正确的布局。我在下面的示例中使用了 CSS 自定义属性,因此您可以在需要时调整位置。

.stage {
  --cell-width: 100px;
  --cell-height: calc(var(--cell-width) * 1.2);
  --cell-spacing: 4px;
  
  padding-top: calc(var(--cell-width) * 0.25);
  padding-bottom: calc(var(--cell-width) * 0.25);
  background-color: #ddd;
}

.row {
  display: flex;
  justify-content: center;
  margin-top: calc(var(--cell-width) * -0.27 + var(--cell-spacing));
  margin-bottom: calc(var(--cell-width) * -0.27 + var(--cell-spacing));
}

.cell {
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  background-color: steelblue;
  width: var(--cell-width);
  height: var(--cell-height);
  margin-left: var(--cell-spacing);
  margin-right: var(--cell-spacing);
}
<div class="stage">
  <div class="row">
    <div class="cell"></div>
  </div>
  <div class="row">
    <div class="cell"></div>
    <div class="cell"></div>
  </div>
  <div class="row">
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
  </div>
  <div class="row">
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
    <div class="cell"></div>
  </div>
</div>

您可以使用 clip-path 多边形将元素制作成六边形。

然后根据用户输入,您可以创建任意数量的六边形并将它们并排放置在新行中。

此代码段是一个简化版本,它通过点击创建一个六边形并将其排成一行。

它使用CSS变量来定义六边形的边长,因此您可以轻松更改。

const row = document.querySelector('.row');

function createHexagon() {
  const hexagon = document.createElement('div');
  hexagon.classList.add('hexagon');
  return hexagon;
}
.hexagon {
  --sin30: 0.5;
  --side: 10vmin;
  --x: calc(var(--side) * var(--sin30));
  background-color: gray;
  clip-path: polygon(var(--x) 0, calc(100% - var(--x)) 0, 100% 50%, calc(100% - var(--x)) 100%, var(--x) 100%, 0% 50%);
  width: calc(2 * var(--side));
  height: calc(2 * var(--side));
  display: inline-block;
  margin: 1vmin;
}
<div class="row"></div>
<button onclick="row.appendChild(createHexagon());">Click to add a hexagon</button>

您可以将模式存储到数组中,然后创建动态循环遍历该数组的元素。

只需创建一个容器 div 然后动态创建行并将六边形放入行中然后将行追加到容器中。

const container = document.querySelector('.container');
const hexagonPattern = [1, 2, 3, 4];

for (let i = 0; i < hexagonPattern.length; i++) {
    const row = document.createElement('div');
    row.classList.add('row');
     for (let j = 0; j < hexagonPattern[i]; j++) {
          const hexagon = document.createElement('div');
          hexagon.classList.add('hexagon');
          row.appendChild(hexagon);
      }
      container.appendChild(row);
}
.container {
    display: flex;
     flex-direction: column;
     align-items: center;
   }
     
   .row {
     margin-bottom: -30px;
   }
   
   .hexagon {
     display: inline-block;
     box-shadow: 10px 10px 5px #000;
     width: 100px; 
     height: 100px;
     background: grey;
     -webkit-clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
     transition: .7s;
     margin: 2px;
   }
   .hexagon:hover {
     background: red;
     transform: rotateY(-180deg);
     transition: .7s;
   }
  <div class="container">
  </div>

这是我写的文章中的一个演示:https://css-tricks.com/hexagons-and-beyond-flexible-responsive-grid-patterns-sans-media-queries/

它是响应式八边形网格。 运行 它在全屏上看到金字塔网格,在小屏幕上会退回到正常的网格。我邀请您阅读以上文章以了解此技术背后的技术细节。

您所要做的就是调整变量以获得您的六边形网格:

let inputs = document.querySelectorAll('input[type=range]')
let elem = document.querySelector('.main')


inputs.forEach(input => {
   input.addEventListener('change', function(e) {
      var p = e.target.getAttribute('name');
      if(p=="s" || p=="mv") {
        elem.style.setProperty("--"+p, this.value+"px");
            e.target.previousElementSibling.innerHTML = this.value+"px";
      } else { 
        elem.style.setProperty("--"+p, this.value);
          e.target.previousElementSibling.innerHTML = this.value;
      }
    });
});
.main {
  display:flex;
  --s: 100px; /* size */
  --r: 1; /* ratio */
  /* clip-path */
  --h: 0.25;  
  --v: 0.35; 
  --hc:calc(clamp(0,var(--h),0.5) * var(--s)) ;
  --vc:calc(clamp(0,var(--v),0.5) * var(--s) * var(--r)); 
  
  /*margin */
  --mv: 4px; /* vertical */
  --mh: calc(var(--mv) + (var(--s) - 2*var(--hc))/2); /* horizontal */
  /* for the float*/
  --f: calc(2*var(--s)*var(--r) + 4*var(--mv)  - 2*var(--vc) - 2px);
  --nr:6;
  --lw:calc(var(--nr)*(var(--s) + 2*var(--mh)));
}

.container {
  font-size: 0; /*disable white space between inline block element */
  max-width:var(--lw);
  margin:0 auto;
}

.container div {
  width: var(--s);
  margin: var(--mv) var(--mh);
  height: calc(var(--s)*var(--r)); 
  display: inline-block;
  font-size:initial;
  clip-path: polygon(var(--hc) 0, calc(100% - var(--hc)) 0,100% var(--vc),100% calc(100% - var(--vc)), calc(100% - var(--hc)) 100%,var(--hc) 100%,0 calc(100% - var(--vc)),0 var(--vc));
  background: red;
  margin-bottom: calc(var(--mv) - var(--vc)); 
}
.container div:nth-child(odd) {
  background:green;
}


.container::before {
  content: "";
  width: clamp(0px, (var(--lw) - 100%)*1000,calc(var(--s)/2 + var(--mh)));
  float: left;
  height: 120%; 
  shape-outside: repeating-linear-gradient(#0000 0 calc(var(--f) - 3px),#000 0 var(--f));
}

.container i::before ,
.container i::after{
  content: "";
  width: clamp(0px, (100% - var(--lw) + 1px)*1000,calc(50% - var(--mh) - var(--s)/2));
  float: left;
  height: calc(var(--f)*(var(--nr) - 1)/2);
  shape-outside: linear-gradient(to bottom right,#000 50.5%,#0000 0);
}
.container i::after {
  float:right;
  shape-outside: linear-gradient(to bottom left,#000 49%,#0000 0);
}

.panel {position: fixed;top: 20px;right: 20px;padding: 10px;border: 1px solid;border-radius: 10px;background: #fff;font-family: sans-serif;opacity:.5}
.panel:hover {opacity:1}
.panel > div:not(:last-child) {border-bottom: 1px solid;padding-bottom: 10px;margin-bottom: 10px;}
*,*::before {transition:0.5s linear}
<div class="main">
  <div class="container">
    <i></i>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>
</div>

<div class="panel">
<div>Size: [<span>100px</span>] <input type="range" min="20" max="200" step="10" value="100" name="s"></div>
<div>Ratio: [<span>1</span>] <input type="range" min="0" max="2" step="0.05" value="1" name="r"></div>
<div>Spacing: [<span>4px</span>]<input type="range" min="0" max="10" step="1" value="4" name="mv"></div>
<div>Clip-path<br>
hc: [<span>0.25</span>]<input type="range" min="0" max=".5" step=".05" value=".25" name="h"><br>
vc: [<span>0.35</span>]<input type="range" min="0" max=".5" step=".05" value=".35" name="v"></div></div>