15

I am using JQuery-UI draggables and droppables to clone elements onto a div. What is the best way to draw a line between elements on a page using JQuery.

What is the best way to refresh lines on the page? I will have multiple lines on the page and only want to update a particular line rather than refresh every line.

StuperUser
  • 10,555
  • 13
  • 78
  • 137
  • 1
    you should check this question: http://stackoverflow.com/questions/536676/how-to-draw-a-line-between-draggable-and-droppable – nunopolonia Jan 17 '11 at 10:42
  • +1 for asking a question on my 'how to' list for my web app--I look forward to these answers; Yashwant Chavan's link is already intriguing! – jlmakes Jan 17 '11 at 10:43
  • Thanks @nunopolnia, I had seen that question before and liked the answer. Trying to implement it to draw a line between two draggables that moved afterwards was pretty painful, so I changed tack. Also wondered whether using `jquery.svg` for lines was better than using `jquery.drawinglibrary` on top of it. – StuperUser Jan 17 '11 at 10:57
  • This question: http://stackoverflow.com/questions/2368492/jquery-remove-drawn-line-added-via-jquery-svg-library seems to have more information on removing particular lines. – StuperUser Jan 17 '11 at 11:28
  • Refer this 1] http://servut.us/akx/stackoverflow/jquery-canvas-lines.html 2] http://www.openstudio.fr/Library-for-simple-drawing-with.html?lang=fr – Vicky Jan 17 '11 at 10:41
  • I wonder if the line will animate along with a mouse drag on a draggable div.... hmm... – jlmakes Jan 17 '11 at 10:48
  • Thanks Yashwant, do you know whether it's possible to isolate a line on the canvas to update rather than redrawing the entire canvas? – StuperUser Jan 17 '11 at 10:58
  • Also, I've tried using `jquery.drawinglibrary` before, if I'm only drawing a line, isn't it simpler to use `jquery.svg` rather than use another library on top of it? – StuperUser Jan 17 '11 at 11:07

2 Answers2

14

I now have this working.

In my experience, don't use jquery.svg, it may have been the pressure to solve it without learning my way around another plugin, but I found it more hassle than it was worth and caused compatibilty issues.

It's possible to solve using the HTML5 canvas and excanvas compatibility script, and a great html5 walkthrough, BUT because of how the HTML5 canvas works, it requires that all the linse on the canvas are destroyed and redrawn if a line needs to be removed or its position needs to be updated.

The elements that I draw lines between are inside a parent element that represents a relationship. The child elements represent a start and end, so I can redraw all of these relationships by getting a collection of the parents using e.g. $('.relationshipClass') and interrogating the set's elements' children to get the points of the line.
To use this code you will have to come up with an approach to easily get the line points available to redraw.

Markup:
Nice and simple, a html <div> to hold any elements that are drawn between (most probably JQuery UI draggables), and a <canvas> that will be in the same position

 <div id="divCanvasId" class="divCanvasClass"></div>
 <canvas id="html5CanvasId"></canvas>

CSS:
Don't control the <canvas> element's width with CSS, see Question on Canvas streching. Position the <canvas> in the same position as the <div> and behind it (with the z-index), this will show the lines up behind the <div> and prevent the <canvas> from blocking any drag and drop interactions with the <div>'s children.

canvas
{
    background-color: #FFFFFF;
    position: absolute;
    z-index: -10;
    /* control height and width in code to prevent stretching */
}

Javascript approach:
Create utility methods: the example code is inside a JQuery plugin that contains:

  • A helper function to reset the canvas (changing the width will delete all of
  • A helper function to draw lines between two elements
  • A function that draws lines between all the elements that require one

When you add a new line or adjust the position of a line, you destroy the existing lines and draw all of the lines. You can put the code below into conventional functions or leave as a plugin.

Javascript code:
N.B. not tested after anonymisation.

$(document).ready(function () {
    $.fn.yourExt = {

        _readjustHTML5CanvasHeight: function () {
            //clear the canvas by readjusting the width/height
            var html5Canvas = $('#html5CanvasId');
            var canvasDiv = $('#divCanvasId');

            if (html5Canvas.length > 0) {
                html5Canvas[0].width = canvasDiv.width();
                html5Canvas[0].height = canvasDiv.height();
            }
        }
        ,
        //uses HTML5 <canvas> to draw line representing relationship
        //IE support with excanvas.js
        _drawLineBetweenElements: function (sourceElement, targetElement) {

            //draw from/to the centre, not the top left
            //don't use .position()
            //that will be relative to the parent div and not the page
            var sourceX = sourceElement.offset().left + sourceElement.width() / 2;
            var sourceY = sourceElement.offset().top + sourceElement.height() / 2;

            var targetX = targetElement.offset().left + sourceElement.width() / 2;
            var targetY = targetElement.offset().top + sourceElement.height() / 2;

            var canvas = $('#html5CanvasId');

            //you need to draw relative to the canvas not the page
            var canvasOffsetX = canvas.offset().left;
            var canvasOffsetY = canvas.offset().top;

            var context = canvas[0].getContext('2d');

            //draw line
            context.beginPath();
            context.moveTo(sourceX - canvasOffsetX, sourceY - canvasOffsetY);
            context.lineTo(targetX - canvasOffsetX, targetY - canvasOffsetY);
            context.closePath();
            //ink line
            context.lineWidth = 2;
            context.strokeStyle = "#000"; //black
            context.stroke();
        }
        ,

        drawLines: function () {
        //reset the canvas
        $().yourExt._readjustHTML5CanvasHeight();

        var elementsToDrawLinesBetween;
        //you must create an object that holds the start and end of the line
        //and populate a collection of them to iterate through
        elementsToDrawLinesBetween.each(function (i, startEndPair) {
            //dot notation used, you will probably have a different method
            //to access these elements
            var start = startEndPair.start;
            var end = startEndPair.end;

            $().yourExt._drawLineBetweenElements(start, end);
        });
    }

You can now call these functions from event handlers (e.g. JQuery UI's drag event) to draw lines.

Community
  • 1
  • 1
StuperUser
  • 10,555
  • 13
  • 78
  • 137
-4

Make a hr or div with 1px height set background, and animate its width using jquery when needed.

hungryMind
  • 6,931
  • 4
  • 29
  • 45
  • 1
    This won't work if the elements aren't on the same Y axis. Using draggables, it's likely that this won't be the case. – StuperUser Jan 17 '11 at 11:05