【问题标题】:Render JSX-elements as multi-level pyramid将 JSX 元素渲染为多级金字塔
【发布时间】:2023-03-11 12:06:01
【问题描述】:

我需要创建一个具有 4 级划分的金字塔图形,如下所示:

虽然我用下面的代码集实现了同样的效果:

const PyramidChart = () => {
  return (
    <div className="d-flex flex-column align-items-center pyramid_wrap">
      <div className="category_one">
        <h6>2</h6>
      </div>
      <div className="category_two">
        <h6>8</h6>
      </div>
      <div className="category_three">
        <h6>11</h6>
      </div>
      <div className="category_four">
        <h6>16</h6>
      </div>
    </div>
  );
};

ReactDOM.render(
  <PyramidChart />,
  document.getElementById('root')
);
.pyramid_wrap {
  height: 100%;
  text-align: center;
}

.category_one {
  width: 70px;
  height: 30px;
  border-left: 35px solid transparent;
  border-right: 35px solid transparent;
  border-bottom: 50px solid tomato;
}

.category_two {
  width: 116px;
  height: 30px;
  border-left: 22px solid transparent;
  border-right: 22px solid transparent;
  border-bottom: 28px solid orange;
}

.category_three {
  width: 162px;
  height: 30px;
  border-left: 22px solid transparent;
  border-right: 22px solid transparent;
  border-bottom: 28px solid cyan;
}

.category_four {
  width: 208px;
  height: 30px;
  border-left: 22px solid transparent;
  border-right: 22px solid transparent;
  border-bottom: 28px solid teal;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>

得到了这样的东西:

但我无法解决的问题是将顶级金字塔区域中的文本对齐到中心。它总是与它对齐的位置不同步。

感谢任何帮助纠正这个问题:)

【问题讨论】:

  • 您实际上可以使用&lt;span&gt; 标签而不是&lt;h1&gt;,然后将css 应用于span 标签。 &lt;h1&gt;&lt;span&gt; 也是一个内联元素。
  • 只有在使用reactjs的情况下,这个html和css如何? :)
  • @bubble-cord : 为每个新层堆叠单独的 CSS 设置可能会变得乏味,我宁愿建议将该部分委托给 React 并在a bit more flexible way中解决问题

标签: javascript html css reactjs


【解决方案1】:

让我们尝试使用 skew 转换属性从 CSS Triangle 移动到 :before:after

我没有更改您的 HTML,只需将 .value 类添加到您的 &lt;h6&gt; 以设置它们的样式。

基本上,您所要做的就是为每个&lt;div&gt; 设置一个负度角的:before 和一个正度角的:after,并为它们设置相同的背景颜色,以便匹配。

只针对顶级&lt;div&gt;我使用了一个CSS Triangle,所以你已经知道它是如何工作的了。

为了使值水平和垂直对齐,我在父级上使用了 flexbox,并结合了justify-content: center;(水平对齐)和align-items: center;(垂直对齐)。不要忘记始终添加 line-height:1em; 并删除要垂直对齐的元素的边距。如果line-height 不等于它的实际高度,它总是在中间轴上或下一些像素。

.pyramid_wrap {
  margin-top: 200px;
  height: 100%;
  text-align: center;
}

.category_one,
.category_two,
.category_three,
.category_four {
  position: relative;
  margin: 6px auto;
  display: flex;
  justify-content: center;
  align-items: center;
}

.category_one:before,
.category_one:after,
.category_two:before,
.category_two:after,
.category_three:before,
.category_three:after,
.category_four:before,
.category_four:after {
  position: absolute;
  top: 0;
  display: block;
  width: 30px;
  height: 100%;
  content: "";
}

.category_one:before,
.category_two:before,
.category_three:before,
.category_four:before {
  left: -15px;
  transform: skew(-25deg);
}

.category_one:after,
.category_two:after,
.category_three:after,
.category_four:after {
  right: -15px;
  transform: skew(25deg);
}

.category_one {
    width: 20px;
    height: 40px;
    background: tomato;

}

.category_one:before,
.category_one:after {
  width: 30px;
  height: 40px;
  background: tomato;
}

.category_one:before {
  left: -16px;
  top: 0;
}
.category_one:after {
  right: -16px;
  top: 0;
}

.category_one .value:after {
  position: absolute;
  z-index: -1;
  top: -55px;
  left: 50%;
  transform: translateX(-50%);
  content: "";
  display: block;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 28px 70px 28px;
  border-color: transparent transparent tomato transparent;
}


.category_two {
    width: 70px;
    height: 40px;
    background: orange;
}

.category_two:before,
.category_two:after {
  width: 40px;
  background: orange;
}

.category_two:before {
  left: -12px;
}
.category_two:after {
  right: -12px;
}


.category_three {
  width: 120px;
  height: 40px;
  background: cyan;
}

.category_three:before,
.category_three:after {
  background: cyan;
}
.category_three:before {
  left: -9px;
}
.category_three:after {
  right: -9px;
}


