在 HTML/CSS 中一行的特定点上显示大小调整按钮

Displaying size adjusted buttons on specific points on a line in HTML/CSS

我是 HTML/CSS 的新手,现在正在使用 Django 构建 Web 应用程序。

此应用程序从预先计算的数据库中提取数据。每个条目都有一定的长度,包含多个 child 条目,每个条目的长度是 parent 条目总长度的一小部分。作为 python 字典,它看起来像这样:

{entry1: {'length': 10000, {child1}: {'start':1, 'end':1000 }, {child2}: {'start':2000, 'end':6000}, ...}

这里每个child的长度是end-start。

我想要做的是在 HTML/CSS 中将每个条目显示为一行,并且每个 child 条目都显示为该行上的按钮。线上每个按钮的大小应反映其长度(这是 parent 条目长度的一小部分,但每个 child 条目都不同)。重要的是,每个child条目在parent条目上都有一定的位置(例如:parent条目长度为10000,child1在1-1000,child 2 在 2000 到 6000 等等)

我想要得到的结果是这样的:

我有几十个条目要像这样显示,最终甚至从一个条目到下面显示的那个条目建立图形连接。 (假设从条目 1 的位置 1200 到条目 2 的位置 400 画一条线)。

我已经设法将按钮放在 HTML/CSS 中的一条线上,但我不知道如何适当地调整每个按钮或如何将它们放在 [=] 中的正确位置41=] 行。

任何人都可以向我指出可以帮助我实现这一目标的代码、库、方法、教程或其他内容吗?

这完全可以做到 CSS。诀窍是使用 CSS 变量和 calc() 函数动态计算宽度和起始位置作为整个图形的百分比。这使图形响应。

它是如何工作的

一个图是用单个 hr 元素和可变数量的子节点构建的。节点绝对位于图形元素内。使用 calc() 函数计算节点的长度和位置。

图表长度用 --graph-length 变量设置。
节点有两个变量 --start--end.

节点的宽度计算方式:

calc((var(--end) - var(--start)) / var(--graph-length) * 100%)

及其起始位置:

calc(var(--start) / var(--graph-length) * 100%)

在 Django 模板中用字典中的值替换硬编码值。 如果您可以像这样在字典中创建子列表:

graph = {'entry': {'length': 10000, 'children': [{'child': {'start': 1, 'end': 1000}}, {'child': {'start': 2000, 'end': 6000}}]}}

然后在 Django 模板中生成图形将很简单:

<div class="graph" style="--graph-length: {{ entry.length }};">
  <hr class="line">
    {% for node in entry.children %}
        <span class="node" style="--start: {{ node.child.start }}; --end: {{ node.child.end }};"></span>
    {% endfor %}
</div> 

.graph {
  position: relative;
}

.node {
  position: absolute;
  top: 0;
  width: calc((var(--end) - var(--start)) / var(--graph-length) * 100%);
  left: calc(var(--start) / var(--graph-length) * 100%);
  height: 1em;
  background-color: cornflowerblue;
  display: block;
  border: 1px solid gray;
  border-radius: 3px;
}

.line {
  position: absolute;
  left: 0;
  right: 0;
  height: 0px;
}
<div class="graph" style="--graph-length: 10000;">
  <hr class="line">
  <span class="node" style="--start: 1; --end: 250;"></span>
  <span class="node" style="--start: 400; --end: 800;"></span>
  <span class="node" style="--start: 1500; --end: 3500;"></span>
  <span class="node" style="--start: 6000; --end: 8000;"></span>
  <span class="node" style="--start: 9500; --end: 10000;"></span>
</div>
<br>
<br>
<div class="graph" style="--graph-length: 10000;">
  <hr class="line">
  <span class="node" style="--start: 1; --end: 150;"></span>
  <span class="node" style="--start: 500; --end: 850;"></span>
  <span class="node" style="--start: 1200; --end: 2500;"></span>
  <span class="node" style="--start: 3000; --end: 3200;"></span>
  <span class="node" style="--start: 5000; --end: 6000;"></span>
  <span class="node" style="--start: 6500; --end: 7500;"></span>
  <span class="node" style="--start: 8500; --end: 9000;"></span>
