DEV Community

Cover image for Geeking-out on SVG Graphics part-three
Tracy Gilmore
Tracy Gilmore

Posted on • Edited on

Geeking-out on SVG Graphics part-three

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>
Enter fullscreen mode Exit fullscreen mode

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 inside svg 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;
Enter fullscreen mode Exit fullscreen mode

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; 
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

The following functions follow a similar pattern:

  1. Locate the DOM entry point (group element) using the document.querySelector('#resolution ... method.
  2. Prepare the data to be rendered.
  3. 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>
    `)
  );
}
Enter fullscreen mode Exit fullscreen mode

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 the drawScreenResultions 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.

Cutting-mat with screen resultions

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.

Top comments (0)