canvas进阶——贝塞尔公式推导与物体跟随复杂曲线的轨迹运动

写在最前

在以前的这篇文章中咱们提到了对于贝塞尔公式的运用。本次分享一下如何推导贝塞尔公式以及附一个简单的?即小球跟随曲线轨迹运动。html

欢迎关注个人博客,不按期更新中——git

效果预览

2017-12-26 14_18_42

demo地址github

对于如何绘制连续的贝塞尔曲线能够参照这篇文章:基于canvas使用贝塞尔曲线平滑拟合折线段canvas

在本例中生成的曲线由以上文章中的源码提供。动画

贝塞尔曲线公式推导

7460499-2603066c32c19ba9

上面这张图是贝塞尔曲线的完整公式,看起来一脸懵逼=。=,由于这是N阶的推导公式,本次咱们以一二阶贝塞尔公式的推导来理解一下这个推导公式的由来。先来看下网上流传已久的几张贝塞尔动图:this

1012380-20170218214830535-1198588161

1012380-20170218214945519-1357139579

1012380-20170218215045082-1043570102

在这三张图中最重要的部分是咱们须要理解变量t。t的取值范围是0-1。从上面的gif中也能够看出来彷佛曲线的绘制过程就是t从0到1的过程。嗯其实就是这样的。t的真实含义是什么呢?spa

在p0p一、p1p二、p2p3等等的起点到控制点再到终点的连线中,每段连线都被分割成了两部分(仔细看动图中的黑色、绿色、蓝色圆点),各段连线中两部分的比值都是相同的,比值范围是0到1,而这个比值就是tprototype

来看下面的一阶贝塞尔曲线示意图:code

image

pt是p0p1上的任意一点,p0pt / ptp1 = t。从而咱们能够引出下面的推导视频

image

此时t为时间,v为速度。咱们能够看作从p0到p1的距离等于固定速度乘以固定时间

image

故到p上某一点的时间为固定的速度乘以某个时间值。同时固定的速度已经已经能够表示为上面的推导公式。此时等式右边就造成了t(0,1) / t;即至关于某个时间值 / 固定时间值,即产生了咱们一开始所强调的变量t,其取值范围为[0,1]。从而下面的等式也就比较好理解了。

image

至此一阶贝塞尔曲线咱们已经推到了出来,其中变量为起点、终点与比值t。

那么二阶公式如何从一阶过渡过去呢?

来看下面这张图:

image

其中Pp(t)的通过路径就是咱们所求的二阶贝塞尔曲线,那么其实咱们也能够将其从一阶进行演变:

image

咱们先将pa、pb两个点所连线段当作一阶曲线,以后再由两端一阶曲线分别表示pa、pb,最后就获得了咱们的二阶曲线公式。仔细观察就能发现这和咱们最初的完整公式是相同的:

7460499-2603066c32c19ba9

其中n选择不一样数值时就能够得出不一样阶的曲线公式。同时从上面的推导过程也能够知道,不管是几阶曲线,咱们均可以彻底由一阶来表示,而这个“表示”的过程就是咱们在上面看到的造成动画中那些辅助线。故能够感觉下做者本身写的曲线造成动画中的效果,每段辅助线均由一阶曲线造成:

2017-12-28 17_21_52

相关地址

物体跟随复杂曲线轨迹运动

当咱们知道曲线的公式有何而来以后,如何让小球沿着曲线运动就很好理解了。咱们生成的每段曲线都是能够用公式表示出来的,也正因如此咱们就能够获得每一个t值时的曲线坐标点。从而知道物体的绘制坐标。

//核心逻辑
LinearGradient.prototype.drawBall = function() {
    var self = this
    var item = ctrlNodesArr[ctrlDrawIndex] 
    //存储了各段曲线的控制点
    //各段曲线均为三阶贝塞尔,故下面计算x,y值代入到了三阶公式中
    var ctrlAx = item.cAx,//各个控制点
        ctrlAy = item.cAy,
        ctrlBx = item.cBx,
        ctrlBy = item.cBy,
    ...
    if(item.t > 1) {
        ctrlDrawIndex++ //当一段曲线的t>1说明曲线已经走到头
    }else {
        self.ctx.clearRect(0, 0, self.width, self.height)
        item.t += 0.05
        var ballX = ox * Math.pow((1 - item.t), 3) + 3 * ctrlAx * item.t * Math.pow((1 - item.t), 2) + 3 * ctrlBx * Math.pow(item.t, 2) * (1 - item.t) + x * Math.pow(item.t, 3)
        var ballY = oy * Math.pow((1 - item.t), 3) + 3 * ctrlAy * item.t * Math.pow((1 - item.t), 2) + 3 * ctrlBy * Math.pow(item.t, 2) * (1 - item.t) + y * Math.pow(item.t, 3)
        //代入三阶贝塞尔曲线公式算出小球的坐标值
        self.ctx.beginPath()
        self.ctx.arc(ballX, ballY, 5, 0, Math.PI * 2, false)
        self.ctx.fill()
    }
    if(ctrlDrawIndex !== ctrlNodesArr.length) {
        window.requestAnimationFrame(newMap.drawBall.bind(self))
    }
}

其余canvas相关文章

最后

demo地址:这里✨✨

源码地址:欢迎star

惯例po做者的博客,不定时更新中——

有问题欢迎在issues下交流。

相关文章
相关标签/搜索