使用jquery和canvas从html元素id到另一个html元素绘制线条

时间:2013-12-23 23:24:45

标签: javascript jquery html css canvas

是否可以通过引用元素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之间画一条线。有可能吗?

提前致谢!

2 个答案:

答案 0 :(得分:4)

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

example output

该方法是创建一个浮动画布元素(粉红色阴影),并将其放置在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();
}

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

为了防止线条被画布边缘切断,有一个阴沟设置可以填充画布的宽度和高度。其余的只是在客户端DOM坐标和在画布本身上绘制的坐标之间进行坐标平移。

唯一的另一个不直观的位是计算沿着一条线的框边界上的点的坐标。它并不完美,但它是一个合理的开始。关键是从源(to)点的角度计算目标(from)点的角度,并看看它与盒角的已知角度的比较:

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(),其中包括左边,顶部,宽度,高度以及其他内容,我用来查找框的中心:

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

将所有这些组合在一起,您可以从一个元素到另一个元素绘制一条线。

答案 1 :(得分:3)

我做了一个非常粗略的例子,说明我将如何解决这个问题。它应该让你在正确的轨道上:

我将class line用于hoverable元素,并使用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/