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.
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:
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.