有没有办法在 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>
我正试图开始我的一个个人项目,我想在特定的 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>