【问题标题】:Draw line from html element id to another html element with jquery and canvas使用jquery和canvas从html元素id画线到另一个html元素
【发布时间】:2013-12-23 23:24:45
【问题描述】:

是否可以通过引用元素id来用html和jquery画一条线?我在文本中有一个重要的词,想在这个词和描述它的图像之间画一条线。我已经看到可以使用画布在元素之间绘制,但它们的样式位置设置为绝对。由于我的元素是文本中的一个单词,因此我无法将其设置为绝对值。 示例

<p>This is my text with this very <span id="important_word">important</span> word</p>
...
<img src="important.jpg" id="important_img"/>

现在我想在 span 和 img 之间画一条线。有可能吗?

提前致谢!

【问题讨论】:

    标签: javascript jquery html css canvas


    【解决方案1】:

    由于这个问题不时出现,我已经付出了一些努力。它不是 jquery,所以你可以在某种程度上简化。仅供参考,这个答案也发布在an answer to this other question,但请求是一样的。使用该问题的html和CSS,这里有一个jsbin演示http://jsbin.com/guken/3/

    方法是创建一个浮动画布元素(粉红色阴影),并将其放在 DOM 的其余部分下方(使用 z-index)。然后我计算两个框的边界上与框中心之间的线相对应的点。红色和蓝色方块实际上是随着行尾移动的div,可以用于源、目标等注释。

    在那个 jsbin 中,您可以单击一个元素,然后准备一行以单击下一个元素。它会检测所选元素的悬停并在您将鼠标悬停在一个目标上时捕捉到目标。

    我不会在这里粘贴所有代码,但是我们在客户端 DOM 坐标中从一个 x,y 位置到另一个位置画一条线的位置是这样的:

    var lineElem;
    function drawLineXY(fromXY, toXY) {
        if(!lineElem) {
            lineElem = document.createElement('canvas');
            lineElem.style.position = "absolute";
            lineElem.style.zIndex = -100;
            document.body.appendChild(lineElem);
        }
        var leftpoint, rightpoint;
        if(fromXY.x < toXY.x) {
          leftpoint = fromXY;
          rightpoint = toXY;
        } else {
          leftpoint = toXY;
          rightpoint = fromXY;
        }
    
        var lineWidthPix = 4;
        var gutterPix = 10;
        var origin = {x:leftpoint.x-gutterPix, 
                      y:Math.min(fromXY.y, toXY.y)-gutterPix};
        lineElem.width = Math.max(rightpoint.x - leftpoint.x, lineWidthPix) + 
          2.0*gutterPix;
        lineElem.height = Math.abs(fromXY.y - toXY.y) + 2.0*gutterPix;
        lineElem.style.left = origin.x;
        lineElem.style.top = origin.y;
        var ctx = lineElem.getContext('2d');
        // Use the identity matrix while clearing the canvas
        ctx.save();
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        ctx.clearRect(0, 0, lineElem.width, lineElem.height);
        ctx.restore();
        ctx.lineWidth = 4;
        ctx.strokeStyle = '#09f';
        ctx.beginPath();
        ctx.moveTo(fromXY.x - origin.x, fromXY.y - origin.y);
        ctx.lineTo(toXY.x - origin.x, toXY.y - origin.y);
        ctx.stroke();
    }
    

    由于示例只有一行,我们总是可以存储已经“完成”的行以准备创建更多行,它使用全局变量lineElem。在第一次尝试画线时,它会创建一个画布元素,将其插入 DOM 并将其分配给 lineElem。在此构造之后,它随后重用 canvas 元素,更改大小并重新绘制新的坐标对。

    为了防止线条被画布的边缘切断,有一个装订线设置可以填充画布的宽度和高度。剩下的就是在客户端 DOM 坐标和画布本身的绘图坐标之间进行坐标转换。

    唯一不简单的一点是计算沿一条线的框边界上的点的坐标。这并不完美,但它是一个合理的开始。关键是从源 (from) 点的角度计算目标 (to) 点的角度,看看它与框角的已知角度相比如何:

    function getNearestPointOutside(from, to, boxSize) {
        // which side does it hit? 
        // get the angle of to from from.
        var theta = Math.atan2(boxSize.y, boxSize.x);
        var phi = Math.atan2(to.y - from.y, to.x - from.x);
        var nearestPoint = {};
        if(Math.abs(phi) < theta) { // crosses +x
            nearestPoint.x = from.x + boxSize.x/2.0;
            nearestPoint.y = from.y + ((to.x === from.x) ? from.y : 
            ((to.y - from.y)/(to.x - from.x) * boxSize.x/2.0));
        } else if(Math.PI-Math.abs(phi) < theta) { // crosses -x
            nearestPoint.x = from.x - boxSize.x/2.0;
            nearestPoint.y = from.y + ((to.x === from.x) ? from.y : 
            (-(to.y - from.y)/(to.x - from.x) * boxSize.x/2.0));
        } else if(to.y > from.y) { // crosses +y
            nearestPoint.y = from.y + boxSize.y/2.0;
            nearestPoint.x = from.x + ((to.y === from.y) ? 0 : 
            ((to.x - from.x)/(to.y - from.y) * boxSize.y/2.0));
        } else { // crosses -y
            nearestPoint.y = from.y - boxSize.y/2.0;
            nearestPoint.x = from.x - ((to.y === from.y) ? 0 :
            ((to.x - from.x)/(to.y - from.y) * boxSize.y/2.0));
        }
        return nearestPoint;
    }
    

    Theta 是与第一个框角的角度,phi 是实际的直线角度。

    要获取框在客户端坐标中的位置,您需要使用elem.getBoundingClientRect(),它会产生left、top、width、height等,我使用它来找到框的中心:

    function getCentreOfElement(el) {
        var bounds = el.getBoundingClientRect();
        return {x:bounds.left + bounds.width/2.0,
                y:bounds.top + bounds.height/2.0};
    }
    

    将所有这些放在一起,您可以从一个元素到另一个元素画一条线。

    【讨论】:

      【解决方案2】:

      我做了一个非常粗略的例子来说明我将如何去做。不过,它应该会让你走上正轨:

      我为可悬停的元素使用了类行,为相应的元素使用了 data-id 属性。

      HTML:

      <p>This is my <span class="line" data-id="important_img">text</span> with this very <span class="line" data-id="important_img2">important</span> word</p>...
      
      <img src="important.jpg" id="important_img" />
      <br>
      <br>o asf isj biojso jo f ad f
      <img src="important.jpg" id="important_img2" />
      

      jQuery:

      $('.line').hover(function () {
          var $t = $(this);
          var $i = $('#' + $t.data('id'));
      
          // find offset positions for the word (t = this) and image (i)
          var ot = {
              x: $t.offset().left + $t.width() / 2,
              y: $t.offset().top + $t.height() / 2
          };
          var oi = {
              x: $i.offset().left + $i.width() / 2,
              y: $i.offset().top + $i.height() / 2
          };
      
          // x,y = top left corner
          // x1,y1 = bottom right corner
          var p = {
              x: ot.x < oi.x ? ot.x : oi.x,
              x1: ot.x > oi.x ? ot.x : oi.x,
              y: ot.y < oi.y ? ot.y : oi.y,
              y1: ot.y > oi.y ? ot.y : oi.y
          };
      
          // create canvas between those points
          var c = $('<canvas/>').attr({
              'width': p.x1 - p.x,
              'height': p.y1 - p.y
          }).css({
              'position': 'absolute',
              'left': p.x,
              'top': p.y,
              'z-index': 1
          }).appendTo($('body'))[0].getContext('2d');
      
          // draw line
          c.strokeStyle = '#f00';
          c.lineWidth = 2;
          c.beginPath();
          c.moveTo(ot.x - p.x, ot.y - p.y);
          c.lineTo(oi.x - p.x, oi.y - p.y);
          c.stroke();
      }, function () {
          $('canvas').remove();
      });
      

      演示:

      http://jsfiddle.net/v8pbc/

      【讨论】:

      • 非常感谢安迪!这是我不知道的方法offset(),节日快乐:)
      猜你喜欢
      • 2019-06-04
      • 2015-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-21
      • 2013-04-26
      • 1970-01-01
      • 2015-03-25
      相关资源
      最近更新 更多