cvmachine.com - 申博开户网

搜索: 您的位置主页 > 网络频道 > 阅读资讯:如何基于canvas基于贝塞尔曲线平滑拟合折线段?

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

2018-01-31 13:12:41 来源:www.cvmachine.com 【

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

写在最前

本次分享一下在canvas中将绘制出来的折线段的棱角“磨平”,也就是通过贝塞尔曲线穿过各个描点来代替原有的折线图。

为什么要平滑拟合折线段

先来看下Echarts下折线图的渲染效果:

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

一开始我没注意到其实这个折线段是曲线穿过去的,只认为是单纯的描点绘图,所以起初我实现的“简(丑)易(陋)”版本是这样的:

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

不要关注样式,重点就是实现之后才发现看起来人家Echarts的实现描点非常的圆滑,也由此引发了之后的探讨。怎么有规律的画平滑曲线?

效果图

先来看下最终模仿的实现:

因为我也不知道Echarts内部怎么实现的(逃

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

看起来已经非常圆润了,和我们最初的设想十分接近了。再看下曲线是否穿过了描点:

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

好的!结果很明显现在来重新看下我们的实现方式。

实现过程

  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
      }
    };

绘制折线图

首先初始化一个构造函数来放置需要用到的数据:

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中的原点是左上角,
  //所以下面要做一些换算,
  //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) //绘制各个数据点
  })
  ...
}

贝塞尔曲线平滑拟合

贝塞尔曲线的关键点在于控制点的选择,这个网站可以动态的展现控制点不同而绘制的不同的曲线。而对于控制点的计算。。作者还是选择了百度一下毕竟数学不好:)。具体算法有兴趣的同学可以深入了解下,现在直接说下计算控制点的结论。

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

上面的公式涉及到四个坐标点,当前点,前一个点以及后两个点,而当坐标值为下图展示的时候绘制出来的曲线如下所示:

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

不过会有一个问题就是起始点和最后一个点不能用这个公式,不过那篇文章也给出了边界值的处理办法:

如何基于canvas基于贝塞尔曲线平滑拟合折线段?

所以在将折线换成平滑曲线的时候,将边界值以及其他控制点计算好之后代入到贝塞尔函数中就完成了:

//核心实现
this.series.data.forEach(function(item, index) { //找到前一个点到下一个点中间的控制点
  var scale = 0.1 //分别对于ab控制点的一个正数,可以分别自行调整
  var last1X = 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
  } 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);
    //绘制出上一个点到当前点的贝塞尔曲线
  })

由于我每次遍历的点都是当前点,但是文章中给出的公式是计算会知道下一个点的控制点算法,故在代码实现中我将所有点的计算挪前了一位。当index = 0时也就是初始点是不需要曲线绘制的,因为我们绘制的是从前一个点到当前点的曲线,没有到0的曲线需要绘制。从index = 1开始我们就可以正常开始绘制,从0到1的曲线,由于index = 1时是没有在他前面第二个点的故其属于边界值点,也就是需要特殊进行计算,以及最后一个点。其余均按照正常公式算出AB的xy坐标代入贝塞尔函数即可。

最后

源代码见这里

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持申博开户。


本文地址:http://www.cvmachine.com/a/question/96317.html
Tags: canvas 曲线 基于
编辑:申博开户网
m88 188bet uedbet 威廉希尔 明升 bwin 明升88 bodog bwin 明升m88.com 18luck 188bet unibet unibet Ladbrokes Ladbrokes 真钱的棋牌游戏 casino m88明升 明升 明升 m88.com 188bet m88 明陞 uedbet赫塔菲官网 365bet官网 nba英文官网 嘉年华官网 皇冠备用 火箭队官网 中国人论坛 bet 单双公式 博彩资讯网 w88优德 港京印刷图库 澳门足球盘 球盘 娱乐场 联众网站 沙龙365 7080棋牌游戏 真钱 澳门回归日期 大众娱乐网 卡宾官方旗舰店 mg电子 全亚洲首选288x nba即时比分 本港 中国足彩竞猜网 斗地主规则 七星彩论坛图规 网上赌博 单双规律 沈泳 华夏娱乐网 明升备用网址 梦网书城 盈禾国际 明升体育 博狗沃鑫 卡宾官方旗舰 信誉赌场 球探比分网 竞彩论坛空间 三亚娱乐 soutec 北京福利彩票 澳门彩票公司 北京pk10 凤凰 纸牌小游戏 皇冠现金代理 银河国际亚洲首选288x 三六八高手论坛 空中城市 赌球心得 188比分 让球规则 三星娱乐城 诺贝尔娱乐城 今天有nba直播吗 5060全讯网 新世纪 久乐娱乐场 世界杯盘口 易胜博 麻将单机版 华人娱乐总站 财神爷心水论坛 巴特 澳门百利宫 明升 红姐统一主图库 九龙老牌图库 打牌 娱乐王子 娱乐真人 亚豪平台 真人娱乐 飞禽走兽老虎机 188bet下载 博发娱乐城 北单 陈怀生 比分188 百万图库 老挝赌博 pc蛋蛋注册 天上人间娱乐 红姐统一图库 产业新闻网 葡京
相关文章列表
关于我们 | 联系我们 | 友情链接 | 网站地图 | Sitemap | App | 返回顶部