在多个 html 元素的固定位置之间绘制连接元素

问题描述 投票:0回答:1

我对网络编程还很陌生,所以如果我遗漏了一些明显的东西,请耐心等待。我使用 html、css 和 javascript 来可视化不同的 DNA 序列。每个序列都表示为 html 元素图,并且具有与 DNA 序列长度相对应的固定长度。跨度位于每个图中的固定点以表示某些主题。此类图的代码如下所示(使用 django 作为我的 Web 框架):

<div class="graph" style='--graph-length: {{sequence_length}}'>
        <hr class="line">
      {% for key, value2 in value.items %}
        {% if value2.start_position %}
            <span class='motif' style="--start: {{ value2.start_position }}; --stop: {{ value2.stop_position }};"></span>
        {% endif %}
      {% endfor %}
      </div>

像这样,我在网页上创建了多个序列的表示,一个在另一个下面。现在,其中一些序列可能彼此相似。我想以图形方式显示序列与其下方显示的序列相似的区域,如下所示: enter image description here

在上面的示例中,graph1 和 2 以及 graph2 和 3 表示的序列具有相似的区域,但序列的总长度(以及因此图表上表示的图案的显示大小)不同,导致蓝色元素更宽在图2和图3之间。

因此,对于每个图,我所拥有的是每个图应表示的序列长度、相应图上每个主题的开始和结束位置,以及每个图上相似区域的开始和结束位置(如果两个图(序列)包含)类似的区域(图中绿色)。

所以我的问题是:如何使用 html、css 或 javascript 创建表示两个图形(图中蓝色)之间相似区域的元素? (或者我可以在已有的内容中实现的任何其他内容)

感谢任何有关我如何解决此问题的提示,如果您有更多问题,请告诉我!

编辑:我实际上更喜欢 svg 中的解决方案,因为我认为它更适合集成到我的应用程序中

javascript html css svg visualization
1个回答
1
投票

您可以使用 svg 或 canvas。

这是使用画布的极简主义方法。您可能想要依赖一个“非常”可能提供与您的图表接近的图表的库。 但是刮伤永远不会痛。

下面,每个图负责正确缩放其轴。

最终,梯形依靠它们所依赖的图来缩放相应的顶点。

您可能希望更有条件地显示

text

,因此可以使用 Bar 并可能给它一个布尔值来指示是否应显示它。 您可以通过调整笔划线等的重量来进一步设置画布样式,但这只是一个演示,向您展示您可以轻松完成它

const canvas = document.querySelector('canvas') let ctx = canvas.getContext('2d') let bars = [ [Bar(200, 300), Bar(1800,2300), Bar(2500, 4500), Bar(5000,5200), Bar(8000,8500)], [Bar(1100,1300), Bar(3000, 3800), Bar(4000, 4200), Bar(7000, 7500)], [Bar(1, 2700)] ] function Bar(a,b){return [a, b]} class Graph{ constructor ({anchorY, width, min, max}) { this.anchorY = anchorY this.width = width this.dw = width / (max - min ) this.min = min this.max = max } plot (bars) { // plot bars // resize bars dimension to fit canvas const e = 5 ctx.fillStyle = 'orange' const scaledBars = bars.map(([a, b]) => [ a, b, a * this.dw, b * this.dw ]) scaledBars.forEach(([_, dum, left, right])=>{ ctx.fillRect(left, this.anchorY - e, right - left, 2*e) }) // plot line ctx.strokeStyle = 'black' ctx.beginPath() ctx.moveTo(0, this.anchorY) ctx.lineTo(this.width, this.anchorY) ctx.closePath() ctx.stroke() ctx.strokeStyle = 'green' ctx.font = '10px serif' scaledBars.forEach(([origLeft, origRight, left, right]) => { ctx.strokeText(origLeft, left, this.anchorY - 10) if (right - left > 100 ) { ctx.strokeText(origRight, right, this.anchorY - 10) } }) } //x will be shifted automatically moveTo (x) { ctx.moveTo(x * this.dw, this.anchorY) } lineTo (x) { ctx.lineTo(x * this.dw, this.anchorY) } } const graphs = [ new Graph({anchorY:100, width: canvas.width, min: 1, max: 10000}), new Graph({anchorY:200, width: canvas.width, min: 1, max: 8500}), new Graph({anchorY:300, width: canvas.width, min: 1, max: 4000}) ] // g first graph, (a,b) left, right anchors in first graph // g2 second graph, c right anchor, d left anchor in second graph function trapeze(g, a, b, g2, c, d){ ctx.beginPath() g.moveTo(a) g.lineTo(b) g2.lineTo(c) g2.lineTo(d) ctx.closePath() ctx.fillStyle = 'rgba(84, 147, 158, 0.5)' ctx.fill() } const [g1, g2, g3] = graphs const trapezes = [ [g1, 1800, 4500, g2, 3800, 1100], [g1, 8000, 8500, g2, 7500, 7000], [g2, 1100, 3800, g3, 2700, 1], ] trapezes.forEach(t => trapeze(...t)) graphs.forEach((g, i) => { g.plot(bars[i]) })
canvas{background:#eeeeee;}
<canvas width="400" height="400"></canvas>

© www.soinside.com 2019 - 2024. All rights reserved.