While simple circles and paths are easy enough to generate by hand-coding or a vector application, complex illustrations with regular geometry are often easiest to generate with some programming. A good example of the latter is a compass rose, shown above, which was mostly created using JavaScript.
The Markup
I started the design on the page with a SVG element that contains only symbols:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 500 500" id="compassrose">
<defs>
<symbol>
<line x1="40" y1="250" x2="50" y2="250" id="roseline" />
<line x1="40" y1="250" x2="60" y2="250" id="majline" />
<path d="M10,250a240,240 0 1,0 480,0a240,240 0 1,0 -480,0"
id="rosecircle" />
</symbol>
</defs>
</svg>
Because <symbol>
elements do not render by themselves, the SVG won’t be “seen” until it is completed with JavaScript. The <path>
will be used as a guide to place elements on a circle, including the short and long degree lines (roseline
and majline
, respectively).
The CSS
The SVG is styled so that it is in the center of the page, and the lines and text provided with an appearance with CSS:
#compassrose {
width: 40%;
border: 1px solid rgba(255,255,255,0.3);
margin-left: 30%;
border-radius: 50%;
}
#roseline, #majline {
stroke: #eee;
stroke-width: .5;
}
#compassrose text {
font-family: Montserrat, sans-serif;
font-size: 10;
fill: #eee;
}
Note the slightly unusual appearance of the svg
element, which is given a border-radius
to make it appear as the outside of the compass: the SVG element itself can be made to look circular.
Creating the Lines
The script to make the compass lines is added to the end of the document:
var lineInc = 2,
majMarkDegree = 10,
degreeInc = 30,
compassrose = document.getElementById("compassrose"),
xmlns = "http://www.w3.org/2000/svg",
xlink = "http://www.w3.org/1999/xlink";
if (lineInc > 0) {
for (var i=0; i < 360; i+=lineInc) {
var newline = document.createElementNS(xmlns,'use');
if (i % majMarkDegree == 0) {
newline.setAttributeNS(xlink,'xlink:href','#majline');
} else {
newline.setAttributeNS(xlink,'xlink:href','#roseline');
}
newline.setAttributeNS(null,'transform','rotate('+i+' 250 250)');
compassrose.appendChild(newline);
}
The variables are:
lineInc
: how many degrees apart the markings aremajMarkDegree
: how many degrees apart the major markings aredegreeInc
: the numerical separation between the degrees printed around the edge of the circle
The for
loop increments by the amount specified by lineInc
. At every increment, a <use>
element is created. If the incremented amount is divisible by majMarkDegree
via a modulus operator, then the majline
is used; otherwise, roseline
is added instead. Each line is rotated into the orientation provided by i
.
The Degree Markers
The degree markers use startOffSet
to position the text around the edge of the compass. startOffSet
takes values from 0
to 100
, so the loop is based on that.
Above 0 - 9 degrees, the printed numeral will be slightly out of alignment on the circle, since the text starts at the degree point. I’ve used a somewhat unusual equation with log
to determine how many numerals are in the number, and (if it is longer than a single digit), the script pulls the rotation of the number back by a degree:
var writeDegs = document.createElementNS(xmlns,'text'),
currentDeg = 0,
writeOffset = 0;
for (var i=0; i < 99; i+=(degreeInc/360)*100) {
var degree = document.createElementNS(xmlns,'textPath');
degree.setAttributeNS(xlink,'xlink:href','#rosecircle');
var length = Math.log(i) * Math.LOG10E + 1 | 0;
if (length > 1) { writeOffset = 1; }
degree.setAttributeNS(null,'startOffset',(i - writeOffset)+"%");
degree.textContent = currentDeg;
writeDegs.appendChild(degree);
currentDeg += degreeInc;
}
compassrose.appendChild(writeDegs);
This isn’t perfectly accurate mathematically (to achieve that would require a bit more JavaScript) but it’s close enough for our purposes.
The Animation
I’ve also animated the compass to sway back and forth using the Web Animation API, using a similar technique to my “Random Walk” article. (Note that this animation will only work in Chrome and Firefox, at least currently).
function randomRot() {
var oldOrientation = newOrientation;
newOrientation = Math.floor(Math.random() * 240);
compassrose.animate([
{ transform: 'rotate('+ oldOrientation+'deg)' },
{ transform: 'rotate('+ newOrientation+'deg)' }
], {
duration: Math.abs(oldOrientation - newOrientation) * 30,
fill: 'forwards'
}).onfinish = function() {
randomRot();
}
}
newOrientation = 0;
randomRot();
The function compares the oldOrientation
of the compass with the newOrientation
of the element (a random number between 0 and 240, interpreted as degrees) and animates between them, with a duration calculated as the difference between the orientations multiplied by 30 (interpreted as time in milliseconds).
Conclusion
There are many other ways to create SVG with JavaScript, which I’ll go into more depth in future articles; for now, I hope this might prove a useful starting point for your own experiments.
Photograph by Mel Foody, licensed under Creative Commons.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/yOyzpy