• Building a Dynamic Tree Diagram with SVG and vue.js
  • Krutie Patel
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: YueYong
  • Proofread by: Moonliujk, Shixi-Li

Use SVG and vue.js to build dynamic tree graphs

This article will show you how I created a dynamic tree graph that uses SVG (scalable vector graphics) to plot Cubic Bezier paths and uses vue.js to implement the data response.

Before we start, let’s take a look at a demo.

With the power of SVG and vue.js frameworks, it’s easy to create data-driven, interactive, and configurable charts and infographics.

The graph is a collection of cubic Bezier curves that start at a single point and end at different points with the same distance between them, based on user-supplied data. Therefore, the graph responds to user input.

We will first learn how to make cubic Bezier curves, and then try to find x and Y points available to < SVG > elements in the coordinate system by clipping masks.

I used a lot of visual animation in this case to keep things interesting. The main idea of this article is to help you design your own diagrams for similar projects.

SVG

How is the Cubic Bezier curve formed?

The curve you saw in the demo above is called a cubic Bezier curve. I have highlighted each part of the curve structure below.

It has four pairs of coordinates. The first pair of coordinates — (x0, y0) — are the starting anchors, and the last pair — (x3, y3) — are the finishing anchors, indicating the position of the completion path.

The two middle pairs of coordinates are:

  • Bezier control point #1(x1, y1)
  • Bezier control point #2(x2, y2)

The path realized based on these points is a smooth curve. Without these control points, the path is a straight line!

Let’s put these four coordinates into the element of the SVG syntax.

// <path D="M x0,y0 C x1,y1 x2,y2 x3,y3" />
Copy the code

The letter C in grammar stands for cubic Bezier curves. Small C represents relative value, while capital C represents absolute value. I’m creating this graph with the absolute value of C.

Realize symmetry

Symmetry is the key to achieving this diagram. To achieve this, I use only one variable to derive values like height, width, and midpoint.

Let’s call this variable size. Since the orientation of the tree is horizontal, the variable size can be regarded as the horizontal space of the entire graph.

Let’s assign the actual value to this variable. This way, you can also calculate the coordinates of the path.

size = 1000
Copy the code

Looking for the coordinate

Before we can find the coordinates, we need to create a new coordinate system!

Coordinate system and viewBox

The viewBox attribute of the < SVG > element is important because it defines the user coordinate system for SVG. In short, the viewBox defines the location and dimensions of the user space to draw SVG.

The viewBox consists of four numbers in the same order — min-x, min-y, width, height.

<svg viewBox="min-x min-y width height">... </svg>Copy the code

The size variable we defined earlier controls the width and height of this coordinate system.

Later in the vue.js section, the viewBox is bound to the calculated property to fill in width and height, while min-x and min-y are always zero in this instance.

Note that we do not use the width and height attributes of the SVG element itself. Because we will later use CSS to set width: 100% and height: 100% for < SVG > to adaptively fill the entire viewPort.

Now that the user space/coordinate system for the entire graph is ready, let’s see how the size variable can help calculate coordinates by using different % values.

Constant and dynamic coordinates

A circle is part of a diagram. That’s why it’s important to include it in your calculations from the start. As shown in the figure above, let’s begin by exporting the coordinate values of a circle and a sample path.

The vertical height is divided into two parts: topHeight (20% of the size) and bottomHeight (80% of the remaining size). The horizontal width is divided into two parts — 50% of the size.

So the coordinates of the circle (halfSize, topHeight) are obvious. The radius property of the circle is set to half of topHeight, which is a good amount of free space.

Now, let’s look at the path coordinates…

  • x0, y0— The first pair of anchorsAlways the same. herex0Is the chartsizeThe center of they0Is the vertical point where the circle stops (Therefore, a RADIUS is added) and is the starting point of the path.

    =(50% size, 20% size + RADIUS)
  • x1, y1Bezier control point 1, for all pathsAnd it stays the same. In terms of symmetry,x1y1Always chartsizeIn the half. =(50% size, 50% size)
  • x2, y2— Bezier control point 2, wherex2Indicates which side forms the curve and is for each pathDynamic calculation. In the same way,y2Is the chartsizeIn the half.

    = The size (x2, 50%)
  • x3, y3— The last pair of anchor points, indicating where path drawing ends. herex3imitationx2This is dynamically computed.y3Occupy thesizeOf 80%.

    = (x3, 80% of the size)

