如何使用 CSS、JS 沿曲线对齐 HTML 元素(图标)

Posted

技术标签:

【中文标题】如何使用 CSS、JS 沿曲线对齐 HTML 元素(图标)【英文标题】:How to align HTML elements(icons) along the curve using CSS, JS 【发布时间】:2021-11-16 14:12:07 【问题描述】:

大家好,

无法找到沿曲线对齐这些 png 图标的方法。我正在为此寻找 CSS 或(和)javascript 解决方案。关于如何做到这一点的任何想法?

html

<div class="container">
  <div class="svg-curve">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 141">
      <path class="layer-1"
        d="M0,32c9.8,2.43,22.75,5.55,38,9,16,3.62,47.89,10.63,89,18,9,1.62,47.71,8.49,112,17,29.43,3.9,82.81,10.9,153,17,85.18,7.41,149.55,9.54,196,11,114.72,3.62,201,1.55,222,1,104.73-2.73,182.88-9.34,202-11,26-2.26,87.86-8,167-19,5.39-.75,32.57-5.1,66.52-10.53,22.55-3.61,45.94-7.39,52.48-8.47,27.81-4.59,72.7-13.43,142-32h0V0H0Z" />
      <path class="layer-2"
        d="M0,95c14.71,2.7,35.31,6.28,60,10,18.28,2.75,39.79,5.58,86.06,11,26.82,3.14,61.67,7.22,103.07,11,65.61,6,115.62,8.3,153.11,10,52.28,2.36,112.79,4,180.12,4,37.5,0,96.71-.13,175.12-4,28.61-1.4,91.33-4.87,172.12-13,65-6.54,130.95-13.26,217.15-29,55.91-10.19,98.43-20.15,123.09-26,67.74-16,125.3-32.33,170.12-46.07L1242.86,64.07h0c-59.38,10.1-139.89,21.83-236.16,30-13.44,1.13-52,4.4-106.08,7-129.63,6.2-240.37,5.09-326.22,2C295.06,93,113.9,56.09,71.05,47.09c-29.73-6.24-54.26-12-71-16Z" />
    </svg>
  </div>
</div>

CSS

    .container 
      margin:0;
      padding:0;
      position:relative;
    
    .svg-curve 
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      overflow: hidden;
    

    .svg-curve svg 
      position: relative;
      display: block;
      height: 200px;
    

    .svg-curve svg .layer-1 
      fill: red;
    

    .svg-curve svg .layer-2 
      fill: blue;
    

根据屏幕大小,SVG 高度会像这样改变:

@media (min-width: 576px) 
      .svg-curve svg 
        height: 90px;
      
    

    @media (min-width: 768px) 
      .svg-curve svg 
        height: 120px;
      
    

    @media (min-width: 992px) 
      .svg-curve svg 
        height: 150px;
      
    

    @media (min-width: 1400px) 
      .svg-curve svg 
        height: 200px;
      
    

【问题讨论】:

你需要一个方程来生成这样的曲线,然后使用计算出的坐标绝对定位图标。 你是怎么画曲线的?如果您有图像,请测量与图像相关的所有内容并以百分比形式定位所有图标 - 这样它就可以响应。但是如果你的曲线随着视口的变化而变化,那就不同了。包括到目前为止的代码,以便我们查看。 @AHaworth,它是一个 SVG 文件,是的,我认为它会随着视口而改变。 请向我们展示您的代码,包括 svg。这将取决于它如何变化。 @AHaworth 刚刚更新 【参考方案1】:

您可以使用合适的公式来计算绝对坐标。使用 CSS 无法做到这一点,因为您一开始就无法使用 CSS 定义曲线。

下面是示例代码:

function curve(x) 
  const dx = 400;
  const dy = 100;
    return -0.0004 * (x - dx) * (x - dx) + dy;


for (let i = 0; i < 10; i++) 
    const div = document.createElement("div");
  div.classList.add('icon');
  const x = i * window.innerWidth / 10;
  const y = curve(x);
  div.style.transform = `translate(0, $ypx)`;
  document.getElementById('curve').append(div);
#curve 
  position: relative;
  display: flex;
  justify-content: space-between;


#curve .icon 
  width: 30px;
  height: 30px;
  border: 1px solid grey;
&lt;div id="curve"&gt;&lt;/div&gt;

【讨论】:

克里斯,谢谢你!看起来这就是我想要的。曲线本身是一个 SVG 文件,无论如何我可以使用 SVG 或其路径生成用于定位的曲线? 理论上你可以从 SVG 中提取路径,解析它的坐标并使用它们。在实践中,SVG 将是固定大小或以线性方式改变大小,这两种情况都可以通过调整曲线方程来处理。 @Dave 编辑了我的答案以使其具有响应性。【参考方案2】:

如果 svg 完整显示,则可以相对于它完成整个布局 - 在 CSS 中计算每个图标的 % 位置。

