贝塞尔曲线不仅有起点和终点,而且还有控制曲线形状的控制点。在您链接的 DynApi 演示中,端点标记为黄色,控制点标记为红色。
您的路径将是一系列贝塞尔曲线,首尾相连。
因此,让我们使用您的伪代码,但我们会将所有没有.time 属性的点都视为控制点。
function Path(points) {
this.points = points;
// Sanity check.
if (points[0].time == undefined || points[points.length - 1].time == undefined)
throw new Error("all control points must be between two real points");
}
Path.prototype.getXYAtTime = function (t) {
var points = this.points;
// First, see if t is out of range.
if (t < points[0].time)
return points[0];
if (t > points[points.length - 1].time)
return points[points.length - 1];
// OK, t is in range. Find out which Bezier curve we're in.
//
// Specifically we want 'start' and 'stop' to be the indexes of two points
// that each have a .time property, bracketing the current time t; and
// all the points in between 'start' and 'stop' should be control points.
//
var start = 0, stop = points.length - 1;
for (var i = 1; i < points.length; i++) {
var p = points[i];
if (t < p.time) {
stop = i;
break;
}
if (p.time != undefined)
start = i;
}
var n = stop - start;
// Adjust t to be in the range [0, 1).
var t0 = points[start].time, t1 = points[stop].time;
t = (t - t0) / (t1 - t0);
var tInv = 1 - t;
// Now calculate the current position in the curve.
// Wikipedia says this is:
// sum for i = 0 to n of (n C i * (1 - t) ^ (n - i) * t ^ i * P[i])
//
var x = 0, y = 0;
for (var i = 0; i <= n; i++) {
var p = points[start + i];
var c = nCr(n, i) * Math.pow(1 - t, n - i) * Math.pow(t, i);
x += c * p.x;
y += c * p.y;
}
return {x: x, y: y};
}
// The number of k-combinations of a set of size n.
function nCr(n, k) {
var z = 1;
for (var i = 1; i <= k; i++)
z *= (n + 1 - i) / i;
return z;
}
这样数学部分就完成了。将它连接到画布并让它运行取决于您。
以下是您如何调用该方法:
// Here's a Path consisting of a single Bezier curve.
var path = new Path([
{x: 200, y: 150, time: 0}, // start point
{x: 200, y: 500}, // 2 control points
{x: 250, y: 100},
{x: 500, y: 300, time: 50} // end point
]);
var p = path.getXYAtTime(2.718);
alert(p.x + ", " + p.y);