Bezier 曲线的妙用

贝塞尔曲线在设计软件中很常见,它可以根据控制点来得出一条平滑的曲线。不仅可以用作运动路径,还能作为时间插值使用。 本文介绍一下贝塞尔曲线在代码中的使用,以 JavaScript 为例。

二阶曲线

这是一个二阶贝塞尔函数,它需要传入 3 个点坐标,描述一个物体的运动曲线,然后根据时间 progress 计算出当前进度下的物体位置:

function bezier(x1,y1,x2,y2,x3,y3, progress) {
  let px1=(x2-x1)*t+x1;
  let py1=(y2-y1)*t+y1;
  let px2=(x3-x2)*t+x2;
  let py2=(y3-y2)*t+y2;
  return[(px2-px1)*t+px1,(py2-py1)*t+py1]
}

用法如下:传入 3 个点的坐标,传入 progress 进度(取值 0 ~ 1)

let [cx, cy] = bezier(80,250, 200,30, 300,370, progress)


1.在 React 中使用

在 React 中,你可以用 setInterval(),结合状态做到动画。

export function Widget() {
    // 定义 progress 状态
    const [progress, setProgress] = useState(0)

    useEffect(() => {
        let frame = 0
        let interval = setInterval(() => {
            // 每帧(16ms)刷新一次
            setProgress( ((frame++ * 16) % 5000) / 5000 )
        }, 16)
        // 组件卸载时记得清理定时器
        return () => { clearInterval(interval) }
    }, []);

    // 计算小球位置
    let [cx, cy] = bezier(80,250, 200,30, 300,370, progress)

    return (
        <svg width={400} height={400}>
            {/* 绘制小球 */}
            <circle cx={ cx } cy={ cy } r={10} fill="#FFD93FFF"/>
        </svg>
    )
}

效果如下



2.在 AE 中使用

在 AE 表达式中,可以通过 time 属性 + linear() 函数来获得 progress

// 在 3秒 ~ 5秒 的时候,进度从 0 进行到 1
let progress = linear(time, 3, 5, 0, 1);

有了 progress 之后就和之前一样好办了,只需要调用 bezier() 计算出坐标,然后给到图层的位置属性就可以了。

可以给图层的位置属性设置表达式:

// 在 3秒 ~ 5秒 的时候,进度从 0 进行到 1
let progress = linear(time, 3, 5, 0, 1);
// 复制bezier函数到这里
function bezier(){ ... }
// 调用它
let pos = bezier(.../*自行设置*/, progress);
// 返回坐标
[pos[0], pos[1]];


3.在 Remotion 中使用

在 Remotion 中可以使用 useCurrentFrame() 结合 interpolate() 来映射时间范围到 progress

// 获取当前时间(帧)
const frame = useCurrentFrame()
// 在第 30 到 90帧 的时候,进度从 0 进行到 1
const progress = interpolate(frame, [30, 90], [0,1])

之后就和 React 中使用没有差别了。

三阶曲线

三阶贝塞尔曲线,待补充。

最后修改:2025年09月03日