这个 sn-p 有 svg 的纵横比和每个图标的位置从被测量。 CSS calc 然后产生 % 距离。尺寸在宽度上逐渐减小,图标之间的距离是恒定的。

此 sn-p 中的测量值并非绝对准确,仅用于此演示。你可能想自己做。

* 
  padding: 0;
  margin: 0;

    .container 
      --svgAspectRatio: calc(1440 / 141);
      margin:0;
      padding:0;
      position:relative;
      width: 100vw;
      height: calc(100vw / var(--svgAspectRatio) );
      display: flex;
      justify-content: center;
      
    
    .svg-curve 
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      overflow: hidden;
      line-height: 0;
      background: transparent;
    

    .svg-curve svg 
      position: relative;
      display: block;
      width: 100%;
    

    .svg-curve svg .layer-1 
      fill: red;
    

    .svg-curve svg .layer-2 
      fill: blue;
    

    .icons 
      --measuredW: 60.96; /* the measured width of the icons picture */
      --measuredH: 10.03;
      --w: calc(100vw - 8vw);
      width: var(--w);
      height: auto;
      position: relative;
      top: 0;
      left: 0;
      display: inline-block;
    
    .icon 
      --iconW: calc(((14 - var(--n)) / 13) * 2vw);
      width: var(--iconW);
      height: var(--iconW);
      background-color: #eeeeee; 
      display: inline-block;
      position: absolute;
      left: calc(100% * (var(--n) - 1) / 13);
      z-index: 1;
      background-size: contain;
      background-position: center center;
      background-repeat: no-repeat no-repeat;
      background-image: var(--bg);
      top: calc(var(--t) / var(--measuredH) * 100%);
      
      .icon:nth-child(1) 
      --n: 1;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 4.6;
      
      .icon:nth-child(2) 
      --n: 2;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 5.6;
      
      .icon:nth-child(3) 
      --n: 3;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 6.48;
      
      .icon:nth-child(4) 
      --n: 4;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 7.2;
      
      .icon:nth-child(5) 
      --n: 5;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 7.8;
      
      .icon:nth-child(6) 
      --n: 6;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 8.1;
      
      .icon:nth-child(7) 
      --n: 7;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 8.1;
      
      .icon:nth-child(8) 
      --n: 8;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 8.1;
      
      .icon:nth-child(9) 
      --n: 9;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 7.8;
      
      .icon:nth-child(10) 
      --n: 10;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 7.3;
      
      .icon:nth-child(11) 
      --n: 11;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 6.5;
      
      .icon:nth-child(12) 
      --n: 12;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 5.5;
      
      .icon:nth-child(13) 
      --n: 13;
      --bg: url(https://i.stack.imgur.com/DWx67.png);
      --t: 4.5;
      
<div class="container">
  <div class="svg-curve">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 141">
      <path class="layer-1"
        d="M0,32c9.8,2.43,22.75,5.55,38,9,16,3.62,47.89,10.63,89,18,9,1.62,47.71,8.49,112,17,29.43,3.9,82.81,10.9,153,17,85.18,7.41,149.55,9.54,196,11,114.72,3.62,201,1.55,222,1,104.73-2.73,182.88-9.34,202-11,26-2.26,87.86-8,167-19,5.39-.75,32.57-5.1,66.52-10.53,22.55-3.61,45.94-7.39,52.48-8.47,27.81-4.59,72.7-13.43,142-32h0V0H0Z" />
      <path class="layer-2"
        d="M0,95c14.71,2.7,35.31,6.28,60,10,18.28,2.75,39.79,5.58,86.06,11,26.82,3.14,61.67,7.22,103.07,11,65.61,6,115.62,8.3,153.11,10,52.28,2.36,112.79,4,180.12,4,37.5,0,96.71-.13,175.12-4,28.61-1.4,91.33-4.87,172.12-13,65-6.54,130.95-13.26,217.15-29,55.91-10.19,98.43-20.15,123.09-26,67.74-16,125.3-32.33,170.12-46.07L1242.86,64.07h0c-59.38,10.1-139.89,21.83-236.16,30-13.44,1.13-52,4.4-106.08,7-129.63,6.2-240.37,5.09-326.22,2C295.06,93,113.9,56.09,71.05,47.09c-29.73-6.24-54.26-12-71-16Z" />
    </svg>
  </div>
  <div class="icons">
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
  <div class="icon"></div>
</div>
</div>

【讨论】:

以上是关于如何使用 CSS、JS 沿曲线对齐 HTML 元素(图标)的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法用 CSS 对齐浮动 HTML 元素?

CSS:有没有办法在每个列表元素之前垂直对齐数字/项目符号?

CSS:有没有办法在每个列表元素之前垂直对齐数字/项目符号?

CSS:有没有办法在每个列表元素之前垂直对齐数字/项目符号?

html 笔记:CSS与SVG实现元素沿环形路径动画 - 3

html 笔记:CSS与SVG实现元素沿环形路径动画 - 1