【问题标题】:Displaying size adjusted buttons on specific points on a line in HTML/CSS在 HTML/CSS 中的一行上的特定点上显示调整大小的按钮
【发布时间】:2019-11-06 11:07:03
【问题描述】:

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

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

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

每个孩子的长度在这里结束。

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

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

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

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

谁能告诉我代码、库、方法、教程或其他可以帮助我实现这一目标的东西?

【问题讨论】:

  • 如果是连续的一系列子按钮,你想怎么排列?与溢出滑块成一条直线..

标签: html css django data-visualization visualization


【解决方案1】:

这可以在纯 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>

【讨论】:

  • 谢谢,在对我的词典进行了一些重新调整后,它可以正常工作了!必须更多地阅读缩放和所有内容,但答案很好且简单!
【解决方案2】:

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

我会这样设置:

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

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>

【讨论】:

    【解决方案3】:

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

    给定父级的(固定)宽度,在给定宽度的情况下将子元素均匀分布在父级中。

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

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

    父级display: flex => 默认为 1:N 列的无包装 flexbox 行。

    孩子flex-grow: 'javascript calculated'=&gt; high - low = value。没错,忽略 CSS width 值,只设置 CSS flex-grow 值。

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

    就是这样,剩下的就是眼花缭乱了。

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

    无论如何,您有时都需要对父级宽度设置一个限制,用户讨厌必须多次滚动或向左/向右滑动(我知道我会这样做!)。

    eyecandy:除了最后一个子元素之外的所有子元素都会获得 margin-right(无论您需要什么值)。

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

    伪 javascript 示例显示了一个简单的 for 循环,将 flex-grow 值分配给子元素。

    守则看起来很多,但 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>

    【讨论】:

      猜你喜欢
      • 2015-03-16
      • 1970-01-01
      • 1970-01-01
      • 2017-08-24
      • 2016-10-21
      • 2015-08-05
      • 1970-01-01
      • 2017-03-21
      • 2021-11-22
      相关资源
      最近更新 更多