HTML offers the
<canvas>, but with some limitations, it's pixel based but can use SVGs but generally meant as "viewport" as opposed to DOM spanning. Rather than go into the "whys," Canvas doesn't quite fit what I'm after to create. SVGs can be positioned via absolute positioning anywhere on the viewport (just like any DOM object). Unlike other image types, the content inline SVGs can easily be altered via the DOM as they're XML data. This means I can easily change the color, or size, even shape of objects.
Hopefully, this tutorial is understandable for novices, more seasoned devs may want to skip the bottom for the codepen example. I've written my tutorial using only ES5 syntax although my codepen has some ES6 syntax.
- Draw an SVG line between objects on the screen.
- On resize change the SVGs position in objects on the screen have changed.
- Allow to have lines between multiple objects, and do this dynamically.
The above will create a black line 1px wide that starts at 0 pixels and span 100 pixels to the right and 100 pixels down, to create a diagonal line.
Pictured: simple SVG line using the above code
It's 2018, but jQuery still has its place,
offset reliably can get us the absolute positioning of elements on the screen to the document as its base even if they aren't absolute positioned. This isn't a complete win for our goal of drawing a point between two objects as this only gets us the top-left corner of our a
<div>. We need the center of that a
So with a bit of simple math, we can figure out the center position of a
<div> by querying the width and height of the div and dividing by 2, then adding it to the offset position. This will measure diagonally to the center.
Now to draw an SVG, we need two sets of center coordinates. X1, Y1 and X2, Y2.
Now if we just apply this to
#mySVG, we can draw a line that goes between the center of these two hypothetical DOM objects. We want to place the SVG in our body tag and then give it some really basic styling in CSS so it can occupy any space on the viewport.
Not bad right? What happens if we resize? Our hypothetical DOM objects on the screen might move, thus we'd need a window resize event. We better make this a function now, and clean up the legibility first.
Adding resizing is pretty easy now:
Pretty nifty right? Now that we've covered the basics of drawing and redrawing the SVG, we can use jQuery's
clone to duplicate our line within our SVG and call our drawSVG multiple times.
This isn't very dynamic as we're assuming we always know that we want to draw a line between 3 things on our screen. We're getting close but this isn't dynamic. It's time to break out .each and convert what we have into an object to cut down on our mess. We're going to do a few changes. First let's simplify our SVG.
Now that we're going to copy and paste our SVG, we don't need any co-ordinates. In fact, we do not want them at all until. A line without the required
y2 won't be rendered to the screen. This works for us as we want to use this as a template for future lines but do not want our original to display.
Also, for legibility, we can turn the messy code above into something more readable and hopefully maintainable.
Let's also assume all our hypthetical myPoints use the class
.myPoint and not IDs. We can now call our function
drawBetweenObjects.drawSVG($(".myPoint"), more paramets)
iterateOverObject has some funky stuff, such as
$(this) which you have probably seen before. jQuery's
this aren't quite the same, What's the difference between '$(this)' and 'this'? but in the array of objects, it will use the current entry. Next we will need to select the next item in our query,
eq creates a new a query to the specific entry on the array of objects. So if I ask for
var myLi = $("li"), and there are four
<li>s on the page,
myLi.eq(2) would only require the data for the second
<li>. Using our index, we ask next in the list, using
index + 1.
Now we can update our script resizing.
Removing old clones
If you run the code, you'll be able to redraw the points, but the problem is our old lines are still in the DOM. So the best place to remove them is before we re-iterate over the object. So before we create new lines, we delete the old ones. Time to add a simple jQuery remove to the
Now we should have an SVG line that will draw between any DOM objects with the class of
.myPoint and redraw the lines on a window resize.
Below is the hyper spiffy version that has a config file and the ability to delay rerenders on resize. To make this work for pages larger than 100vh, the SVG height of the
#svg would need to set the height as
It'd be feasible to make this without jQuery, by replacing jQuery with
Document.querySelectorAll() and removing the offset with techniques such as