.category_four {
    width: 150px;
    height: 40px;
    background: teal;
}

.category_four:before,
.category_four:after {
  background: teal;
}

.value {
  margin: 0;
  position: relative;
  z-index: 3;
  color: #fff;
  font-size: 16px;
  font-family: Arial, sans-serif;
  font-weight: normal;
  line-height: 1em;
}
 <div class="d-flex flex-column align-items-center pyramid_wrap">
        <div class="category_one">
            <h6 class="value">2</h6>
        </div>
        <div class="category_two">
          <h6 class="value">8</h6>
        </div>
        <div class="category_three">
          <h6 class="value">11</h6>
        </div>
        <div class="category_four">
          <h6 class="value">16</h6>
        </div>
      </div>

【讨论】:

  • 还有一个问题,定义带和不带 :after、:before 的类有什么用?
  • 当我组合在一起例如 .category_one,.category_two,.category_three,.category_four { /* ... */ } 这是因为它们共享相同的样式并且样式将应用于 div本身,当我分组例如 .category_one:before,.category_two:before,.category_three:before,.category_four:before {transform: skew(-25deg);} 这是因为我知道所有左侧 (:before) 都会有负度角。只是你不要重复 transform: skew(-25deg);每次。有关伪元素的更多信息,请阅读此处:w3schools.com/css/css_pseudo_elements.asp
  • 好的,我明白你为什么会感到困惑。我为您创建了一个 Codepen:codepen.io/alezuc/pen/MWKmJNp?editors=1100 在这里您可以在 .category_two 上看到元素本身是橙色的,而之前的元素是带有背景的:蓝色;以及带有背景的负偏斜和后面:绿色;和正偏斜。所以之前和之后需要单独设置样式。不管怎样,总是尝试检查在键盘上按 F12 的元素,因此您可以使用它们的样式可视化页面上的所有元素。希望能解开你的疑惑,否则,请告诉我!
  • 好的。我会通过他们。感谢您提供如此详细的帮助:)
  • 很高兴为您提供帮助!我的建议是学习如何使用伪元素,因为它们非常强大和灵活!
【解决方案2】:

一种可能的解决方案是在整个金字塔的包装器上使用CSS mask。到目前为止,它可能还没有得到很好的支持,但它提供了使用 SVG 路径(三角形)塑造金字塔的真正方法:

为了不让遮罩缩小(隐藏上下层),您可能需要保留包装器的min-width(等于层数乘以层高 - 3em)。

为避免图层标签落在蒙版之外,您可能需要根据标签位置将蒙版居中 (mask-position: center)

实现该概念的现场演示可能如下所示:

const { render } = ReactDOM,
      rootNode = document.getElementById('root')

const pyramidItems = [{label:'2', color: '#f47660'}, {label:'8', color:'#fcae60'}, {label:'11', color:'#a7e6db'}, {label:'16',color:'#79d4c5'}]

const Pyramid = ({items}) => (
  <div 
    className="wrapper"
    style={{minWidth:`${items.length}*3em`}}
  >
    {
      items.map(({label,color},key) => (
        <div 
          key={key}
          className="layer"
          style={{backgroundColor:color}}
        >
          {label}
        </div>
      ))
    }
  </div>
)

render (
  <Pyramid items={pyramidItems} />,
  rootNode
)
.wrapper {
  --triangle-shape: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNTAsMCBMMTAwLDEwMCBMMCwxMDAgeiIvPjwvc3ZnPg==");
  -webkit-mask-image: var(--triangle-shape);
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-image: var(--triangle-shape);
  mask-repeat: no-repeat;
  mask-position: center;
  display: flex;
  flex-direction: column;
}

.layer {
  height: 3em;
  color: #fff;
  font-size: 15px;
  display: flex;
  justify-content: center;
  align-items: center;
  border: .2em solid #fff;
  margin: -.2em 0px;
}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"&gt;&lt;/script&gt;&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"&gt;&lt;/script&gt;&lt;div id="root"&gt;&lt;/div&gt;

【讨论】:

  • 这确实是一个非常动态的解决方案。非常感谢。但是有一个疑问,当我尝试增加宽度时,它似乎不起作用。顶层的尖端也没有指向。你能帮忙解决这两个问题吗?
  • 能否帮我提供实际的 svg 图片?
  • 我对最适合的解决方案持开放态度,因为每个解决方案对我来说都是很好的学习体验。在您发布您的问题后,我使用了它,因为它对我来说似乎更动态更可行,因为我需要尽快在其中呈现同步数据
  • @bubble-cord :当标签居中时,为了使它们保持可见(落在掩码多边形内),您可以执行 mask-position: center 并保留包装器的 min-width 不让您的面具形状收缩隐藏金字塔的外层。查看this fix
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
  • 2011-12-07
  • 2022-11-01
  • 1970-01-01
  • 2018-12-29
  • 2017-03-08
  • 1970-01-01
相关资源
最近更新 更多