根据网页和 SQL 数据库中的坐标创建 svg 元素

我有一个类似于在几个html元素上的固定位置之间绘制连接元素的问题,但我正在寻找适用于svg的解决方案(不幸的是,自从我发布问题到现在为止,我没有时间处理这个项目,所以我仍然是网络开发的初学者)。


我试图在我的 Web 应用程序中可视化不同 DNA 序列的对齐情况(django 是我的 Web 框架)。每个 DNA 序列都以图表形式显示在<div>. 每个图都包含代表 DNA 序列中子序列的子元素(黄色框)。我通过循环遍历包含从数据库获取的原始值的字典来创建图表。字典是这样构建的:


{'1': {'organism': 'sequence 1', 'env_length': 10000, 'frame': '+', 'alns': {'aln_1': {'s_id': 2, 'q_id': 1, 'aln_len': 2700, 'q_start': 1800, 'q_end': 4500, 's_start': 1100, 's_end': 3800}, 'aln_2': {'s_id': 2, 'q_id': 1, 'aln_len': 500, 'q_start': '8000', 'q_end': 8500, 's_start': 7000, 's_end': 7500}}, 'env_genes': {'g1': {'env_name': 'gene_1', 'env_start': 1850, 'env_end': 4700, 'env_strand': '+', 'gene_class': 'normal'}, 'g2': {'env_name': 'gene_2', 'env_start': 8000, 'env_end': 8500, 'env_strand': '+', 'gene_class': 'normal'}}}, '2': {'organism': 'sequence 2', 'env_length': 8500, 'frame': '+', 'alns': {'aln_1': {'s_id': 3, 'q_id': 2, 'aln_len': 2700, 'q_start': 1100, 'q_end': 3800, 's_start': 1, 

元素在 html 中是这样创建的:


<div class="graph" style='--graph-length: {{value.env_length}}'>

          <hr class="line seq">

          {% for key2, value2 in value.items %}

            {% for key3, value3 in value2.items %}

              {% if value3.env_start %}

                {% if value3.env_strand == '+' %}

                  <span class='env_gene_right {{ value3.gene_class }}' style="--start: {{ value3.env_start }}; --stop: {{ value3.env_end }};"></span>

                {% else %}

                  <span class='env_gene_left {{ value3.gene_class }}' style="--start: {{ value3.env_start }}; --stop: {{ value3.env_end }};"></span>

                {% endif %}

              {% endif %}

            {% endfor %}

          {% endfor %}

</div>

并使用 css 进行样式设置,如下所示:


交互式爱情
浏览 95回答 1
1回答

森栏

DIV我强烈建议使用单个库来绘制整个图像,而不仅仅是尝试组合s 和图像(这意味着要面对几个对齐和缩放问题)。我的示例使用 JS 画布库(内置在所有较新的浏览器中),它需要提供将图像绘制到页面中的 JavaScript 所需的所有原始数据:在我的示例中,我使用了单个 JSON input。在开始我的例子之前:我不得不承认我不明白黄色矩形的含义,我称它们为evidences;我认为在 graph3 中存在一个拼写错误:相似的结束边缘位于 2700,而长度为 2000,在我的示例中,我将长度更改为 4000我自由地从 graph1 和 graph3 添加了另一对类似的夫妇const input = {  graphs: [{      name: "graph1",      length: 10000,      evidences: [{          from: 100,          length: 1000        },        {          from: 1800,          length: 1000        },        {          from: 3000,          length: 1000        },        {          from: 6000,          length: 1000        },        {          from: 8000,          length: 500        },        {          from: 9300,          length: 500        },      ],    },    {      name: "graph2",      length: 8500,      evidences: [{          from: 1100,          length: 1000        },        {          from: 2500,          length: 1000        },        {          from: 5000,          length: 1000        },        {          from: 7000,          length: 500        },      ],    },    {      name: "graph3",      length: 4000,      evidences: [{          from: 1,          length: 2700        },        {          from: 3200,          length: 500        },      ],    },  ],  similar: [{      first: 0,      second: 1,      from: [1800, 1100],      length: 2700,      color: "#8080ff",    },    {      first: 1,      second: 2,      from: [1100, 1],      length: 2700,      color: "#8080ff",    },    {      first: 0,      second: 1,      from: [8000, 7000],      length: 500,      color: "#8080ff",    },    {      first: 0,      second: 2,      from: [9300, 3200],      length: 500,      color: "#ff8080",    },  ],};const imgScale = window.devicePixelRatio;function main() {  // init  const nameWidth = 100;  const lengthWidth = 70;  const graphHight = 50;  const canvas = document.getElementById("canvas");  canvas.style.height = graphHight * input.graphs.length + "px";  const {    clientWidth,    clientHeight  } = canvas;  const width = (canvas.width = clientWidth * imgScale);  const height = (canvas.height = clientHeight * imgScale);  const graphWidth = width - (nameWidth + lengthWidth) * imgScale;  var ctx = canvas.getContext("2d");  // white fill canvas  ctx.fillStyle = "#ffffff";  ctx.fillRect(0, 0, clientWidth, clientHeight);  // draw each similar  for (let i = 0; i < input.similar.length; ++i) {    const {      first,      second,      from,      length,      color    } = input.similar[i];    const middleFirst = graphHight * (first + 0.5) * imgScale;    const middleSecond = graphHight * (second + 0.5) * imgScale;    const fromScaleFirst =      (from[0] * graphWidth) / input.graphs[first].length;    const lengthScaleFirst =      (length * graphWidth) / input.graphs[first].length;    const fromScaleSecond =      (from[1] * graphWidth) / input.graphs[second].length;    const lengthScaleSecond =      (length * graphWidth) / input.graphs[second].length;    ctx.fillStyle = color;    ctx.beginPath();    ctx.moveTo(nameWidth * imgScale + fromScaleFirst, middleFirst);    ctx.lineTo(      nameWidth * imgScale + fromScaleFirst + lengthScaleFirst,      middleFirst    );    ctx.lineTo(      nameWidth * imgScale + fromScaleSecond + lengthScaleSecond,      middleSecond    );    ctx.lineTo(nameWidth * imgScale + fromScaleSecond, middleSecond);    ctx.closePath();    ctx.fill();    populatePolygons({      points: [        [nameWidth * imgScale + fromScaleFirst, middleFirst],        [          nameWidth * imgScale + fromScaleFirst + lengthScaleFirst,          middleFirst,        ],        [          nameWidth * imgScale + fromScaleSecond + lengthScaleSecond,          middleSecond,        ],        [nameWidth * imgScale + fromScaleSecond, middleSecond],      ],      object: {        type: "similar",        first,        second,        from,        length      },    });  }  // write each similar edge  ctx.fillStyle = "#008000";  ctx.font = "20px Arial";  ctx.textBaseline = "middle";  ctx.textAlign = "center";  for (let i = 0; i < input.similar.length; ++i) {    const {      first,      second,      from,      length,      color    } = input.similar[i];    const middleFirst = graphHight * (first + 0.5) * imgScale;    const middleSecond = graphHight * (second + 0.5) * imgScale;    const fromScaleFirst =      (from[0] * graphWidth) / input.graphs[first].length;    const lengthScaleFirst =      (length * graphWidth) / input.graphs[first].length;    const fromScaleSecond =      (from[1] * graphWidth) / input.graphs[second].length;    const lengthScaleSecond =      (length * graphWidth) / input.graphs[second].length;    ctx.fillText(      from[0],      nameWidth * imgScale + fromScaleFirst,      middleFirst - 20    );    ctx.fillText(      from[0] + length,      nameWidth * imgScale + fromScaleFirst + lengthScaleFirst,      middleFirst - 20    );    ctx.fillText(      from[1],      nameWidth * imgScale + fromScaleSecond,      middleSecond - 20    );    ctx.fillText(      from[1] + length,      nameWidth * imgScale + fromScaleSecond + lengthScaleSecond,      middleSecond - 20    );  }  //draw each graph  ctx.strokeStyle = "#000000";  for (let i = 0; i < input.graphs.length; ++i) {    const graph = input.graphs[i];    const middle = graphHight * (i + 0.5) * imgScale;    ctx.beginPath();    ctx.moveTo(nameWidth * imgScale, middle);    ctx.lineTo(nameWidth * imgScale + graphWidth, middle);    ctx.stroke();    ctx.fillStyle = "#000000";    ctx.textAlign = "left";    ctx.fillText(graph.name, 10, middle);    ctx.textAlign = "right";    ctx.fillText("1", (nameWidth - 10) * imgScale, middle);    ctx.fillText(graph.length, width - 10, middle);    // draw each evidence    ctx.fillStyle = "#ffff80";    for (let l = 0; l < graph.evidences.length; ++l) {      const {        from,        length      } = graph.evidences[l];      const fromScale = (from * graphWidth) / graph.length;      const lengthScale = (length * graphWidth) / graph.length;      ctx.fillRect(        fromScale + nameWidth * imgScale,        middle - 10,        lengthScale,        20      );      ctx.beginPath();      ctx.rect(        fromScale + nameWidth * imgScale,        middle - 10,        lengthScale,        20      );      ctx.stroke();      populatePolygons({        points: [          [fromScale + nameWidth * imgScale, middle - 10],          [fromScale + nameWidth * imgScale + lengthScale, middle - 10],          [fromScale + nameWidth * imgScale + lengthScale, middle + 10],          [fromScale + nameWidth * imgScale, middle + 10],        ],        object: {          type: "evidence",          graph: i,          evidence: l,        },      });    }  }}const polygons = [];function populatePolygons(polygon) {  polygons.push(polygon);}function inside(point, vs) {  var x = point[0],    y = point[1];  var inside = false;  for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {    var xi = vs[i][0],      yi = vs[i][1];    var xj = vs[j][0],      yj = vs[j][1];    var intersect =      yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;    if (intersect) inside = !inside;  }  return inside;}function mouseEvent(kind, event) {  const {    offsetX,    offsetY  } = event;  for (polygon in polygons) {    const {      object,      points    } = polygons[polygon];    if (inside([offsetX * imgScale, offsetY * imgScale], points))      interaction(kind, object);  }}function interaction(kind, object) {  const {    type,    graph,    evidence,    first,    second,    from,    length  } = object;  if (type == "evidence")    console.log(      `${kind} on evidence nr ${evidence + 1} of graph nr ${graph + 1}`    );  else    console.log(      `${kind} on similar between graph nr ${first + 1} from ${            from[0]          } and graph nr ${second + 1} from ${from[1]} long ${length}`    );}main();<canvas id="canvas" style="width: 100%" onclick="mouseEvent('click', event)" onmousemove="mouseEvent('move', event)" />这将为您提供一个也可以复制到剪贴板的图像;尝试:左键单击“运行代码片段”按钮右键单击结果图像选择“复制图像”上下文菜单(Windows chrome)粘贴到任何接受剪贴板图像的程序中编辑:根据新的请求(用户需要与图像交互),使用单个库使一切变得更加简单这一点并没有改变。使用很棒的多边形点算法,我们也可以轻松实现这个更多目标。我添加了一个简单的控制台日志作为概念验证,您可以随意附加任何其他内容。不幸的是,console.log 几乎占据了“运行代码片段”的所有空间,以获得更好的体验:编辑这个答案单击编辑预览中代码段之外的“编辑上述代码段”链接按“运行”按钮
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript