基于canvas如何使用贝塞尔曲线平滑拟合折线段
小编给大家分享一下基于canvas如何使用贝塞尔曲线平滑拟合折线段,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!
为什么要平滑拟合折线段
先来看下Echarts下折线图的渲染效果:
一开始我没注意到其实这个折线段是曲线穿过去的,只认为是单纯的描点绘图,所以起初我实现的“简(丑)易(陋)”版本是这样的:
不要关注样式,重点就是实现之后才发现看起来人家Echarts的实现描点非常的圆滑,也由此引发了之后的探讨。怎么有规律的画平滑曲线?
效果图
先来看下最终模仿的实现:
因为我也不知道Echarts内部怎么实现的(逃
看起来已经非常圆润了,和我们最初的设想十分接近了。再看下曲线是否穿过了描点:
好的!结果很明显现在来重新看下我们的实现方式。
实现过程
绘制折线图
贝塞尔曲线平滑拟合
模拟数据
vardata=[Math.random()*300]; for(vari=1;i<50;i++){//按照echarts data.push(Math.round((Math.random()-0.5)*20+data[i-1])); } option={ canvas:{ id:'canvas' }, series:{ name:'模拟数据', itemStyle:{ color:'rgb(255,70,131)' }, areaStyle:{ color:'rgb(255,158,68)' }, data:data } };
绘制折线图
首先初始化一个构造函数来放置需要用到的数据:
functionLinearGradient(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//存放模拟数据 }
绘制折线图:
LinearGradient.prototype.draw1=function(){//折线参考线 ... //要考虑到canvas中的原点是左上角, //所以下面要做一些换算, //diff为x,y轴被数据最大值和最小值的取值范围所平分的等份。 this.series.data.forEach(function(item,index){ varx=diffX*index, y=Math.floor(self.height-diffY*(item-dataMin)) self.ctx.lineTo(x,y)//绘制各个数据点 }) ... }
贝塞尔曲线平滑拟合
贝塞尔曲线的关键点在于控制点的选择,这个网站可以动态的展现控制点不同而绘制的不同的曲线。而对于控制点的计算。。作者还是选择了百度一下毕竟数学不好:)。具体算法有兴趣的同学可以深入了解下,现在直接说下计算控制点的结论。
上面的公式涉及到四个坐标点,当前点,前一个点以及后两个点,而当坐标值为下图展示的时候绘制出来的曲线如下所示:
不过会有一个问题就是起始点和最后一个点不能用这个公式,不过那篇文章也给出了边界值的处理办法:
所以在将折线换成平滑曲线的时候,将边界值以及其他控制点计算好之后代入到贝塞尔函数中就完成了:
//核心实现 this.series.data.forEach(function(item,index){//找到前一个点到下一个点中间的控制点 varscale=0.1//分别对于ab控制点的一个正数,可以分别自行调整 varlast1X=diffX*(index-1), last1Y=Math.floor(self.height-diffY*(self.series.data[index-1]-dataMin)), //前一个点坐标 last2X=diffX*(index-2), last2Y=Math.floor(self.height-diffY*(self.series.data[index-2]-dataMin)), //前两个点坐标 nowX=diffX*(index), nowY=Math.floor(self.height-diffY*(self.series.data[index]-dataMin)), //当期点坐标 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 }elseif(index===1){ cAx=last1X+(nowX-0)*scale cAy=last1Y+(nowY-self.height)*scale }elseif(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); //绘制出上一个点到当前点的贝塞尔曲线 })
由于我每次遍历的点都是当前点,但是文章中给出的公式是计算会知道下一个点的控制点算法,故在代码实现中我将所有点的计算挪前了一位。当index = 0时也就是初始点是不需要曲线绘制的,因为我们绘制的是从前一个点到当前点的曲线,没有到0的曲线需要绘制。从index = 1开始我们就可以正常开始绘制,从0到1的曲线,由于index = 1时是没有在他前面第二个点的故其属于边界值点,也就是需要特殊进行计算,以及最后一个点。其余均按照正常公式算出AB的xy坐标代入贝塞尔函数即可。
看完了这篇文章,相信你对“基于canvas如何使用贝塞尔曲线平滑拟合折线段”有了一定的了解,如果想了解更多相关知识,欢迎关注恰卡编程网行业资讯频道,感谢各位的阅读!
推荐阅读
-
html5 canvas元素使用1
-
canvas如何截取圆角图片
canvas如何截取圆角图片小编给大家分享一下canvas如何截...
-
怎么用html5的canvas跳一跳小游戏效果
怎么用html5的canvas跳一跳小游戏效果这篇文章主要介绍了怎...
-
Canvas渐进填充与透明实现图像的Mask效果怎么实现
Canvas渐进填充与透明实现图像的Mask效果怎么实现这篇文章主...
-
微信小程序canvas图片及文本适配的方法
微信小程序canvas图片及文本适配的方法这篇文章主要介绍了微信小...
-
怎么用HTML5组件Canvas实现图像灰度化
怎么用HTML5组件Canvas实现图像灰度化今天小编给大家分享一...
-
如何用html5的canvas画布绘制贝塞尔曲线
如何用html5的canvas画布绘制贝塞尔曲线这篇“如何用htm...
-
微信小程序canvas中translate怎么用
微信小程序canvas中translate怎么用本篇内容介绍了“微...
-
HTML5中怎么用Canvas实现变形
HTML5中怎么用Canvas实现变形本篇内容主要讲解“HTML5...
-
HTML5中怎么用Canvas绘制各种线条
HTML5中怎么用Canvas绘制各种线条本篇内容介绍了“HTML...