First look at the final result:

Or online experience: jsbin.com/resedebati/…

I. Background:

A recent project needed to show line charts because the project was relatively simple and didn’t reference the associated chart library, but implemented it natively.

Method 1 (javascript) :

The first instinct is to implement it in javascript. First, use CSS to draw the starting and ending points of the line chart as well as the turning point of the circle and achieve self-adaptation. As shown below:

The next step is to use javascript to get the DOM nodes of these circles, calculate the length and Angle between the adjacent points, and set the relevant styles. Because the length and Angle between two points change as the screen width changes, you need to listen for the resize event and restyle it.

Ok, a simple line chart is implemented, but then discovered the problem of this implementation.

Method 1 problem:

This project needs the printing function. When printing preview, we found the problem of javascript implementation, as shown in the following figure:

Because the browser grabs the DOM structure of the page at the width of A4 paper as soon as it prints, and because the circles of the vertices are implemented in CSS, it works well for adaptive layout based on the width of A4 paper, but creases don’t. The lines are implemented in javascript, so when printing, the lines are still rendered at the width of the browser at the moment of printing. If the browser is wider than A4 paper, it looks like the image above. If the browser is smaller than A4 paper, it looks like the image below. Unless the browser is exactly the width of A4 paper, it will print exactly right, which is unlikely.At first, MY thinking was that it would be ok to render a crease at the same time as the print. But for a long time, no results… Later, I consulted Zhang Xinxu zhang, who suggested using CSS directly to achieve line charts. I was a little confused at first, can CSS also implement line charts?

CSS: SVG path

First draw an unmarked line chart:

<svg width="1470" height="200" viewBox="0 0 1470 200" preserveAspectRatio="none"> <path d="M0 0L210 200L420 50L630 160L840 150L1060 180L1270 40L1470 120" fill="none" stroke="red" stroke-width="1" style="vector-effect: non-scaling-stroke;" > </svg>Copy the code

Since the height of the line graph is fixed, it is necessary to add a preserveAspectRatio=” None “attribute to the SVG tag to distort the aspect ratio to fit the viewport properly. For the relationship between this attribute and viewport and viewBox, please refer to Zhang’s article.

You can then define a circular marker in SVG with

Marker ID ="markerCircle" markerWidth="8" markerHeight="8" refX="4" refY="4"> <circle Cx ="4" cy="4" r="2.5" fill="#fff" stroke="red" stroke-width="1"> </marker> </defs>Copy the code

Then, in the element above, marker-mid, marker-start and marker-end are used to reference the marker through ID to realize the circle of starting and ending points and turning points.

<svg width="1470" height="200" viewBox="0 0 1470 200" preserveAspectRatio="none"> <defs> <marker id="markerCircle" <circle cx="4" cy="4" r="2.5" Fill ="# FFF "stroke="red" fill="# FFF" stroke="red" stroke-width="1"></circle> </marker> </defs> <path d="M0 0L210 200L420 50L630 160L840 150L1060 180L1270 40L1470 120" fill="none" stroke="red" stroke-width="1" marker-mid="url('#markerCircle')" marker-end="url('#markerCircle')" marker-start="url('#markerCircle')" style="vector-effect: non-scaling-stroke;" ></path> </svg>Copy the code

In this case, vector-effect: non-scaling is used to keep the stroke from scaling and to keep the width of the polyline unchanged. At this point, the use of pure CSS to achieve adaptive line charts, again lamenting the power of CSS. But there is one small flaw.

Method 2 question:

Since the entire SVG uses the

property to distort the aspect ratio to fit the Viewport adequately, the rendering of markers is related to the aspect ratio of the viewBox, and circles behave differently under different aspect ratios. =”>

So far feel into a dead end, javascript implementation printing problems, CSS implementation circle deformation… Fortunately, another colleague yan Wenbin came up with the perfect solution.

Method 3 (CSS: SVG) :

Since method 2 will cause the distortion of marker due to forcibly changing the aspect ratio of viewBox, why not not change the aspect ratio and render the polyline section by section through proportion? Directly on the code:

<div class="line"> <svg> <defs> <marker id="dot" markerWidth="8" markerHeight="8" refX="4" refY="4"> <circle cx="4" Cy = "4" r = "2.5" > < / "> < / marker > < / defs > < line x1 =" 0 "y1 =" 0% "x2 =" 14.2857% "y2 =" 100% "> < / line > < line x1 =" 14.2857%" Y1 = "100%" x2 = "28.5714%" y2 = "25%" > < / line > < line x1 = "28.5714%" y1 = "25%" x2 = "42.8571%" y2 = "80%" > < / line > < line x1 = "42.8571%" Y1 = "80%" x2 = "57.1428%" y2 = "75%" > < / line > < line x1 = "57.1428%" y1 = "75%" x2 = "71.4286%" y2 = "90%" > < / line > < line x1 = "71.4286%" Y1 = "90%" x2 = "85.7143%" y2 = "20%" > < / line > < line x1 = "85.7143%" y1 = "20%" x2 = "100%" y2 = "30%" > < / line > < / SVG > < / div >Copy the code
.line { width: calc(100% - 102px); height: 200px; position: absolute; left: 51px; } .line svg { width: 100%; height: 100%; color: red; stroke: currentColor; stroke-width: 1; overflow: visible; transform: scaleY(-1); } circle { stroke: currentColor; stroke-width: 1; fill: #fff; } .line line { marker-start: url(#dot) } .line line:last-child { marker-end: url(#dot) }Copy the code

Where x1 y1 is the first vertex of a line; X2, y2 is the second vertex of a line; The coordinates of the second vertex of the previous line are the same as the coordinates of the first vertex of the next line. The final rendering is the GIF at the beginning of the article, with good self-adaptation, undeformed circles and good printing effect.

V. Summary:

In the process of realizing the twists and turns, I learned a lot of knowledge points related to SVG, and also realized my deficiencies in CSS knowledge points. The realization of an effect, the first reaction to solve the way of thinking with javascript really should be changed.

Thank you very much for the help of Mr Zhang and Mr Wen Bin, in the process of implementation we all referred to Mr Zhang’s third book “CSS new World” in a lot of knowledge points, the book is very comprehensive explanation of CSS2.1 after the knowledge, after the experience feel very good!

Six, attention points:

  1. SVG elements can also use Overflow: Visible directly to display content beyond the scope of the viewBox.

  2. The percentage in SVG has the upper left corner as the reference point. You can use Transform: scaleY(-1) to flip the entire SVG to the lower left corner as the reference point with the Y-axis center line, as is customary.

  3. Markers in

    can also be customized as triangles and squares, which can be freely played according to preferences.

Thank you for your reading. If you find any inaccuracies, please correct them.

Refer to the article

  1. Understand SVG Viewport, viewBox, preserveAspectRatio scaling
  2. CSS New World