</div>
<br>
<br>
<div class="graph" style="--graph-length: 1000;">
  <hr class="line">
  <span class="node" style="--start: 1; --end: 100;"></span>
  <span class="node" style="--start: 300; --end: 500;"></span>
  <span class="node" style="--start: 650; --end: 750;"></span>
  <span class="node" style="--start: 900; --end: 950;"></span>
</div>

您不需要图书馆或任何其他疯狂行为。您只需要设置一些基本样式,然后通过 Javascript 修改 position & width 属性。

我会这样设置:

您需要 HTML 来匹配您的数据布局,所以让我们使用那些 类。然后我们将使用 CSS 来创建对齐并使用 javascript 将所有内容放在正确的位置。我还建议计算一个百分比,这样你就可以有任何宽度的线 (parent)。

let data = {
  'one': {
    'left' : 0,
    'width' : 20
  },
  'two': {
    'left' : 25,
    'width' : 5
  },
  'three': {
    'left' : 35,
    'width' : 40
  },
  'four': {
    'left' : 80,
    'width' : 5
  }
}

var parentEl = document.getElementById('parent'); 

Object.entries(data).forEach((item,i) => {
  let props = item[1]
  let el = parentEl.getElementsByTagName("button")[i]
  // These are percents in the data if they're px you need to convert
  // startX/parentWidth * 100 & childW/ParentW * 100

  el.style.left = props.left + '%'
  el.style.width = props.width  + '%'
});
#parent {
  /* give the absolute position an anchor */
  position: relative;
  /* margin to make it easier to see */
  margin: 50px 10px;
  
  width: 100%;
  height: 1px;
  background-color: tan;
}

.child {
  /* position it relative to the line */
  position: absolute;
  padding: 0;
  margin: 0;
  min-width: 1px;
  
  /* bring the top above the line */
  top: -50%;
  transform: translateY(-50%);
  
  /* 
  now all the buttons are stacked at the left 
  we'll change that position with javascirpt.
  */
}
<div id="parent">
  <button class="child">1</button>
  <button class="child">2</button>
  <button class="child">3</button>
  <button class="child">4</button>
</div>

乍一看,您的问题看起来比实际情况要复杂得多。诀窍是忽略你对 Django 的引用,像线条和颜色一样引人注目,并将你的问题简化为:

给定 parent 的(固定)宽度,在给定宽度的 parent 中均匀分布 child 个元素。

  • 我们从数据库中得到parent.widthDjangoParentSizeValue
  • 我们还从数据库中知道要显示的元素的宽度: DjangoHighValue - DjangoLowValue = child.width

Flexbox 机制已经提供了一种分发 child 元素的方法,那么为什么不使用它呢:

Parent: display: flex => 默认为 none 包裹 flexbox 行 1:N 列。

Child: flex-grow: 'javascript calculated'=> high - low = value。没错,忽略CSSwidth值,直接设置CSSflex-grow

A flex-grow 值 > 0 表示允许元素在 parent 内增长。它通常是 0 或 1(没有 grow=default/grow),但它可以有任何值。给 child 元素一个从 Django 数据库数据计算的 flex-grow 值(而不是 0/1),Flexbox 机制会将它们均匀分布在 parent 中。正是我们需要的!!

仅此而已,剩下的就是养眼了。

现在您只需决定如何处理从数据库中检索到的 parent 宽度值:100%、x 倍视口宽度、x 倍智能手机显示宽度等。

你有时需要对 parent 宽度设置限制,用户讨厌多次滚动或滑动 left/right(我知道我这样做!)。

亮点:除最后一个 child 元素外,所有元素都得到 margin-right(您需要的任何值)。

对于中间的线条,我简单地使用了在 parent 背景上绘制线条的 SVG 图像。

