1 回答
TA贡献1810条经验 获得超5个赞
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 几乎占据了“运行代码片段”的所有空间,以获得更好的体验:
编辑这个答案
单击编辑预览中代码段之外的“编辑上述代码段”链接
按“运行”按钮
添加回答
举报