贝塞尔曲线手势生成技术解析

28

主题

184

回帖

581

积分

管理员

积分
581
// 随机数生成函数
function random(a, b) {
    return rand.randNumber(a, b);
}

/**
 * @param time_ms {number} 执行时间 ms 例如 3000
 * @param t {number} 间隔系数 0-1 间, 越小线条线条越圆润,计算量越大, 例如 0.005
 * @param pt {number[][]} 控制点坐标的二维数组 例如 [[137,169],[140,283]]
 */
function bezier(time_ms, t, pt) {
    var gesture1 = new path(); // 创建一个手势对象
    gesture1.setStarTime(10); // 开始前延时
    var fps = 1 / t;
    for (let i = 0; i <= fps; i++) {
        let copyArr = Array.from(pt, row => row.slice()); // 拷贝一份 js的数组是引用传递
        var xy = calculateBezier(i * t, copyArr);
        gesture1.addPoint(xy[0], xy[1]);
    }
    gesture1.setDurTime(time_ms); // 完成时间
    auto.dispatchGesture([gesture1]); // 执行手势操作
}

/**
 * @param t {number} 0 ~ 1的时间
 * @param point2Fs {number[][]} 贝塞尔点集合
 * @return {number[]} 返回当前时间下的贝塞尔点
 */
function calculateBezier(t, point2Fs) {
    let len = point2Fs.length;
    for (let i = len - 1; i > 0; i--) {
        for (let j = 0; j < i; j++) {
            point2Fs[j][0] = point2Fs[j][0] + (point2Fs[j + 1][0] - point2Fs[j][0]) * t;
            point2Fs[j][1] = point2Fs[j][1] + (point2Fs[j + 1][1] - point2Fs[j][1]) * t;
        }
    }

    return [parseInt(point2Fs[0][0]), parseInt(point2Fs[0][1])];
}

// 控制点
var CtrlPt1 = [
    [167, 625], // 起点1
    [200, 200],
    [500, 500],
    [300, 100]
];

var CtrlPt2 = [
    [309, 833], // 起点2
    [500, 700],
    [600, 600],
    [500, 300]
];

var CtrlPt3 = [
    [151, 892], // 起点3
    [300, 880], // 引入Y坐标的小变化
    [450, 895], // 引入Y坐标的小变化
    [600, 885]  // 引入Y坐标的小变化
];

// 执行贝塞尔手势
bezier(4000, 0.005, CtrlPt1);
bezier(4000, 0.005, CtrlPt2);
bezier(4000, 0.005, CtrlPt3);

bezier(time_ms, t, pt) 函数

679177f0eebd8.png
1月24日(1).gif

这个函数用于生成并执行一个贝塞尔曲线手势。具体参数解释如下:

  1. time_ms

    • 作用:定义手势从开始到结束的总时间,单位是毫秒。
    • 示例bezier(4000, 0.005, CtrlPt1); 表示整个手势将在4000毫秒(4秒)内完成。
  2. t

    • 作用:定义间隔系数,用于控制贝塞尔曲线的计算精度和线条的圆润度。t 的取值范围是0到1之间。
    • 精度与圆润度:较小的 t 值意味着计算的间隔更小,生成的曲线更圆润,但计算量也会相应增加。较大的 t 值则表示计算间隔更大,曲线可能会显得更折线化,计算量较少。
    • 示例bezier(4000, 0.005, CtrlPt1); 中的 t 为0.005,表示每0.005的时间单位计算一次贝塞尔曲线上的点。
  3. pt

    • 作用:定义贝塞尔曲线的控制点坐标。控制点决定了贝塞尔曲线的形状。起点和终点是曲线的两端,而中间的控制点决定了曲线如何弯曲。
    • 格式pt 是一个二维数组,其中每个子数组包含两个数字,分别表示控制点的X坐标和Y坐标。
    • 示例
      • CtrlPt1 定义了起点 [167, 625],两个控制点 [200, 200] 和 [500, 500],以及终点 [300, 100]
      • CtrlPt2 定义了起点 [309, 833],两个控制点 [500, 700] 和 [600, 600],以及终点 [500, 300]
      • CtrlPt3 定义了起点 [151, 892],两个控制点 [300, 880] 和 [450, 895],以及终点 [600, 885]

calculateBezier(t, point2Fs) 函数

这个函数用于计算贝塞尔曲线在某个时间点 t 上的坐标。具体参数解释如下:

  1. t

    • 作用:表示时间参数,取值范围是0到1之间。t=0 对应曲线的起点,t=1 对应曲线的终点。
    • 计算过程:通过递归地插值计算,最终得到 t 时刻的曲线坐标。
  2. point2Fs

    • 作用:包含贝塞尔曲线的控制点坐标。
    • 格式:二维数组,每个子数组包含两个数字,分别表示控制点的X坐标和Y坐标。

计算贝塞尔曲线的详细过程

贝塞尔曲线的计算通常使用递归的插值方法。在 calculateBezier 函数中,具体步骤如下:

  1. 拷贝控制点数组

    • 由于 JavaScript 中数组是引用传递的,为了避免修改原始控制点数组,首先需要拷贝一份 point2Fs
  2. 递归插值计算

    • 对于每个控制点,通过线性插值计算新的点,直到只剩下一个点为止。
    • 具体公式为:point2Fs[j][0] = point2Fs[j][0] + (point2Fs[j + 1][0] - point2Fs[j][0]) * t;
      • 这里的 t 是当前时间参数,point2Fs[j][0] 和 point2Fs[j + 1][0] 是相邻控制点的X坐标。
      • 通过这个公式计算新的X坐标。
    • 同样地,计算新的Y坐标:point2Fs[j][1] = point2Fs[j][1] + (point2Fs[j + 1][1] - point2Fs[j][1]) * t;
  3. 返回结果

    • 最终,point2Fs[0] 将包含贝塞尔曲线在 t 时间点的坐标。
    • 返回这个坐标 [parseInt(point2Fs[0][0]), parseInt(point2Fs[0][1])],其中 parseInt 用于将坐标转换为整数。

执行贝塞尔手势

在 bezier 函数中,通过循环调用 calculateBezier 函数,生成从起点到终点的一系列点,并将这些点添加到 gesture1 对象中,最后通过 auto.dispatchGesture([gesture1]); 执行这个手势操作。

举报 回复