Easily generate SVG patterns for your React projects | by Massimo Cassandro | Medium

Building a pattern generator in React

Easily generate SVG patterns to include in a React project (with a bonus tip for Figma or Sketch)

Recently, I’ve been dealing with some SVG patterns to be used for some chart filling.

I needed to create a lot of them, and the hardest thing was figuring out the right colors and tones for the various fills so that the page looked balanced.

After looking into several options, I found that the best solution was to use patterns instead of solid or gradient fills.

One of the most annoying problems was the difficulty of testing models in Figma. I tried many different stroke widths, colors, line angles, and so on, and each variation took quite a long time in Figma. I also tried different plug-ins, but none of them was fast enough or suitable for my purpose.

To fix this, I decided to arrange a quick and dirty JavaScript app. First, I exported the module I was working on from Figma in SVG format and mounted it on a web page. Then I connected the areas that needed to be filled with patterns to a simple script where I could change some parameters:

In this way, I was able to test many variations of the models in a very short time. In the end, I completed my prototype by bringing the definitive patterns back to Figma.
To do this, I used the tiles that you can see in the gray box on the right, but we will come back to this later.

It’s possible that I could have done everything directly in Figma, but I didn’t have the time (or inclination) to do so.

The following is the process I used to create the pattern, first for the prototype and then as a React component.

According to Wikipedia, Tesselation:

A tessellation or tiling is the covering of a surface, often a plane, using one or more geometric shapes, called tiles, with no overlaps and no gaps. In mathematics, tessellation can be generalized to higher dimensions and a variety of geometries.

We need a simple type of tessellation based only on the translation of a tile on the x and y axes. Furher, the pattern we need is a simple sequence of lines:

As you can see, the pattern is a repetition of the green tile. All we need to do is arrange it so that it can be repeated seamlessly.

Once we decide the distance between lines and their angle, our goal is to determine the size of each tile.

To do this, we have to deal with triangles and trigonometry.

Looking at the schema below, we can see that

  • The line start point (A) is the top-left corner of the tile;
  • The line (in blue) is actually the hypotenuse of a right triangle, whose legs are the sides of the tile;
  • The A-B side (h) corresponds to the distance between the pattern lines. We know it, as it is one of the parameters we can set,
  • same for the angle.

So, the only value we are missing to get the C point coordinates is the B-C side, the base of our triangle (b).

But we can determine it by using a straightforward trigonometric formula. The base of the triangle is equal to the other leg multiplied by the cotangent of the adjacent angle or by the tangent of the opposite angle.

Now we have all we need to generate our SVG pattern.

SVG has a specific pattern element.

A pattern element must be defined within a defs one and can be added to any svgelement (not just the one to which they are applied).

Once a pattern is defined, it can be used to fill an SVG element in this way:

<someElement fill="url(#pattern-id)">...</someElement>

To define our tile as a SVG pattern element, we’ll write something like this:

<svg xmlns="http://www.w3.org/2000/svg">
<pattern id="pattern" patternUnits="userSpaceOnUse" width="69.282" height="40">
<rect width="69.282" height="40" fill="#dddddd"></rect>
<path d="M0,0 l69.282,40 M-34.641,20 l69.282,40 M34.641,-20 l69.282,40" stroke="#666666" stroke-width="1.5" stroke-opacity="1"></path>

The rect element is the tile background (in this example, it is filled with a light gray, #ddd), and the path describes our line.

To generate our pattern, we have to calculate their width, height, and d attributes.

Actually, we don’t need to calculate the height of the rect element, since it is one of the parameters we can choose.

Then, given the parameters h (the height of the rectangle, i.e., the distance between the lines), and (the line angle), the rect base (b) is:

b = Math.tan(((90 - ) * Math.PI) / 180) * h)

Because JavaScript lacks a native function for calculating cotangents, we must use Math’s tanmethod with the opposite of (that is, in our scheme, ). We don’t know , but since the sum of all triangle angles is 180 and our triangle is rect, we can calculate it as 90 - .

Note that the contangent is the inverse of the tangent, so you can use 1/Math.tan()instead of Math.tan() if you prefer.

All Math trigonometric functions accept only angles in radians, so we must convert degrees to radians using the following simple method:

angle_in_radians = angle_in_degrees * Math.PI / 180

where Math.PI represents the number π.

The result of our operation will easily have several decimal places. If you want to reduce them while keeping an acceptable precision, say 3, you can use this method:

b = Math.round((Math.tan(((90 - ) * Math.PI) / 180) * h) * 1000) / 1000

Now we have the width attribute of the rect element and the ending x coordinate of our line (C). The last step is to calculate the d attribute of the path element.

The d allows us to define any path by using some simple command parameters. We will use only the M and l commands, for a complete list, take a look at the reference page on MDN.

d is for draw, so imagine what you would do to draw our line: you would first move the pen to the starting point, and then draw a line to the ending point.

This becomes M0,0 (Move to point of coordinates 0,0) and lb,h (draw a line to the coordinates b,h.

The l command is lowercase, indicating that its coordinates are relative to the previous point.

Putting all togheter (having already defined our b and h variables:

d = `M0,0 l${b},${h}`

It would seem very easy, but unfortunately there is a small issue.

Applying the above code to our pattern, we will get something like this:

The edge of the pattern cuts out our line, so we have two small parts missing at the beginning and end of each tile. To fix this issue, we have to add the missing pieces:

The simplest solution is to draw two other lines similar to the first one but translated left-bottom and top-right:

The pattern will be cropped, hiding the unnecessary parts of the lines. So our d attribute becomes:

d=`M0,0 l${b},${h} 
M${-b / 2},${h / 2} l${b},${h}
M${b / 2},${-h / 2} l${b},${h}`

We just need to add two more strokes, the first starting at ${-b / 2},${h / 2} and the second at ${b / 2},${-h / 2}. The l parameters are relative to the previous command, so they remain the same.

Everything is clearly explained using a very useful tool: SVG Path Visualizer:

Another similar great tool is SVG Path Editor.

I’ve built a small demo where you can change many parameters to build your pattern.

This is intended to be the first step of a larger project to create generators for various types of patterns (however, I have no idea how it will go at the moment).

The script works well with an angle between about 5–10 and 70–75 degrees; with other values, the b parameter tends to Infinity or 0, making the procedure ineffective. This is not difficult to handle, but as it is beyond the scope of this article, these cases are not covered here.

There are many ways to implement this procedure in React. To have a reusable pattern engine, I chose to create a component with the ability to add a set of parameters for each pattern.

The demo below shows a series of patterns applied to some SVG shapes:

What about Figma and Sketch?

The last box of my demo show a single tile standalone SVG that doesn’t make use of the pattern.

You can copy the SVG via the button and then paste it into Figma or Sketch; both will render the SVG so you can use it to create patterns for your mockups.

The whole process remains cumbersome, but I couldn’t find a quicker way.

Finally, if you don’t need a custom pattern, you might find a suitable one in Michael Semmler’s Basic Pattern Repository

Source link

Leave a Reply