This time I will share how to smooth the edges and corners of the drawn polyline segments in canvas, that is, to replace the original polyline graph by passing the Bezier curve through each drawing point.
Why smooth fitting polyline segmentsLet’s first look at the rendering effect of the line chart under Echarts:
At first, I didn’t notice that this polyline segment was actually a curve passing through it. I just thought it was a simple point drawing, so the simple (ugly) and easy (ugly) version I implemented at first was like this:
Don't pay attention to the style. The key point is that after implementation, I discovered that the implementation of Echarts seems to be very smooth, which also triggered subsequent discussions. How to draw smooth curves regularly?
renderings
Let’s first look at the implementation of the final imitation:
Because I don’t know how Echarts is implemented internally (escape
It already looks very rounded, very close to our original vision. Let’s see if the curve passes through the drawing point:
OK! The result is obvious. Now let’s take a look at our implementation.
Implementation process
simulated data
var data = [Math.random() * 300]; for (var i = 1; i < 50; i++) { //Follow echarts data.push(Math.round((Math.random() - 0.5) * 20 + data[i - 1])); } option = { canvas:{ id: 'canvas' }, series: { name: 'Simulated 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 data you need:
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 //Storage simulation data}Draw a line chart:
LinearGradient.prototype.draw1 = function() { //Polyline reference line... //It should be considered that the origin of the canvas is the upper left corner, //So some conversions need to be done below, //diff is x, y axis is the data The equal parts of the range of the maximum and minimum values. this.series.data.forEach(function(item, index) { var x = diffX * index, y = Math.floor(self.height - diffY * (item - dataMin)) self.ctx.lineTo(x, y) //Plot individual data points}) ...} Bezier curve smooth fittingThe key point of the Bezier curve is the selection of control points. This website can dynamically display different curves drawn with different control points. And for the calculation of control points. . The author still chose Baidu, after all, he is not good at mathematics:). Students who are interested in the specific algorithm can learn more about it. Now let’s talk about the conclusion of calculating control points.
The above formula involves four coordinate points, the current point, the previous point and the next two points. When the coordinate values are as shown in the figure below, the curve drawn is as follows:
However, there is a problem that this formula cannot be used for the starting point and the last point, but that article also gives a method for processing boundary values:
So when changing the polyline to a smooth curve, calculate the boundary values and other control points and substitute them into the Bessel function:
//Core implementation this.series.data.forEach(function(item, index) { //Find the control point between the previous point and the next point var scale = 0.1 //For a positive number of ab control point, you can Adjust var last1X = diffX * (index - 1), last1Y = Math.floor(self.height - diffY * (self.series.data[index - 1] - dataMin)), //The coordinates of the previous point last2X = diffX * (index - 2), last2Y = Math.floor(self.height - diffY * (self.series.data[index - 2] - dataMin)), //The first two point coordinates nowX = diffX * (index) , nowY = Math.floor(self.height - diffY * (self.series.data[index] - dataMin)), //Current point coordinates nextX = diffX * (index + 1), nextY = Math.floor(self.height - diffY * (self.series.data[index + 1] - dataMin)), //Next point coordinate 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 Bezier curve from the previous point to the current point})Since the point I traverse each time is the current point, but the formula given in the article is a control point algorithm that calculates the next point, so in the code implementation, I moved the calculation of all points one place forward. When index = 0, which is the initial point, no curve is required to be drawn, because we are drawing a curve from the previous point to the current point, and there is no curve to 0 that needs to be drawn. Starting from index = 1, we can start drawing normally. The curve from 0 to 1, because there is no second point in front of it when index = 1, it is a boundary value point, which requires special calculation, and finally A point. For the rest, just calculate the xy coordinates of AB according to the normal formula and substitute them into the Bessel function.
at lastSee source code here
The above is the entire content of this article. I hope it will be helpful to everyone’s study. I also hope everyone will support VeVb Wulin Network.