"Drawing" 超过 html 个文本

"Drawing" over html text

我需要做到以下几点:拥有一个普通的网页,能够使用鼠标、手写笔或手指(如果是平板电脑或智能手机)在文本上“绘制形状”,然后使用信息形状,确定某些“动作”。

我举个例子。想象一下 Whosebug 上的这个问题,我在 ipad 上围绕这一段“画”了一个圆圈。圆圈(或者圆圈的尝试,手工绘制总是很不完美),“捕获”段落,所以我知道我会在“段落”上做一个动作,比如,select all里面的文字。

我的问题是:这可行吗?有没有这样做的图书馆?如果不是,可以研究哪些技术?

我一直在进行自己的研究,但没有任何东西能满足我的所有需求。例如,canvas 完全按照我的需要绘制,但是 canvas 不能用于 html 页面。它是页面上的另一个元素,而不是“包裹”另一个元素。

所以回答最初的问题:这可行吗?

首先,您需要能够在页面上绘图。 HTML5 标签对此很有用,因为它允许我们很容易地在页面上绘制

其次,您需要能够识别页面上的哪些元素已被圈出。 javascript 函数 .getBoundingClientRect() 对此很有用,因为它告诉我们 x 和 y 位置以及宽度和高度。因为这些值是基于它们相对于文档边界框的位置(而不是父元素),我们可以将其与我们绘制的圆的坐标进行比较。

所以有了这两个组件,我们可以把这样的东西放在一起......(我承认这有点笨拙,但它可以根据你的具体用例进行优化):

var canvas, ctx, flag, coords, cntnr, dot_flag;
    
    function _InitDraw() {
      flag = false;
      coords = {
        pX: 0,
        cX: 0,
        pY: 0,
        cY: 0
      };
      cntnr = {
        minX: document.body.offsetWidth,
        minY: document.body.offsetHeight,
        maxX: 0,
        maxY: 0
      };
      dot_flag = false;

      canvas = document.createElement('canvas');
      canvas.id = "cnvTest";
      canvas.style.position = "fixed";
      canvas.style.top = 0;
      canvas.style.left = 0;
      document.body.appendChild(canvas);
      canvas.style.width = "100%";
      canvas.style.height = "100%";
      canvas.style.zIndex = 9001;
      ctx = canvas.getContext("2d");
      ctx.canvas.width = window.innerWidth;
      ctx.canvas.height = window.innerHeight;
  
      canvas.addEventListener("mousemove", function (e) { _Draw('move', e); }, false);
      canvas.addEventListener("mousedown", function (e) { _Draw('down', e); }, false);
      canvas.addEventListener("mouseup", function (e) { _Draw('up', e); }, false);
      //canvas.addEventListener("mouseout", function (e) { _Draw('out', e); }, false);
    }
    
    function _Draw(action, e) {
      if(action == 'down') {
        coords.pX = coords.cX;
        coords.pY = coords.cY;
        coords.cX = e.pageX;
        coords.cY = e.pageY;
        
        if(coords.cX < cntnr.minX) cntnr.minX = coords.cX;
        if(coords.cY < cntnr.minY) cntnr.minY = coords.cY;
        if(coords.cX > cntnr.maxX) cntnr.maxX = coords.cX;
        if(coords.cY > cntnr.maxY) cntnr.maxY = coords.cY;

        flag = true;
        dot_flag = true;
        if(dot_flag) {
          ctx.beginPath();
          ctx.fillStyle = "black";
          ctx.lineCap = 'round';
          ctx.fillRect(coords.cX, coords.cY, 2, 2);
          ctx.closePath();
          dot_flag = false;
        }
      }
      if(action == 'up' || action == "out") {
        flag = false;
        _GetText();
      }
      if(action == 'move') {
        if(flag) {
          coords.pX = coords.cX;
          coords.pY = coords.cY;
          coords.cX = e.pageX;
          coords.cY = e.pageY;

          if(coords.cX < cntnr.minX) cntnr.minX = coords.cX;
          if(coords.cY < cntnr.minY) cntnr.minY = coords.cY;
          if(coords.cX > cntnr.maxX) cntnr.maxX = coords.cX;
          if(coords.cY > cntnr.maxY) cntnr.maxY = coords.cY;
          
          ctx.beginPath();
          ctx.moveTo(coords.pX, coords.pY);
          ctx.lineTo(coords.cX, coords.cY);
          ctx.strokeStyle = "black";
          ctx.lineCap = 'round';
          ctx.lineWidth = 4;
          ctx.stroke();
          ctx.closePath();
        }
      }
    }

    function _GetText() {
      // Find position and get closest element
      document.body.removeChild(canvas);

      var allEls = document.body.getElementsByTagName("*");
      for(var i = 0; i < allEls.length; i++) {
        var elBox = allEls[i].getBoundingClientRect();
        if(elBox.x > cntnr.minX && elBox.x+elBox.width < cntnr.maxX && elBox.y > cntnr.minY && elBox.y+elBox.height < cntnr.maxY) {
          // Do whatever you want with the element here.
          // I am adding an outline for confirmation of the circled elements.
          allEls[i].style.outline = "2px solid rgba(255, 255, 0, 0.8)";
          if(allEls[i].innerText !== null && allEls[i].innerText !== "undefined") {
            // Do something with the innerText here if you need the text from the element.
            console.log(allEls[i].innerText);
          }
        }
      }
    }

为了了解一点上下文,您需要先调用 _InitDraw() 函数。这将在应该覆盖整个页面的页面上创建一个 元素。

一旦您松开鼠标,它就会结束绘图模式并使用最小和最大位置值并遍历页面上的所有元素以查看是否有任何元素在这些范围内。

出于示例目的,此脚本随后概述了这些对象并将其中的任何文本记录到控制台。

*如果您想将此用于移动设备,则需要更改事件侦听器(touchstart、touchmove、touchend)。

**另请注意,某些元素(例如任何 'block' 元素)的边界框可能比您在视觉上看到的文本大得多。所以这个脚本不会'capture'那些元素。当然,您可以修改脚本以包含图形传递的任何元素,但这可能会导致混乱的结果。