After combining the above calculations, see the general path syntax below. To represent %, I simply divide the % value by 100.

<path d="M size*0.5, (size*0.2) + RADIUS C size*0.5, size*0.5 x2, size*0.5 x3, size*0.8"
>
Copy the code

Note: the entire code logic%The choice of “is” may seem at first to be all subjective, but it is the right ratio to choose in order to achieve symmetry. Once you understand the purpose of building this diagram, you can try your own%Value and check for different results.

The next section focuses on finding the values of the remaining coordinates X2 and x3 — this makes it possible to dynamically form multiple curved paths based on their array indexes.

Depending on the number of elements in the array, the available horizontal space should be allocated equally so that each path gets the same amount of space on X-axis.

The formula should eventually work for any number of items, but for the purposes of this article, I’ve used five array items — [0,1,2,3,4]. That means I’m going to plot five Bezier curves.

Finding dynamic coordinates (x2 and x3)

First, I divide size by the number of elements, the length of the array, and name it distance — the distance between the two elements.

distance = size/arrayLength
// distance = 1000/5 = 200
Copy the code

I then loop through each element in the array and multiply its index value by distance. Just to keep things simple, I’m going to use x for x2 and x3.

// value of x2 and x3
x = index * distance
Copy the code

This graph looks a little strange when I use the values of x to represent x2 and x3.

As you can see, the coordinates are in the right place, but not very symmetrical. There are more elements on the left than there are on the right.

At this point, for some reason, I need to put the x3 coordinate in the center of distance, not where I started.

To solve this problem, let’s revisit the variable distance, which has a value of 200 for a given scenario. I just added another half of distance to x.

X = index * distance + distance * 0.5Copy the code

The above formula means that I find the midpoint of distance and place the final x3 coordinate there, and adjust the x2 of Bezier curve #2.

Add half of distance to the x2 and x3 coordinates, for the odd and even elements of the array.

Layer mask

To make the mask shape circular, I have defined a circle in the mask element.

<defs>
  <mask id="svg-mask">
     <circle :r="radius" 
             :cx="halfSize" 
             :cy="topHeight" 
             fill="white"/>
  </mask>
</defs>
Copy the code

Next, using the tag in the < SVG > element as the content, I bind the image to the

element (already created in the code above) using the mask attribute.

<image mask="url(#svg-mask)" 
      :x="(halfSize-radius)" 
      :y="(topHeight-radius)". > </image>Copy the code

Since we were trying to fit the square image into a circle, I adjusted the image position by reducing the radius of the circle to achieve full visibility of the image through the circular mask.

Let’s put all the values into the diagram to help us see the complete picture.

Dynamic SVG using vue.js

So far, we’ve seen the nature of bezier curves, and how they work. Thus, we have the concept of static SVG diagrams. Using vue.js and SVG, we will now drive the diagram with data and transform it from static to dynamic.

In this section, we will decompose SVG diagrams into Vue components, bind SVG properties to computed properties, and make them respond to data changes.

Finally, we’ll look at the configuration panel component, which is used to feed data to dynamic SVG diagrams.

We will look at the following key topics in this section.

  • Binding SVG viewBox
  • Computes SVG path coordinates
  • Two options for implementing Bessel curve paths
  • Configuration panel
  • Homework ❤

Binding SVG viewBox

First, we need a coordinate system to draw inside SVG. The size variable will be used to evaluate the property viewbox. It contains four values separated by Spaces — which are fed into the viewBox property of the < SVG > element.

viewbox() 
{
   return "0 0" + this.size + "" + this.size;
}
Copy the code

In SVG, viewBox attributes already use camelCase.

<svg viewBox="0 0 1000 1000">
</svg>
Copy the code

So to evaluate the attribute correctly on the binding, I use kebab-case for the variable after the.camel modifier (as shown below). In this way, HTML is able to bind this attribute correctly.

Now, every time we change size, the chart adjusts itself without having to manually change the markup.

Computes SVG path coordinates

Since most values are derived from a single variable size, I have used computed attributes for all constant coordinates. Don’t be confused by constants here. These values are derived from size, but after that, they remain the same no matter how many curve paths are created.

