Desta vez vou compartilhar como suavizar as arestas e cantos dos segmentos de polilinha desenhados no canvas, ou seja, substituir o gráfico de polilinha original passando a curva de Bézier por cada ponto do desenho.
Por que segmentos de polilinha com ajuste suaveVejamos primeiro o efeito de renderização do gráfico de linhas em Echarts:
No início, não percebi que esse segmento de polilinha era na verdade uma curva passando por ele, apenas pensei que fosse um desenho de ponto simples, então a versão simples (feia) e fácil (feia) que implementei inicialmente foi assim:
Não preste atenção ao estilo. O ponto principal é que após a implementação, descobri que a implementação do Echarts parece ser muito tranquila, o que também desencadeou discussões subsequentes. Como desenhar curvas suaves regularmente?
renderizações
Vejamos primeiro a implementação da imitação final:
Porque não sei como o Echarts é implementado internamente (escape
Já parece muito arredondado, muito próximo da nossa visão original. Vamos ver se a curva passa pelo ponto de desenho:
OK! O resultado é óbvio. Agora vamos dar uma olhada em nossa implementação.
Processo de implementação
dados simulados
var data = [Math.random() * 300]; for (var i = 1; i < 50; i++) { //Seguir echarts data.push(Math.round((Math.random() - 0.5) * 20 + dados[i - 1])); opção = { canvas:{ id: 'canvas' }, série: { nome: 'Dados simulados', itemStyle: { cor: 'rgb(255, 70, 131)' }, areaStyle: { cor: 'rgb(255, 158, 68)' }, dados: dados } };Desenhe um gráfico de linhas
Primeiro inicialize um construtor para colocar os dados necessários:
função LinearGradient (opção) { 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 //Armazenamento de dados de simulação}Desenhe um gráfico de linhas:
LinearGradient.prototype.draw1 = function() { //Linha de referência da polilinha... //Deve-se considerar que a origem do canvas é o canto superior esquerdo, //Portanto, algumas conversões precisam ser feitas abaixo, //diff é x, eixo y são os dados As partes iguais do intervalo dos valores máximo e mínimo. this.series.data.forEach(function(item, index) { var x = diffX * index, y = Math.floor(self.height - diffY * (item - dataMin)) self.ctx.lineTo(x, y) //Plotar pontos de dados individuais}) ...} Encaixe suave da curva de BézierO ponto chave da curva de Bezier é a seleção de pontos de controle. Este site pode exibir dinamicamente diferentes curvas desenhadas com diferentes pontos de controle. E para cálculo de pontos de controle. . O autor ainda escolheu o Baidu, afinal ele não é bom em matemática :). Os alunos interessados no algoritmo específico podem aprender mais sobre ele. Agora vamos falar sobre a conclusão do cálculo dos pontos de controle.
A fórmula acima envolve quatro pontos de coordenadas, o ponto atual, o ponto anterior e os próximos dois pontos. Quando os valores das coordenadas são mostrados na figura abaixo, a curva desenhada é a seguinte:
No entanto, há um problema que esta fórmula não pode ser usada para o ponto inicial e o último ponto, mas esse artigo também fornece um método para processar valores limite:
Portanto, ao transformar a polilinha em uma curva suave, calcule os valores limite e outros pontos de controle e substitua-os na função de Bessel:
//Implementação principal this.series.data.forEach(function(item, index) { //Encontre o ponto de controle entre o ponto anterior e o próximo ponto var scale = 0.1 //Para um número positivo de pontos de controle ab, você pode Ajustar var last1X = diffX * (index - 1), last1Y = Math.floor(self.height - diffY * (self.series.data[index - 1] - dataMin)), //As coordenadas do ponto anterior last2X = diffX * (índice - 2), last2Y = Math.floor(self.height - diffY * (self.series.data[index - 2] - dataMin)), //As primeiras duas coordenadas de ponto agoraX = diffX * (índice) , nowY = Math.floor(self.height - diffY * (self.series.data[index] - dataMin)), //Coordenadas do ponto atual nextX = diffX * (index + 1), nextY = Math.floor(self.height - diffY * (self.series.data[index + 1] - dataMin)), //Coordenada do próximo ponto cAx = last1X + (nowX - last2X) * escala, cAy = last1Y + (nowY - último2Y) * escala, cBx = agoraX - (próximoX - último1X) * escala, cBy = agoraY - (próximoY - último1Y) * escala if(índice === 0) { self.ctx.lineTo(nowX, nowY) return } else if(index ===1) { cAx = last1X + (nowX - 0) * escala cAy = last1Y + (nowY - self.height) * escala } else if (índice === self.series.data.length - 1) { cBx = agoraX - (agoraX - último1X) * escala cBy = agoraY - (agoraY - último1Y) * escala } self.ctx.bezierCurveTo(cAx, cAy, cBx, cBy, nowX, nowY); //Desenhe a curva de Bézier do ponto anterior ao ponto atual})Como o ponto que atravesso a cada vez é o ponto atual, mas a fórmula fornecida no artigo é um algoritmo de ponto de controle que calcula o próximo ponto, portanto, na implementação do código, movi o cálculo de todos os pontos uma casa para frente. Quando índice = 0, que é o ponto inicial, nenhuma curva é necessária para ser desenhada, porque estamos desenhando uma curva do ponto anterior ao ponto atual, e não há nenhuma curva para 0 que precise ser desenhada. A partir do índice = 1, podemos começar a desenhar normalmente a curva de 0 a 1, pois não há segundo ponto na frente dela quando índice = 1, é um ponto de valor limite, que requer cálculo especial, e finalmente um ponto. . De resto, basta calcular as coordenadas xy de AB de acordo com a fórmula normal e substituí-las na função de Bessel.
afinalVeja o código fonte aqui
O texto acima é todo o conteúdo deste artigo. Espero que seja útil para o estudo de todos. Também espero que todos apoiem a Rede VeVb Wulin.