平滑多条曲线

Canvas 画曲线(非圆弧)有两个方法:

  1. quadraticCurveTo(cpx, cpy, x, y)

  2. bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)


本文只讲quadraticCurveTo(),先来看如何画一条曲线。


我们需要曲线的 开始点(p0) 和 结束点(p1),外加一个控制点(pc),虽然说有3个点,但实际画图时,我们关心的只是开始点和结束点

可以看到曲线经过 p0 和 p1 两个点,控制点pc 只是用来调整曲线的形状的。

如果曲线要经过3个点呢?不好做了吧?


这里介绍一个公式,从上图可知,曲线不经过控制点,但曲线肯定会经过某个点,这个点怎么求呢?

xc = x1 * 2 - (x0 + x2) / 2; 
yc = y1 * 2 - (y0 + y2) / 2; 


(x0, y0) 是开始点,(x2, y2) 是结束点,(xc, yc) 是控制点,而 (x1, y1) 则是曲线会经过的某点。


继续刚才的3点画线,我们可以把第二个点当作点(x1, y1),于是


如果有4个点呢?这里需要总结一个算法,先看一个图:


首先声明一下,这张图原本不是用来解释本文的,我从别处截来,但是可以讲解我想说的东西。

这条曲线很平滑,注意中间的两个黑点,他俩都是相邻控制点的中点,于是我得出了以下结论:

通过第一个控制点,循环算出之后的控制点,相邻的两个控制点的中点是曲线会经过的点。


仔细看3个点的例子,我们算出了控制点,它就是第一个控制点。

第二个控制点怎么算呢?


我们知道曲线会经过p2,所以 p2 是第一个控制点 和 第二个控制点的中点,于是

// (ctrlX, ctrlY) 是第一个控制点
ctrlX = 2 * x2 - ctrlX;
ctrlY = 2 * y2 - ctrlY;


之后的控制点都可以这样计算,下面我给出一段算法:

/**
 * @param {object} c 即canvas.getContext('2d')
 * @param {array} points 数组元素格式为{x: 0, y: 0}
 */
function drawMultipleCurves(c, points) {
    var len = points.length;
    if (len < 3) {
        return;
    }
    
    // 计算第一个控制点
    var ctrlX = 2 * points[1].x - 0.5 * (points[0].x + points[2].x);
        ctrlY = 2 * points[1].y - 0.5 * (points[0].y + points[2].y);

    // 画出前三个点的曲线
    c.beginPath();
    c.moveTo(points[0].x, points[0].y);
    c.quadraticCurveTo(ctrlX, ctrlY, points[2].x, points[2].y);

    if (len === 3) {
        c.stroke();
        return;
    }
    
    for (var i = 2; i < len - 1; i++) {
        // 计算下个控制点
        ctrlX = 2 * points[i].x - ctrlX;
        ctrlY = 2 * points[i].y - ctrlY;
        
        c.quadraticCurveTo(ctrlX, ctrlY, points[i + 1].x, points[i + 1].y);
    }
    c.stroke();
}


最后看一个经过6个点的曲线吧

posted @ 2012-02-18 15:47  越己  阅读(1719)  评论(1编辑  收藏  举报