伪 javascript 示例显示了一个简单的 for-loop 将 flex-grow 值分配给 child 元素。

代码好像很多,但99%只是例子。

最后:检查 LeaderLine 是否需要您的线条可点击并具有某些功能。我最近才发现这个库(带有 Github 源代码),它看起来很有趣...

/*
    Psuedo/None working example Javascript code
 */
function setFlexGrowValues() {
// Get a list of all FBL parents
var parentList = document.querySelectorAll('.parent');
var childList,x,y;

    // Traverse parent list
    for (x = 0; x < parentList.length; x++) {
        // Get all the 'button' child elements inside the parent
     childList = parentList[x].querySelectorAll('button');
        // Traverse the parent.button.list
        for (y = 0; y < childList.length; y++) {
            // Calculate and assign the the width found in the Django Database 
         childList[y].style.flexGrow = DjangoHighValue - DjangoLowValue;
        }
    }
};
/*
    - A Flexbox parent container is a none wrapping row (of 1:N columns) by default.
    - In this case FBL container must have some (min-/max-) width set so the FBL mechanism
      can use that width to properly distribute the FBL child elements with the flex-grow value.
    - Width can be 100% (like here), but essentially any value will do (fixed or relative units).
      The FBL mechanism will use the flex-grow value as a 'ratio' for distributing the child elements. 

    - Combined with the 'flex-grow' attribute of the child elements, the only relevant CSS to make  
      things work is the below line. 

    Any other styling like a line, border, spacing, colors is arbitrary and yours to decide upon. 
*/
.parent { display: flex; width: 100% }

/* using some simple elements spacing => eyecandy */
.parent>button      { margin-right: 1rem }

/* No right margin for the last child in line => eyecandy */
.parent>:last-child { margin-right: 0 }

/*
    No need to use calculation for some line to draw, just use an inline svg to do the job
    and draw a line as a background image from parent (0,50%) upto parent (100%,50%) => eyecandy
*/
.parent { background-image: url('data:image/svg+xml, \
                            <svg xmlns="http://www.w3.org/2000/svg"> \
                                <line x1="0" x2="100%" y1="50%" y2="50%" height="1" stroke="black" /> \
                             </svg>');
}

/* irrelevant, again: eyecandy */
button  { height: 1.5rem; border: 1px solid LightGrey; cursor: pointer }
<div class="parent"><!-- 'style' created by CSS or JS. Here as example only! -->
    <button style="flex-grow: 4000; background-color: PaleTurquoise">1</button>
    <button style="flex-grow: 1000; background-color: Chartreuse"   >2</button>
    <button style="flex-grow:  500; background-color: LemonChiffon" >3</button>
    <button style="flex-grow: 1350; background-color: Cornsilk"     >4</button>
    <button style="flex-grow:  750; background-color: GreenYellow"  >5</button>
    <button style="flex-grow:  550; background-color: DarkSeaGreen" >6</button>
</div>

<div class="parent">
    <button style="flex-grow: 1000; background-color: Chartreuse"   >1</button>
    <button style="flex-grow:  750; background-color: GreenYellow"  >2</button>
    <button style="flex-grow: 1350; background-color: Cornsilk"     >3</button>
    <button style="flex-grow:  500; background-color: LemonChiffon" >4</button>
    <button style="flex-grow:  550; background-color: DarkSeaGreen" >5</button>
    <button style="flex-grow: 4000; background-color: PaleTurquoise">6</button>
</div>

<div class="parent">
    <button style="flex-grow:  550; background-color: DarkSeaGreen" >1</button>
    <button style="flex-grow: 4000; background-color: PaleTurquoise">2</button>
    <button style="flex-grow: 1000; background-color: Chartreuse"   >3</button>
    <button style="flex-grow:  500; background-color: LemonChiffon" >4</button>
    <button style="flex-grow:  750; background-color: GreenYellow"  >5</button>
    <button style="flex-grow: 1350; background-color: Cornsilk"     >6</button>
</div>