Drawing connective elements between fixed positions on several html elements

有些话、适合烂在心里 提交于 2021-01-29 15:20:12

问题


I am quite new to web programming, so please bear with me if I am missing something obvious. I am using html, css and javascript to visualize different DNA sequences. Each sequence is represented as a graph of html elements and has a fixed length corresponding to the length of the DNA sequence. Spans are positioned at fixed points in each graph to represent certain motifs. The code for one such graph looks like this (using django as my web framework):

<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>

Like this, I create representations of several sequences on my webpage, one below the other. Now some of these sequences may be similar to one another. I want to graphically display the regions over which a sequence is similar to the one displayed below it, like this:

In the above example, the sequences represented by graph1 and 2 and graph2 and 3 have a similar region, but the total length of the sequences (and thus the displayed size of motifs represented on a graph) differs, resulting in the wider blue element between graph2 and 3.

So what I have for each graph are the sequence length each graph should represent, the start and end positions for each motif on the respective graph, and the start and end position for the similar region on each graph if two graphs (sequences) contain a similar region (green in the picture).

So my question is: How can I create the elements representing a similar region between two graphs (blue in the picture) using html, css or javascript? (Or really anything else that I can implement in what I already have)

Any hints on how I could approach this are apprechiated, let me know if you have more questions!

EDIT: I would actually prefer a solution in svg, as I think its more suitable for integration in my application


回答1:


You could use svg, or canvas.

Here is a minimalist approach with canvas. You may want to rely on a library which are very likely to offer diagrams close to your ones.

But scratch never hurts.

Below, each graph is responsible of scaling properly its axis.

Eventually, the trapezes rely on the graph they depend on to scale the corresponding vertices.

You may want to display the text even more conditionnaly so play with Bar and potentially give it a boolean to indicate whether it should be displayed or not.

You can further style the canvas by playing on the weight of the stroke line and all, but it is just a demo to show you you can get it up easily

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>


来源:https://stackoverflow.com/questions/58956059/drawing-connective-elements-between-fixed-positions-on-several-html-elements

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!