在 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.width
,DjangoParentSizeValue
- 我们还从数据库中知道要显示的元素的宽度:
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>
我是 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.width
,DjangoParentSizeValue
- 我们还从数据库中知道要显示的元素的宽度:
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>