If you resize the SVG, these values will be computed again. With this in mind, here are the five values needed to plot bezier curves.

  • TopHeight –The size * 0.2
  • BottomHeight –The size * 0.8
  • Width –size
  • HalfSize –The size * 0.5
  • Short –size/arrayLength

At this point, we are left with two unknown values, x2 and x3, and we have a formula to determine their values.

X = index * distance + distance * 0.5Copy the code

To find the x above, we need to enter index into the formula for each path once. So…

Is it appropriate to use computed properties here? Definitely not appropriate.

We cannot pass an argument to a calculated property — because it is a property, not a function. In addition, requiring a parameter to evaluate means that using computed attributes is not good for caching either.

Note: There is one exception above, Vuex. If we are using Vuex Getters, then we can pass the argument to the getter by returning a function.

In the case described in this article, we do not use Vuex. But even so, we still have two options.

Choose a

We can define a function where we pass the array index as an argument and return the result. If you want to use this value in multiple places in the template, choose the Bit Cleaner.

<g v-for="(item, i) in itemArray">
  <path :d="'M' + halfSize + ',' + (topHeight+r) +' '+ 'C' + halfSize + ',' + halfSize +' '+ calculateXPos(i) + ',' + halfSize +' '+ calculateXPos(i) + ',' + bottomHeight" 
  />
</g>
Copy the code

The calculateXPos() method is evaluated on each call. And this method takes the index — I — as an argument (code below).

<script>
  methods: {
    calculateXPos (i)
    {
      returnDistance * I + (distance * 0.5)}} </script>Copy the code

Here is the result of Option 1 running on CodePen.

Option 1 – Bezier Curve Tree Diagram with Vue Js

Choose two

Better yet, we can extract this little SVG path tag into its own child component and pass index as an attribute — along with other required attributes, of course.

In this example, we can even use the computed properties to find x2 and x3.

<g v-for="(item, i) in items"> 
    <cubic-bezier  :index="i" 
                   :half-size="halfSize" 
                   :top-height="topHeight" 
                   :bottom-height="bottomHeight" 
                   :r="radius"
                   :d="distance"
     >
     </cubic-bezier>
</g>
Copy the code

This approach makes our code more organized. For example, we can create one or more child components for a circular clipping mask, as shown below.

<clip-mask :title="title"
           :half-size="halfSize" 
           :top-height="topHeight"                     
           :r="radius"> 
</clip-mask>
Copy the code

Configuration panel

You may already see the control panel in the upper left corner of CodePen. It can add and remove elements from an array. In Option 2, I created a child component to hold the Config Panel, making the top-level Vue components legible. Our Vue component tree looks like this.

Want to know what Option 2 code looks like? The following link is the code using Option 2 on CodePen.

Option 2 – Bezier Curve Tree Diagram with Vue Js

Making the warehouse

Finally, there is a GitHub Repo for you, which you can view before moving on to the next section (using option 2).

homework

Try to create the same chart in vertical mode based on the logic described in this article.

If you think it’s as simple as the x value and the y value in the exchange coordinate system, then you’re right! Because the hard part is done, update the code with the appropriate variables and methods after swapping the required coordinates.

With the help of vue.js, the diagram can be further extended with more features, for example,

  • Create a switch to easily switch between horizontal and vertical modes
  • You can animate paths using GSAP
  • Control path properties (such as color and stroke width) from the configuration panel
  • Use a third-party tool library to save and download the chart as an image /PDF

Give it a try now, if needed, and links to the answers to the homework are below.

Good luck!

conclusion

is one of the many powerful elements in SVG because it allows you to create graphs and charts with precision. In this article, we learned how Bessel curves work and how to create a custom chart application.

Tweaking with the data-driven approach used by modern JavaScript frameworks is always daunting, but vue.js makes it very simple and can handle simple tasks such as DOM manipulation. So, as a developer, you can think in terms of data even when working on visually obvious projects.

I’ve realized that creating this seemingly complex diagram requires some simple concepts from vue.js and SVG. If you’re not ready, I recommend you read about building interactive infographics using vue.js. It will be much easier to go back and read this article after reading that article. Here are the answers to your homework.

I hope you learned something from this article, and that when you read it, you felt as much fun as I had writing it.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.