HTML 在文本后面画箭头

HTML draw arrows behind text

html5canvas可以做到以下几点吗?如果是,如何...

  1. 置于底层HTML文本后面
  2. 无论浏览器缩放大小还是换行,您能否准确找到指定坐标 HTML 文本(可能用 span ID 标识)

我正在尝试使用 HTML/CSS/JS 创建以下内容:

(请原谅绿色波浪下划线)

突出显示的文本显然可以设置 background-color:

棘手的部分是将突出显示的文本与箭头连接起来,我认为它可以用 HTLM canvas 完成,但我愿意接受任何想法。

另外一个不错的小奖励是 highlighting/arrows 出现在悬停或关闭按钮上。

PS 一些背景知识,文本是一些简化的 JCL(大型机的脚本语言),突出显示的项目是文件。我试图通过作业(脚本)更轻松地跟踪数据流。这是一个非常简单的版本,但许多作业可能长达 100 行,其中包含大量细节,因此很难追踪哪些步骤相互关联。如果有其他想法或工具可以帮助跟踪 JCL 中的数据流,请告诉我。

//COBLPGM  EXEC PGM=COBLPGM
//INPUT    DD DSN=&&SORT,DISP=(OLD,DELETE)
//NACHA    DD DSN=NODE.OPER.COBLPGM.OUT(+1)

//SORT2    EXEC PGM=SORT
//SORTIN   DD DSN=NODE.OPER.COBLPGM.OUT(+1)
//SORTOUT  DD DSN=&&SORT2,DISP=(,PASS)
//SYSIN    DD DSN=NODE.OPER.PROCLIB(MEM)

//UNRELATE EXEC PGM=UNPGM
//INPUT    DD DSN=NODE.OPER.UNRELATED.FILE
//REPORT   DD DSN=&&REPORT

//TSTEMPT1 EXEC PGM=SPOPNCLO
//IN       DD DSN=&&SORT2,DISP=(OLD,DELETE)

//    IF TSTEMPT1.RC=0 THEN

//SORT3    EXEC PGM=SORT
//SORTIN   DD DSN=NODE.OPER.COBLPGM.OUT(+1)
//SORTOUT  DD DSN=&&SORT3,DISP=(,PASS),LRECL=141
//SYSIN    DD DSN=NODE.OPER.CNTRLCDS(PARM)

//    ENDIF

这只是一个 "conceptual" 答案,表明您可以跟踪 HTML 以同步 canvas 元素。

以下代码在 HTML 中的 <pre> 标记中包含文本本身。后台有一个canvas,大小固定。 canvas 在滚动时更新,因此框是相对于页面绘制的(它也应该在调整大小时更新,未显示)。

因为我们可以跟踪您可以看到的文本,所以我们也可以放置任何其他与其相关的图形,例如箭头和线条。我没有在这里展示它,因为我觉得它太宽泛了,但你应该了解它的要点,因为它展示了如何计算文本行和字符位置。

依据是:

  • 获取 <pre> 标签的绝对位置
  • 计算行数(注意将文本放在标签后面而不是换行,并将结束标签放在与最后一个文本行相同的行)
  • 将绝对高度除以行数将得出每行的行高(以像素为单位)
  • 使用 measureText() of context 来测量每行的宽度,方法是将 context 设置为使用与 <pre> 标签相同的字体和大小
  • 使用上一个预标记的矩形偏移行位置的 x 和 y。
  • 每个字符都是使用当前字符之前的字符计算的,使用 measureText()(单元格是这个位​​置和下一个字符的位置)。

背景中的 canvas 标记区域使文本保持可选状态。

请注意,文本行中的特殊字符可能会影响 measureText(例如示例文本中的 &&)。在测量之前必须对这些字符进行编码或替换。对于本例中的等宽字体,替换不是问题。

演示

var pre = document.querySelector("pre"),          // get pre ele,ent
    rect = pre.getBoundingClientRect(),           // get its absolute position
    lines = pre.innerHTML.split("\n"),            // split text lines
    count = lines.length,                         // count lines
    lineH = rect.height / count,                  // line height
    
    canvas = document.querySelector("canvas"),    // setup canvas
    ctx = canvas.getContext("2d");

canvas.width = window.innerWidth;                 // todo: update on resize
canvas.height = window.innerHeight;

ctx.font = "14px monospace";                      // use same font in canvas as for pre
ctx.strokeStyle = "#d00";
ctx.translate(0.5, 0.5);                          // makes lines sharper for demo

window.onscroll = drawBoxes;                      // we need to track scrolling
drawBoxes();

function drawBoxes() {                            // render line boxes (y)
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for(var i = 0; i < count; i++) {
    var w = ctx.measureText(lines[i]).width;
    if (w) ctx.strokeRect(rect.left, rect.top + i * lineH - window.scrollY, w, lineH - 1);
    showChars(lines[i], rect.top + i * lineH - window.scrollY, lineH); 
  }
}
function showChars(line, y, h) {                  // render char lines (x)
  ctx.beginPath();
  for(var i = 0, ch, x, s = ""; ch = line[i]; i++) {
    s += ch;
    x = ctx.measureText(s).width;
    ctx.moveTo(x, y); ctx.lineTo(x, y + h - 1);
  }
  ctx.globalAlpha = 0.2;
  ctx.stroke();
  ctx.globalAlpha = 1;
}
canvas {position:fixed;left:0;top:0;z-index:-1}
pre {font:14px monospace}
<canvas></canvas>
<pre>//COBLPGM  EXEC PGM=COBLPGM
//INPUT    DD DSN=SORT,DISP=(OLD,DELETE)
//NACHA    DD DSN=NODE.OPER.COBLPGM.OUT(+1)

//SORT2    EXEC PGM=SORT
//SORTIN   DD DSN=NODE.OPER.COBLPGM.OUT(+1)
//SORTOUT  DD DSN=SORT2,DISP=(,PASS)
//SYSIN    DD DSN=NODE.OPER.PROCLIB(MEM)

//UNRELATE EXEC PGM=UNPGM
//INPUT    DD DSN=NODE.OPER.UNRELATED.FILE
//REPORT   DD DSN=REPORT

//TSTEMPT1 EXEC PGM=SPOPNCLO
//IN       DD DSN=SORT2,DISP=(OLD,DELETE)

//    IF TSTEMPT1.RC=0 THEN

//SORT3    EXEC PGM=SORT
//SORTIN   DD DSN=NODE.OPER.COBLPGM.OUT(+1)
//SORTOUT  DD DSN=SORT3,DISP=(,PASS),LRECL=141
//SYSIN    DD DSN=NODE.OPER.CNTRLCDS(PARM)

//    ENDIF</pre>