二次曲线:quadraticCurveTo(cp1x, cp1y, x, y)
贝塞尔曲线:bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
画圆弧:arcTo(x1,y1,x2,y2,radius);
但是如果一组点给你,怎么通过这些已知点画一条平滑的曲线呢?使用二次曲线,或是圆弧?恐怕这些都没法满足曲线多变的需求,唯一的方法就是一段贝塞尔曲线连着一段贝塞尔曲线。于是乎我在百度上大海捞针,发现居然没有一个人把自己的算法放出来,太不人道了,被逼无奈的我只好从理论入手,先了解一下贝赛尔曲线的控制点计算方法,说起来容易,想要画贝赛尔曲线谁都会,但是计算控制点你就会遇到一点小麻烦,这次我突击恶补了一下终于弄懂了怎么计算贝赛尔曲线控制点的方法。
百度一下贝塞尔曲线控制点计算方法,在搜索结果第一条有一篇百度文库的文章《怎样确定 Bezier 曲线的控制点》,通过浏览全文找到了计算控制点的公式,以下是核心结论。

哎妈,是不是看了之后头都大了,反正公式已经给出来了,就让我们用行数去实现这个Ai和Bi点的计算吧,忘了说一句a、b可以为任意正数,等我们完成了我们的demo可以测试一下不同的a、bz值对我们的曲线会造成什么影响
| |
| |
| |
| |
| |
| |
| function getCtrlPoint(ps, i, a, b){ |
| if(!a||!b){ |
| a=0.25; |
| b=0.25; |
| } |
| var pAx = ps[i].x + (ps[i+1].x-ps[i-1].x)*a; |
| var pAy = ps[i].y + (ps[i+1].y-ps[i-1].y)*a; |
| var pBx = ps[i+1].x - (ps[i+2].x-ps[i].x)*b; |
| var pBy = ps[i+1].y - (ps[i+2].y-ps[i].y)*b; |
| return { |
| pA:{x:pAx,y:pAy}, |
| pB:{x:pBx,y:pBy} |
| } |
| } |
写完这些心中不免有些疑问,ps是我们的坐标点数组,我们的第0个点和第n-1、n个点怎么办,我函数中的ps[n-1+2]和ps[n+2]是会超数组范围的,于是我接着往文库文章下面看,文中他给我们提供了两种解决办法。

方法2确实麻烦,我比较急迫的想看到效果,于是选择了第一种方案,有兴趣的同学可以在附件中找到这篇文章,自己回去研究研究,在我的算法上进行改进。

于是乎我改进了我的函数。
| |
| |
| |
| |
| |
| |
| function getCtrlPoint(ps, i, a, b){ |
| if(!a||!b){ |
| a=0.25; |
| b=0.25; |
| } |
| |
| if(ips.length-3){ |
| var last=ps.length-1 |
| var pBx = ps[last].x - (ps[last].x-ps[last-1].x)*b; |
| var pBy = ps[last].y - (ps[last].y-ps[last-1].y)*b; |
| }else{ |
| var pBx = ps[i+1].x - (ps[i+2].x-ps[i].x)*b; |
| var pBy = ps[i+1].y - (ps[i+2].y-ps[i].y)*b; |
| } |
| return { |
| pA:{x:pAx,y:pAy}, |
| pB:{x:pBx,y:pBy} |
| } |
| } |
nice!接下来开始我们的canvas画图工作吧。
一、首先顶一个点数组,将我们即将描绘的坐标点模拟出来。
| var point=[{x:0,y:380},{x:100,y:430},{x:200,y:280},{x:300,y:160}, |
| {x:400,y:340},{x:500,y:100},{x:600,y:300},{x:700,y:240}] |
二、开始遍历我们的坐标数组这里为了方便描述point简称p,下标point[n]简称pn。
1)创建一个画布,将画笔的移动到p0点,这是我们的起始点。
2)循环点数组,在循环体中使用我们定义的getCtrlPoint函数计算出第i个点A、B两个控制点的点坐标。
| var ctrlP=getCtrlPoint(point,i-1); |
| ctx.bezierCurveTo(ctrlP.pA.x, ctrlP.pA.y, ctrlP.pB.x,ctrlP.pB.y, point[i].x, point[i].y); |
3)刷新页面看效果吧

是不是很有感觉?为了再次确认我话的曲线是否有问题,我又画了一些辅助线,标出了控制点,并且直线连接各个点与我们的贝塞尔曲线做个对比。于是得到了下图。

红色是通过点与控制点的连线,绿色是控制点与控制点之间的连线,蓝色的是直线连接各点画的线。
可以看到所有的点一个不落的被贝赛尔曲线贯穿,达到了我们希望的效果,通过这次探索,以后什么曲线都不怕了,明天再研究研究怎么把这条实线改成虚线,这些都是HighCharts、ECharts直流的基础,有了这个demo曲线图表什么的再也不怕了,想想心里还有些小激动呢。
最后奉上本次成果的demo:
demo.rar
还有前面提到的百度文库的文章:
贝塞尔曲线控制点确定的方法.doc
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通