Beeswarm 情节不会崩溃
Beeswarm plot not collapsing
我正在尝试制作自己的 beeswarm plot,这是一个跨维度的点图,其中点根据需要向上移动,以免与其他点重叠。
我的每个点都有标签,我正在尝试计算出 javascript 仅当它们与前一个点碰撞时才将它们从基线向上移动。但是,无论是否碰撞,它们都在向上移动。
期望的行为: 在第一个示例中(取决于您的屏幕宽度)● Juliet Francis
应该在基线处向下而不是靠近顶部,因为它可以存在于基线而不与任何前面的点碰撞(从右到左排序)。
我已经测试了碰撞功能,似乎工作正常。问题似乎出在 swarm()
或 collidesAll()
函数中。
几天来我一直在研究这个问题,但一直无法正常工作。第二双眼睛将不胜感激。
$(function() {
let $graphs = $('.graph')
let $firstSetOfNames = $('.graph:first-child .graph-dd')
const setup = () => {
let longest = 0
$firstSetOfNames.each(function() {
longest = ($(this).width() > longest) ? $(this).width() : longest
})
$graphs.css('padding-right', longest + 'px')
}
const collides = ($e1, $e2) => {
let e1x1 = $e1.offset().left
let e1x2 = e1x1.x1 + $e1.outerWidth( true )
let e2x1 = $e2.offset().left
let e2x2 = e2x1.x1 + $e2.outerWidth( true )
let x = ((e1x1 < e2x1) && (e2x1 < e1x2)) || ((e2x1 < e1x1) && (e1x1 < e2x2))
let y = parseInt($e1.css('--y'), 10) === parseInt($e2.css('--y'), 10)
return !!(x || y)
}
const collidesAll = ($people, $person, j) => {
for (let i = 0; i < j; i++) {
if (collides($person, $people.eq(i))) {
return true
}
}
return false
}
const swarm = () => {
$graphs.each(function(i) {
let $graph = $(this)
let $people = $($graph.find('.graph-dd').get().reverse())
$people.each(function(j) {
let $person = $(this)
let n = 1
if (0 === j) {
$person.css('--y', 1)
} else {
do {
$person.css('--y', n++)
} while (collidesAll($people, $person, j))
$graph.css('--yMax', n)
}
})
})
}
setup()
swarm()
})
.graph {
margin: 2rem;
padding: calc(calc(var(--yMax, 0) * 1.1em) + 1rem) 1rem 1rem;
border: 1px solid black;
overflow: hidden;
}
.graph-dl {
position: relative;
display: flex;
margin: 0;
justify-content: space-between;
}
.graph-dl::before {
content: "";
position: absolute;
display: block;
height: 1px;
top: calc(50% - 0.5px);
left: 0;
right: 0;
background: gray;
z-index: -1;
}
.graph-dt {
background: black;
width: 1px;
height: 1em;
border: 0.5em solid white;
margin: 0 -0.5em;
}
.graph-dt span {
display: none;
}
.graph-dd {
position: absolute;
display: block;
top: 0.5em;
margin: 0 0 0 calc(-0.5ex - 1px);
left: calc(var(--percent) * 1%);
white-space: nowrap;
transform: translateY(calc(var(--y, 0) * -1.1em));
transition: transform 0.3s;
}
.graph-dd::before {
content: "";
display: inline-block;
background: blue;
width: 1ex;
height: 1ex;
vertical-align: baseline;
border-radius: 100%;
margin-right: 0.2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<figure class="graph">
<dl class="graph-dl">
<dt class="graph-dt"><span>0</span></dt>
<dd class="graph-dd" data-value="9" style="--percent: 9; --y: 1;">Zaccaria Osmant</dd>
<dt class="graph-dt"><span>10</span></dt>
<dd class="graph-dd" data-value="11" style="--percent: 11; --y: 1;">Nelli Dunge</dd>
<dd class="graph-dd" data-value="12" style="--percent: 12; --y: 1;">Ethelind Evers</dd>
<dd class="graph-dd" data-value="16" style="--percent: 16; --y: 1;">Juliet Francis</dd>
<dt class="graph-dt"><span>20</span></dt>
<dt class="graph-dt"><span>30</span></dt>
<dt class="graph-dt"><span>40</span></dt>
<dd class="graph-dd" data-value="48" style="--percent: 48; --y: 1;">Myles Burdoun</dd>
<dt class="graph-dt"><span>50</span></dt>
<dd class="graph-dd" data-value="55" style="--percent: 55; --y: 1;">Gregory Beade</dd>
<dt class="graph-dt"><span>60</span></dt>
<dd class="graph-dd" data-value="60" style="--percent: 60; --y: 1;">Trenna Vigne</dd>
<dd class="graph-dd" data-value="61" style="--percent: 61; --y: 1;">Dulcia Koubu</dd>
<dt class="graph-dt"><span>70</span></dt>
<dd class="graph-dd" data-value="70" style="--percent: 70; --y: 1;">Amberly Wrightham</dd>
<dd class="graph-dd" data-value="73" style="--percent: 73; --y: 1;">Barney Rawstorn</dd>
<dt class="graph-dt"><span>80</span></dt>
<dt class="graph-dt"><span>90</span></dt>
<dd class="graph-dd" data-value="91" style="--percent: 91; --y: 1;">Nealson Helstrip</dd>
<dd class="graph-dd" data-value="92" style="--percent: 92; --y: 1;">Asa Langwade</dd>
<dd class="graph-dd" data-value="93" style="--percent: 93; --y: 1;">Malvin Imlaw</dd>
<dd class="graph-dd" data-value="96" style="--percent: 96; --y: 1;">Joanie Clooney</dd>
<dt class="graph-dt"><span>100</span></dt>
<dd class="graph-dd" data-value="100" style="--percent: 100; --y: 1;">Kristo Biskupski</dd>
</dl>
</figure>
<figure class="graph">
<dl class="graph-dl">
<dt class="graph-dt"><span>0</span></dt>
<dt class="graph-dt"><span>10</span></dt>
<dt class="graph-dt"><span>20</span></dt>
<dt class="graph-dt"><span>30</span></dt>
<dt class="graph-dt"><span>40</span></dt>
<dd class="graph-dd" data-value="44" style="--percent: 44; --y: 1;">Nelli Dunge</dd>
<dd class="graph-dd" data-value="48" style="--percent: 48; --y: 1;">Myles Burdoun</dd>
<dt class="graph-dt"><span>50</span></dt>
<dd class="graph-dd" data-value="51" style="--percent: 51; --y: 1;">Zaccaria Osmant</dd>
<dt class="graph-dt"><span>60</span></dt>
<dd class="graph-dd" data-value="61" style="--percent: 61; --y: 1;">Trenna Vigne</dd>
<dd class="graph-dd" data-value="61" style="--percent: 61; --y: 1;">Dulcia Koubu</dd>
<dd class="graph-dd" data-value="65" style="--percent: 65; --y: 1;">Ethelind Evers</dd>
<dt class="graph-dt"><span>70</span></dt>
<dd class="graph-dd" data-value="70" style="--percent: 70; --y: 1;">Amberly Wrightham</dd>
<dd class="graph-dd" data-value="73" style="--percent: 73; --y: 1;">Barney Rawstorn</dd>
<dd class="graph-dd" data-value="74" style="--percent: 74; --y: 1;">Kristo Biskupski</dd>
<dd class="graph-dd" data-value="75" style="--percent: 75; --y: 1;">Joanie Clooney</dd>
<dd class="graph-dd" data-value="77" style="--percent: 77; --y: 1;">Juliet Francis</dd>
<dd class="graph-dd" data-value="77" style="--percent: 77; --y: 1;">Gregory Beade</dd>
<dd class="graph-dd" data-value="79" style="--percent: 79; --y: 1;">Malvin Imlaw</dd>
<dt class="graph-dt"><span>80</span></dt>
<dd class="graph-dd" data-value="85" style="--percent: 85; --y: 1;">Asa Langwade</dd>
<dt class="graph-dt"><span>90</span></dt>
<dd class="graph-dd" data-value="91" style="--percent: 91; --y: 1;">Nealson Helstrip</dd>
<dt class="graph-dt"><span>100</span></dt>
</dl>
</figure>
或 view in CodePen 与 Pug 和 Sass 相反。
您的代码存在一些问题:
const collides = ($e1, $e2) => {
let e1x1 = $e1.offset().left
let e1x2 = e1x1.x1 + $e1.outerWidth( true )
let e2x1 = $e2.offset().left
let e2x2 = e2x1.x1 + $e2.outerWidth( true )
let x = ((e1x1 < e2x1) && (e2x1 < e1x2)) || ((e2x1 < e1x1) && (e1x1 < e2x2))
let y = parseInt($e1.css('--y'), 10) === parseInt($e2.css('--y'), 10)
return !!(x || y)
}
- 在
collides
函数中,e1x1
和e2x1
已经是一个数字所以
e1x1.x1
和 e2x1.x1
将是未定义的。那是因为 e1x2
和
e2x2
变成 NaN
.
- 图表已经按值排序,所以你只需要检查一个点是否与它之前的一个点碰撞,而不是所有以前的点(例如:只需要检查
Juliet Francis
是否与 Myles Burdoun
在第一张图中)。因此,您不再需要检查 y 值。
这是修改后的 collides
函数:
const collides = ($e1, $e2) => {
let e1x1 = $e1.offset().left;
let e2x1 = $e2.offset().left;
let e1Width = $e1.outerWidth( true );
let e2Width = $e2.outerWidth( true );
let e1x2 = e1x1 + e1Width;
let e2x2 = e2x1 + e2Width;
if (e2x2 >= e1x1) return (e2x2 - e1x1) < (e1Width + e2Width);
else return (e1x2 - e2x1) < (e1Width + e2Width);
}
我还更改了如何计算图表的 --yMax
以采用该修改。
更新:我有点误解了。如果你想让一个项目在有足够空间的情况下位于基线,你可以保持你的逻辑,只需要在 collides
函数和 --yMax
计算中做一点修改,如下面的代码笔:
我正在尝试制作自己的 beeswarm plot,这是一个跨维度的点图,其中点根据需要向上移动,以免与其他点重叠。
我的每个点都有标签,我正在尝试计算出 javascript 仅当它们与前一个点碰撞时才将它们从基线向上移动。但是,无论是否碰撞,它们都在向上移动。
期望的行为: 在第一个示例中(取决于您的屏幕宽度)● Juliet Francis
应该在基线处向下而不是靠近顶部,因为它可以存在于基线而不与任何前面的点碰撞(从右到左排序)。
我已经测试了碰撞功能,似乎工作正常。问题似乎出在 swarm()
或 collidesAll()
函数中。
几天来我一直在研究这个问题,但一直无法正常工作。第二双眼睛将不胜感激。
$(function() {
let $graphs = $('.graph')
let $firstSetOfNames = $('.graph:first-child .graph-dd')
const setup = () => {
let longest = 0
$firstSetOfNames.each(function() {
longest = ($(this).width() > longest) ? $(this).width() : longest
})
$graphs.css('padding-right', longest + 'px')
}
const collides = ($e1, $e2) => {
let e1x1 = $e1.offset().left
let e1x2 = e1x1.x1 + $e1.outerWidth( true )
let e2x1 = $e2.offset().left
let e2x2 = e2x1.x1 + $e2.outerWidth( true )
let x = ((e1x1 < e2x1) && (e2x1 < e1x2)) || ((e2x1 < e1x1) && (e1x1 < e2x2))
let y = parseInt($e1.css('--y'), 10) === parseInt($e2.css('--y'), 10)
return !!(x || y)
}
const collidesAll = ($people, $person, j) => {
for (let i = 0; i < j; i++) {
if (collides($person, $people.eq(i))) {
return true
}
}
return false
}
const swarm = () => {
$graphs.each(function(i) {
let $graph = $(this)
let $people = $($graph.find('.graph-dd').get().reverse())
$people.each(function(j) {
let $person = $(this)
let n = 1
if (0 === j) {
$person.css('--y', 1)
} else {
do {
$person.css('--y', n++)
} while (collidesAll($people, $person, j))
$graph.css('--yMax', n)
}
})
})
}
setup()
swarm()
})
.graph {
margin: 2rem;
padding: calc(calc(var(--yMax, 0) * 1.1em) + 1rem) 1rem 1rem;
border: 1px solid black;
overflow: hidden;
}
.graph-dl {
position: relative;
display: flex;
margin: 0;
justify-content: space-between;
}
.graph-dl::before {
content: "";
position: absolute;
display: block;
height: 1px;
top: calc(50% - 0.5px);
left: 0;
right: 0;
background: gray;
z-index: -1;
}
.graph-dt {
background: black;
width: 1px;
height: 1em;
border: 0.5em solid white;
margin: 0 -0.5em;
}
.graph-dt span {
display: none;
}
.graph-dd {
position: absolute;
display: block;
top: 0.5em;
margin: 0 0 0 calc(-0.5ex - 1px);
left: calc(var(--percent) * 1%);
white-space: nowrap;
transform: translateY(calc(var(--y, 0) * -1.1em));
transition: transform 0.3s;
}
.graph-dd::before {
content: "";
display: inline-block;
background: blue;
width: 1ex;
height: 1ex;
vertical-align: baseline;
border-radius: 100%;
margin-right: 0.2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<figure class="graph">
<dl class="graph-dl">
<dt class="graph-dt"><span>0</span></dt>
<dd class="graph-dd" data-value="9" style="--percent: 9; --y: 1;">Zaccaria Osmant</dd>
<dt class="graph-dt"><span>10</span></dt>
<dd class="graph-dd" data-value="11" style="--percent: 11; --y: 1;">Nelli Dunge</dd>
<dd class="graph-dd" data-value="12" style="--percent: 12; --y: 1;">Ethelind Evers</dd>
<dd class="graph-dd" data-value="16" style="--percent: 16; --y: 1;">Juliet Francis</dd>
<dt class="graph-dt"><span>20</span></dt>
<dt class="graph-dt"><span>30</span></dt>
<dt class="graph-dt"><span>40</span></dt>
<dd class="graph-dd" data-value="48" style="--percent: 48; --y: 1;">Myles Burdoun</dd>
<dt class="graph-dt"><span>50</span></dt>
<dd class="graph-dd" data-value="55" style="--percent: 55; --y: 1;">Gregory Beade</dd>
<dt class="graph-dt"><span>60</span></dt>
<dd class="graph-dd" data-value="60" style="--percent: 60; --y: 1;">Trenna Vigne</dd>
<dd class="graph-dd" data-value="61" style="--percent: 61; --y: 1;">Dulcia Koubu</dd>
<dt class="graph-dt"><span>70</span></dt>
<dd class="graph-dd" data-value="70" style="--percent: 70; --y: 1;">Amberly Wrightham</dd>
<dd class="graph-dd" data-value="73" style="--percent: 73; --y: 1;">Barney Rawstorn</dd>
<dt class="graph-dt"><span>80</span></dt>
<dt class="graph-dt"><span>90</span></dt>
<dd class="graph-dd" data-value="91" style="--percent: 91; --y: 1;">Nealson Helstrip</dd>
<dd class="graph-dd" data-value="92" style="--percent: 92; --y: 1;">Asa Langwade</dd>
<dd class="graph-dd" data-value="93" style="--percent: 93; --y: 1;">Malvin Imlaw</dd>
<dd class="graph-dd" data-value="96" style="--percent: 96; --y: 1;">Joanie Clooney</dd>
<dt class="graph-dt"><span>100</span></dt>
<dd class="graph-dd" data-value="100" style="--percent: 100; --y: 1;">Kristo Biskupski</dd>
</dl>
</figure>
<figure class="graph">
<dl class="graph-dl">
<dt class="graph-dt"><span>0</span></dt>
<dt class="graph-dt"><span>10</span></dt>
<dt class="graph-dt"><span>20</span></dt>
<dt class="graph-dt"><span>30</span></dt>
<dt class="graph-dt"><span>40</span></dt>
<dd class="graph-dd" data-value="44" style="--percent: 44; --y: 1;">Nelli Dunge</dd>
<dd class="graph-dd" data-value="48" style="--percent: 48; --y: 1;">Myles Burdoun</dd>
<dt class="graph-dt"><span>50</span></dt>
<dd class="graph-dd" data-value="51" style="--percent: 51; --y: 1;">Zaccaria Osmant</dd>
<dt class="graph-dt"><span>60</span></dt>
<dd class="graph-dd" data-value="61" style="--percent: 61; --y: 1;">Trenna Vigne</dd>
<dd class="graph-dd" data-value="61" style="--percent: 61; --y: 1;">Dulcia Koubu</dd>
<dd class="graph-dd" data-value="65" style="--percent: 65; --y: 1;">Ethelind Evers</dd>
<dt class="graph-dt"><span>70</span></dt>
<dd class="graph-dd" data-value="70" style="--percent: 70; --y: 1;">Amberly Wrightham</dd>
<dd class="graph-dd" data-value="73" style="--percent: 73; --y: 1;">Barney Rawstorn</dd>
<dd class="graph-dd" data-value="74" style="--percent: 74; --y: 1;">Kristo Biskupski</dd>
<dd class="graph-dd" data-value="75" style="--percent: 75; --y: 1;">Joanie Clooney</dd>
<dd class="graph-dd" data-value="77" style="--percent: 77; --y: 1;">Juliet Francis</dd>
<dd class="graph-dd" data-value="77" style="--percent: 77; --y: 1;">Gregory Beade</dd>
<dd class="graph-dd" data-value="79" style="--percent: 79; --y: 1;">Malvin Imlaw</dd>
<dt class="graph-dt"><span>80</span></dt>
<dd class="graph-dd" data-value="85" style="--percent: 85; --y: 1;">Asa Langwade</dd>
<dt class="graph-dt"><span>90</span></dt>
<dd class="graph-dd" data-value="91" style="--percent: 91; --y: 1;">Nealson Helstrip</dd>
<dt class="graph-dt"><span>100</span></dt>
</dl>
</figure>
或 view in CodePen 与 Pug 和 Sass 相反。
您的代码存在一些问题:
const collides = ($e1, $e2) => {
let e1x1 = $e1.offset().left
let e1x2 = e1x1.x1 + $e1.outerWidth( true )
let e2x1 = $e2.offset().left
let e2x2 = e2x1.x1 + $e2.outerWidth( true )
let x = ((e1x1 < e2x1) && (e2x1 < e1x2)) || ((e2x1 < e1x1) && (e1x1 < e2x2))
let y = parseInt($e1.css('--y'), 10) === parseInt($e2.css('--y'), 10)
return !!(x || y)
}
- 在
collides
函数中,e1x1
和e2x1
已经是一个数字所以e1x1.x1
和e2x1.x1
将是未定义的。那是因为e1x2
和e2x2
变成NaN
. - 图表已经按值排序,所以你只需要检查一个点是否与它之前的一个点碰撞,而不是所有以前的点(例如:只需要检查
Juliet Francis
是否与Myles Burdoun
在第一张图中)。因此,您不再需要检查 y 值。
这是修改后的 collides
函数:
const collides = ($e1, $e2) => {
let e1x1 = $e1.offset().left;
let e2x1 = $e2.offset().left;
let e1Width = $e1.outerWidth( true );
let e2Width = $e2.outerWidth( true );
let e1x2 = e1x1 + e1Width;
let e2x2 = e2x1 + e2Width;
if (e2x2 >= e1x1) return (e2x2 - e1x1) < (e1Width + e2Width);
else return (e1x2 - e2x1) < (e1Width + e2Width);
}
我还更改了如何计算图表的 --yMax
以采用该修改。
更新:我有点误解了。如果你想让一个项目在有足够空间的情况下位于基线,你可以保持你的逻辑,只需要在 collides
函数和 --yMax
计算中做一点修改,如下面的代码笔: