我对Web编程还很陌生,所以如果我遗漏了一些明显的内容,请多包涵。我正在使用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>
像这样,我在网页上创建了多个序列的表示形式,一个序列位于另一个序列之下。现在,这些序列中的某些序列可能彼此相似。我想以图形方式显示序列类似于其下方显示区域的区域,如下所示:
在上面的示例中,由graph1和2以及graph2和3表示的序列具有相似的区域,但是序列的总长度(因此,在图形上显示的基序的显示大小)不同,从而导致graph2和3之间的蓝色元素。
所以我对每个图都有的是每个图应表示的序列长度,每个图上每个基序的开始和结束位置,以及如果两个图(序列)在每个图上相似区域的开始和结束位置)包含相似的区域(图片中为绿色)。
所以我的问题是:如何使用html,css或javascript创建代表两个图形(图片中的蓝色)之间相似区域的元素? (或者实际上是我可以在已有的东西中实现的任何东西)
关于如何处理此问题的任何提示均得到赞赏,如果您还有其他问题,请告诉我!
编辑:我实际上更喜欢svg中的解决方案,因为我认为它更适合集成到我的应用程序中
答案 0 :(得分: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>