Html5 根据多个点使用canvas贝赛尔曲线画一条平滑的曲线
众所周知想用canvas画一条曲线我们可以使用这些函数:
二次曲线: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、b值对我们的曲线会造成什么影响
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
*根据已知点获取第i个控制点的坐标
*param ps 已知曲线将经过的坐标点
*param i 坐标点的个数
*param a,b 可以自定义的正数
*/
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}
}
}
但是对于最初一段和最后一段,是不能用上述方法的,因为(x-1,y-1)和(xn+1,yn+1),这两个点其实是不存在的,文章中提到了两种解决方法,这里只看比较简单的第一种:用(x0,y0)的值作为(x-1,y-1)的值,用(xn,yn)的值作为(xn+1,yn+1)的值。
改进后的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
*根据已知点获取第i个控制点的坐标
*param ps 已知曲线将经过的坐标点
*param i 第i个坐标点
*param a,b 可以自定义的正数
*/
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}
}
}
效果如下:
加入辅助点说明:
红色是通过点与控制点的连线,绿色是控制点与控制点之间的连线,蓝色的是直线连接各点画的线。
具体实现过程见 demo.zip