根据canvas应用贝塞尔曲线图光滑拟合折直线的方

日期:2021-01-19 类型:科技新闻 

关键词:公众号小程序,小程序正规价格表,微信群签到小程序,网络抽签小程序,个人做小程序需要什么

写在最前

本次共享1下在canvas中将绘图出来的折直线的棱角“磨平”,也便是根据贝塞尔曲线图穿过各个描点来替代原来的折线图。

为何要光滑拟合折直线

先看来下Echarts下折线图的3D渲染实际效果:

 

1刚开始我没留意到实际上这个折直线是曲线图穿以往的,只觉得是单纯性的描点制图,因此最初我完成的“简(丑)易(陋)”版本号是这样的:

不必关心款式,关键便是完成以后才发现看起来人家Echarts的完成描点十分的圆润,也由此引起了以后的讨论。如何有规律性的画光滑曲线图?

实际效果图

先看来下最后效仿的完成:

由于我也不知道道Echarts內部如何完成的(逃

 

 

看起来早已十分圆滑了,和大家最开始的构想10分贴近了。再看下曲线图是不是穿过了描点:

 

好的!結果很显著如今来再次看下大家的完成方法。

完成全过程

  1. 绘图折线图
  2. 贝塞尔曲线图光滑拟合

仿真模拟数据信息

var data = [Math.random() * 300];
        for (var i = 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
            }
        };

绘图折线图

最先原始化1个结构涵数来置放必须用到的数据信息:

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 //储放仿真模拟数据信息
}

绘图折线图:

LinearGradient.prototype.draw1 = function() { //折线参照线
    ... 
    //要考虑到到canvas中的原点是左上角,
    //因此下面要做1些换算,
    //diff为x,y轴被数据信息最大值和最少值的赋值范畴所均分的等份。
    this.series.data.forEach(function(item, index) {
        var x = diffX * index,
            y = Math.floor(self.height - diffY * (item - dataMin))
        self.ctx.lineTo(x, y) //绘图各个数据信息点
    })
    ...
}

贝塞尔曲线图光滑拟合

贝塞尔曲线图的重要点在于操纵点的挑选,这个网站能够动态性的呈现操纵点不一样而绘图的不一样的曲线图。而针对操纵点的测算。。作者還是挑选了百度搜索1下终究数学课不太好:)。实际优化算法有兴趣爱好的同学能够深层次掌握下,如今立即说下测算操纵点的结果。

上面的公式涉及到到4个座标点,当今点,前1个点和后两个点,而当座标值为下图展现的情况下绘图出来的曲线图以下所示:

但是会有1个难题便是起止点和最终1个点不可以用这个公式,但是那篇文章内容也得出了界限值的解决方法:

 

因此在将折线换为光滑曲线图的情况下,将界限值和别的操纵点测算好以后代入到贝塞尔涵数中就进行了:

//关键完成
this.series.data.forEach(function(item, index) { //寻找前1个点到下1个点正中间的操纵点
    var scale = 0.1 //各自针对ab操纵点的1个正数,能够各自自主调剂
    var last1X = diffX * (index - 1),
        last1Y = Math.floor(self.height - diffY * (self.series.data[index - 1] - dataMin)),
        //前1个点座标
        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)),
        //下1个点座标
        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);
        //绘图出上1个点到当今点的贝塞尔曲线图
    })

因为我每次遍历的点全是当今点,可是文章内容中得出的公式是测算会了解下1个点的操纵点优化算法,故在编码完成中我将全部点的测算挪前了1位。当index = 0时也便是原始点是不必须曲线图绘图的,由于大家绘图的是从前1个点到当今点的曲线图,沒有到0的曲线图必须绘图。从index = 1刚开始大家便可以一切正常刚开始绘图,从0到1的曲线图,因为index = 1时是沒有在他前面第2个点的故其属于界限值点,也便是必须独特开展测算,和最终1个点。其余均依照一切正常公式算出AB的xy座标代入贝塞尔涵数便可。

最终

源码见这里

以上便是本文的所有內容,期待对大伙儿的学习培训有一定的协助,也期待大伙儿多多适用脚本制作之家。

上一篇:根据HTML5 WebGL的三d主机房的示例 返回下一篇:没有了