森栏
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 几乎占据了“运行代码片段”的所有空间,以获得更好的体验:编辑这个答案单击编辑预览中代码段之外的“编辑上述代码段”链接按“运行”按钮