Geeking-out on SVG Graphics part-three
Tracy Gilmore
Posted on May 12, 2022
Reprise
My project is to create an SVG-based backdrop in the style of a cutting mat for software engineers (well me, not really anyone else, apologies for being selfish).
As covered in the previous post, I have a grid 4000px by 2500px with cells of 250px square and bounded with a slightly thicker rectangle. Also, as stated in the previous post, I have chosen a background colour based on engineer's marking fluid (#191662) and a grey (#777) for the grid lines and the by-line.
In this third post I will be representing a small number of screen resolutions using actual screen dimensions. We will also show ratio projection lines and labels. See the initial post for more details.
I will release the cutting-mat SVG image at the end of each stage via my GitHub repo.
Screen Resolutions
As the technology behind computer displays has progressed we have seen a significant increase in the number of pixels that can be used to present graphical information on screen. We have also seen displays getting wider but in general the width and height a screen presents (in pixels) conforms to an industry standard and align to a common ratio.
Display units (known as Cathode Ray Tube (CRT) displays) of the 1980's and 90's typically assumed a ratio of 4:3, for every four pixels of width there would be three pixels in height.
Examples includes:
Screen Resolutions | Width | Height |
---|---|---|
VGA | 640 | 480 |
SVGA | 800 | 600 |
XGA | 1024 | 768 |
UXGA | 1600 | 1200 |
More recently people will be familiar with flat-panel High Definition (HD) screens, which assume a 16:9 resolution ratio.
Screen Resolutions | Width | Height |
---|---|---|
HD 720p | 1280 | 720 |
Full HD 1080p | 1920 | 1080 |
UHD 2K/QHD 1440p | 2560 | 1440 |
UHD 4K | 3840 | 2160 |
Wikipedia has an excellent page on this topic.
In this step of the project I want to represent each screen resolution using actual pixel dimensions. The rectangles will be anchored to the top-left corner of the grid with a label to indicate the standard and resolution attached to the bottom-right corner. Two projection lines from the origin will pass through the diagonal for each rectangle to link the common resolutions.
Let's get coding
We will update the project in three stages, first we establish a containing svg
element with three group
elements within it.
Stage One - SVG and group containers
There will be one group for the ratio projection lines, a second for the rectangles and labels, and a third for the title text and an anchor point. The order is significant as the graphics will be plotted on top of each other in group
element order.
<svg x="20" y="20" width="1360" height="880"
id="resolution" viewBox="-125 -125 4250 2750">
<g class="ratios"></g>
<g class="rectangles"></g>
<g class="labels">
<text x="25" y="-25" class="title">Screen Resolutions</text>
<circle cx="0" cy="0" r="15"></circle>
</g>
</svg>
Things to notice in the above code include:
- See how the dimensions of the
svg
element (1360 x 880) is considerably smaller than the largest screen resolution I want to plot (3840 x 2160). - We have an attribute of
viewBox
that offsets whatever is presented insidesvg
element#resolution
by 125px up and to the left. We have also remapped the 1360x880 actual pixels to 4250x2750 'virtual' pixels. - The first and second groups are currently empty but the third contains hard-coded SVG elements for the title text and the origin circle.
The projection lines will start at the same origin as the screen resolution rectangles and extend to the edge of the grid. As the grid itself has an aspect ratio of 16:10 neither projection line will pass through the bottom-right corner of the grid. The 16:9 line will pass trough the right-side edge and the 4:3 line will go through the bottom edge.
Stage two - update the styling
First we add a custom property to provide a common colour.
--resolution-colour: #fcc;
Next we add styling for the content.
#resolution line,
#resolution rect {
stroke: var(--resolution-colour);
stroke-width: 3;
fill: transparent;
}
#resolution circle {
stroke: var(--resolution-colour);
stroke-width: 2;
fill: var(--background-colour);
}
#resolution .labels circle,
#resolution text {
fill: var(--resolution-colour);
}
#resolution .ratios text {
font-size: 3rem;
text-anchor: middle;
}
#resolution .labels text {
font-size: 2.5rem;
text-anchor: end;
}
#resolution .labels .title {
font-size: 4rem;
text-anchor: start;
}
We are referencing the id
attribute of the SVG
element to target the text, lines, circles and rectangles it contains to apply styling.
Finally - We have the JavaScript required to populate the empty group elements
We start by updating the main function to call drawResolutions
that calls drawProjectionLines
for the first group and drawScreenResultions
for the second group, as follows.
(function () {
drawGrid();
drawResolutions();
})();
// :
function drawResolutions() {
drawProjectionLines();
drawScreenResultions();
}
The following functions follow a similar pattern:
- Locate the DOM entry point (group element) using the
document.querySelector('#resolution ...
method. - Prepare the data to be rendered.
- Traverse the data and for each item generate the SVG instruction(s) required to represent the data.
drawProjectionLines
function drawProjectionLines() {
const resolutionRatios = document.querySelector('#resolution .ratios');
const ratioLines = [
{ w: 3333, h: 2500, t: '4:3' },
{ w: 4000, h: 2250, t: '16:9' },
];
ratioLines.forEach(
r => (resolutionRatios.innerHTML += `
<line x1="0" y1="0" x2="${r.w}" y2="${r.h}"
stroke-dasharray="8,8"></line>
<circle cx="${r.w}" cy="${r.h}" r="60"></circle>
<text x="${r.w}" y="${r.h + 20}">${r.t}</text>
`)
);
}
So, we locate the group in the resolution SVG
element with a class of ratios
. We prepare an array contain the data for the two projection lines including the width (w), height (h) and text (t) label.
For each item in the array we create the SVG instructions for a projection line using a template literal (string) and inject it into the DOM using the innerHTML
attribute. This might not be the most performant approach but it works and performance is not a major concern, especially for two data items.
The string consists of three SVG elements;
- the line running from the origin to the 'w' and 'h' coordinates. We will use a dashed line for the projection lines via the
stroke-dasharray
attribute. - a large filled circle is drawn on top of the end of the projection line where is intersects with the edge of the grid.
- within the boundary of the circle we add a
text
element stating the ratio the projection line represents. The vertical location of the text is 20px lower than the centre of the circle.
drawScreenResultions
So, I cheated but for good reason. The drawScreenResultions
function is actually split in two further function calls:
- The
drawScreenResultionsRectangles
function draws the rectangles representing the screen resolutions. - The
drawScreenResultionsLabels
function adds the labels, which uses a different resolution otherwise the text would be too tinu to read. Both functions use the same data set that is created in thedrawScreenResultions
function and passed as an attribute to the sub functions. The data consists of an array of objects, one for each screen resolution, and containing the following properties: - width
- height
- spec: the name of the standard the resolution applies
- xOffset: the number of pixels left/right to shift the text so it aligns with the rectangle.
- yOffset: similar to xOffset, it is used to position the text relative to the rectangle representing the screen resolution. I will not present the data in this post as it is quite lengthy but ask you check out the repo if you are interested.
With the data already defined the sub functions just have to locate the entry point in the DOM and render the appropriate graphics.
drawScreenResultionsRectangles
The entry point is the group with the class of rectangles
and the rendering is rectangle that starts at the origin and uses the prescribed width and height, which is also used to locate a small filled circle.
drawScreenResultionsLabels
Using the entry point in the DOM of the group with the 'labels' class, this function injects the text at the prescribed location.
There we have it. We have a cutting-mat with a grid, by-line, Creative-commons link and presenting a selection of the (arguably) most common screen resolutions along with their ratio projection lines.
In the next instalment I will be adding some 'standard angles' and using the path
element to draw some arcs, which is harder than is sounds so stay tuned.
Posted on May 12, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.