Based on canvas, the method of using the besell curve to smoothly fit the line segment is canvas besell
Preface
In this article, the edges and edges of the Line Segment drawn in the canvas are "flattened", that is, the original line chart is replaced by the beiser curve through various points.
Why do we need to smoothly fit the line segment?
Let's take a look at the rendering effect of the line chart under Echarts:
At first, I didn't notice that this line segment was actually curved and thought it was just a simple drawing, so at first I realized "simple (ugly) "The version is as follows:
Do not pay attention to styles. The focus is to find that the implementation of Echarts is smooth and smooth, which leads to subsequent discussions. How can we draw a smooth curve regularly?
Let's take a look at the implementation of the final imitation:
Because I do not know how Echarts is implemented internally (Escape
It looks very round and very close to our original vision. Let's see if the curve goes through the drawing:
Okay! The result is obvious. Now let's review our implementation method.
Implementation Process
- Draw a line chart
- Besell curve smoothing
Analog data
Var data = [Math. random () * 300]; for (var I = 1; I <50; I ++) {// according to echarts data. push (Math. round (Math. random ()-0.5) * 20 + data [I-1]);} option = {canvas: {id: 'canvas '}, series: {name: 'analog data', itemStyle: {color: 'rgb (255, 70,131) '}, areaStyle: {color: 'rgb (255,158, 68)'}, data: data }};
Draw a line chart
First, initialize a constructor to place the required data:
Function LinearGradient (option) {this. canvas = document. getElementById (option. canvas. id) this. ctx = this. canvas. getContext ('2d ') this. width = this. canvas. width this. height = this. canvas. height this. tooltip = option. tooltip this. title = option. text this. series = option. series // store simulation data}
Draw a line chart:
LinearGradient. prototype. draw1 = function () {// Line Reference Line... // take into account that the origin in the canvas is in the upper left corner. // make some conversions below. // diff is x, and the Y axis is equal to the value range of the maximum and minimum values of the data. This. series. data. forEach (function (item, index) {var x = diffX * index, y = Math. floor (self. height-diffY * (item-dataMin) self. ctx. lineTo (x, y) // draw various data points })...}
Besell curve smoothing
The key point of the beiser curve is the selection of control points. This website can dynamically display different curves drawn from different control points. For the calculation of control points .. The author chose Baidu. After all, the mathematics is not good :). If you are interested in specific algorithms, you can gain an in-depth understanding of them. Now, let's talk about the conclusions of calculating control points.
The above formula involves four coordinate points, the current vertex, the previous vertex, and the last two vertices. The curves drawn when the coordinate value is displayed are as follows:
However, there is a problem that the formula cannot be used for the starting point and the last point, but the article also provides a solution to the boundary value:
Therefore, when the line is changed to a smooth curve, the boundary value and other control points are calculated and entered into the besell function:
// Core implementation this. series. data. forEach (function (item, index) {// find the control point from the previous point to the next point var scale = 0.1 // a positive number of the AB control point, respectively, you can adjust var last1X = diffX * (index-1) and last1Y = Math respectively. floor (self. height-diffY * (self. series. data [index-1]-dataMin), // The coordinate last2X = diffX * (index-2), last2Y = Math. floor (self. height-diffY * (self. series. data [index-2]-dataMin), // The first two coordinate points nowX = diffX * (index), nowY = Math. floor (self. height-diffY * (self. series. data [index]-dataMin), // The coordinates of the current vertex nextX = diffX * (index + 1), nextY = Math. floor (self. height-diffY * (self. series. data [index + 1]-dataMin), // cAx = last1X + (nowX-last2X) * scale, cAy = last1Y + (nowY-last2Y) * scale, cBx = nowX-(nextX-last1X) * scale, cBy = nowY-(nextY-last1Y) * scale if (index = 0) {self. ctx. lineTo (nowX, nowY) return} else if (index = 1) {cAx = last1X + (nowX-0) * scale cAy = last1Y + (nowY-self. height) * scale} else if (index = self. series. data. length-1) {cBx = nowX-(nowX-last1X) * scale cBy = nowY-(nowY-last1Y) * scale} self. ctx. bezierCurveTo (cAx, cAy, cBx, cBy, nowX, nowY); // draw the besell curve from the previous vertex to the current vertex })
Since the vertices that I traverse every time are the current vertices, the formula given in the article is that the calculation will know the Control Point Algorithm of the next vertex, therefore, in code implementation, I moved the calculation of all vertices to the first place. When the index is 0, that is, the initial point does not need to be drawn, because we draw the curve from the previous point to the current point, and the curve is not 0. We can start to draw a curve from index = 1. The curve from 0 to 1 is a boundary Value Point because index = 1 is not at the second vertex before it, that is, special computing and the last vertex are required. The rest are calculated based on the normal formula and the xy coordinates of AB can be substituted into the besell function.
Last
For the source code, see here
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.