使用JavaScript实现小球按照贝塞尔曲线运动
作者:xvch
介绍
要在 JavaScript 中实现一个按照贝塞尔曲线运动的小球,关键是要掌握贝塞尔公式的基本原理和实现方式,以及使用 JavaScript 处理动画和物理运算。
以下是实现的核心步骤:
构建
HTML。绘制小球。
实现贝塞尔曲线路径。
实现动画循环。
接下来,我们将详细介绍这些步骤。
构建 HTML
首先,我们需要构建一个简单的 HTML 文件,用于绘制我们的动画。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
canvas {
border-radius: 15px;
background-color: #ffffff;
}
</style>
</head>
<body>
<canvas id="demo-canvas" width="800" height="600"></canvas>
<script>
</script>
</body>
</html>
代码足够简单就行,我们只需要一个 canvas 元素即可。
绘制小球
接下来,我们需要在 JavaScript 中获取 canvas 元素,并使用 canvas 的 API 来绘制一个小球。
const canvas = document.getElementById('demo-canvas');
const ctx = canvas.getContext('2d');
const ball = {
radius: 20,
color: '#ff0000',
};
function drawBall(x, y) {
ctx.beginPath();
ctx.arc(x, y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
}
由于我们需要让小球沿着贝塞尔曲线运动,所以在调用 drawBall 函数时,需要传递最新的坐标位置,因此我们需要给 drawBall 传递 x 和 y 两个参数。
贝塞尔公式
关于贝塞尔公式,网上讲解的文章博客多如牛毛,这里就不赘述了,直接上结论。

实现贝塞尔曲线路径
接下来,我们来实现 NN 阶贝塞尔曲线路径,并获取曲线上的点。
function factorial(n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
function binomialCoefficient(n, i) {
let res = factorial(n) / (factorial(i) * factorial(n - i));
return Math.floor(res);
}
function bernsteinPolynomial(n, i, t) {
return binomialCoefficient(n, i) * Math.pow(1 - t, n - i) * Math.pow(t, i);
}
function getPointOnBezierCurve(t, arr) {
let x = 0;
let y = 0;
for (let i = 0; i < arr.length; i++) {
let bernstein = bernsteinPolynomial(arr.length - 1, i, t);
x += bernstein * arr[i].x;
y += bernstein * arr[i].y;
}
return { x, y };
}
其中,getPointOnBezierCurve 函数第一个参数 t 取值范围是 0到 1,该参数决定了曲线上的点的位置。第二个参数 arr 是控制点数组,该参数决定了贝塞尔曲线的阶数(即 N),例如:arr 传入的是 [p0, p1, p2, p3],那么对应的贝塞尔曲线的阶数 N就等于 3。
实现小球运动
接下来,我们需要实现小球沿着贝塞尔曲线运动,即不断更新小球的坐标位置,并调用 drawBall 函数来绘制小球。
let t = 0;
const points = [
{ x: 100, y: 50 },
{ x: 200, y: 300 },
{ x: 700, y: -20 },
{ x: 500, y: 500 },
];
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const point = getPointOnBezierCurve(t, points);
drawBall(point.x, point.y);
t += 0.01;
if (t > 1) {
setTimeout(() => {
t = 0;
requestAnimationFrame(animate);
}, 1000);
}
else {
requestAnimationFrame(animate);
}
}
animate();
展示

完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
canvas {
border-radius: 15px;
background-color: #ffffff;
}
</style>
</head>
<body>
<canvas id="demo-canvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('demo-canvas');
const ctx = canvas.getContext('2d');
const ball = {
radius: 20,
color: '#ff0000',
};
function drawBall(x, y) {
ctx.beginPath();
ctx.arc(x, y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = ball.color;
ctx.fill();
}
function factorial(n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
function binomialCoefficient(n, i) {
let res = factorial(n) / (factorial(i) * factorial(n - i));
return Math.floor(res);
}
function bernsteinPolynomial(n, i, t) {
return binomialCoefficient(n, i) * Math.pow(1 - t, n - i) * Math.pow(t, i);
}
function getPointOnBezierCurve(t, arr) {
let x = 0;
let y = 0;
for (let i = 0; i < arr.length; i++) {
let bernstein = bernsteinPolynomial(arr.length - 1, i, t);
x += bernstein * arr[i].x;
y += bernstein * arr[i].y;
}
return { x, y };
}
let t = 0;
const points = [
{ x: 100, y: 50 },
{ x: 200, y: 300 },
{ x: 700, y: -20 },
{ x: 500, y: 500 },
];
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const point = getPointOnBezierCurve(t, points);
drawBall(point.x, point.y);
t += 0.01;
if (t > 1) {
setTimeout(() => {
t = 0;
requestAnimationFrame(animate);
}, 1000);
}
else {
requestAnimationFrame(animate);
}
}
animate();
</script>
</body>
</html>
扩展
在此基础上,我们可以添加更多的小球,让它们沿着随机化的贝塞尔曲线运动,从而形成更加复杂的动画效果。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
canvas {
border-radius: 15px;
background-color: #ffffff;
}
</style>
</head>
<body>
<canvas id="canvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
function getRandomPoints() {
const randomNumber = (min, max) => {
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
const number = randomBuffer[0] / (0xffffffff + 1);
return Math.floor(number * (max - min) + min);
}
let points = [
{ x: parseInt(canvas.width / 5), y: parseInt(canvas.height / 2), },
{ x: parseInt(canvas.width / 5 * 4), y: parseInt(canvas.height / 2), },
];
const count = randomNumber(2, 5);
const minX = -100, maxX = 900, minY = -100, maxY = 700;
for (let i = 0; i < count; i++) {
points.splice(i + 1, 0, { x: randomNumber(minX, maxX), y: randomNumber(minY, maxY) });
}
return points;
}
function factorial(n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
function binomialCoefficient(n, i) {
let res = factorial(n) / (factorial(i) * factorial(n - i));
return Math.floor(res);
}
function bernsteinPolynomial(n, i, t) {
return binomialCoefficient(n, i) * Math.pow(1 - t, n - i) * Math.pow(t, i);
}
function getPointOnBezierCurve(t, arr) {
let x = 0;
let y = 0;
for (let i = 0; i < arr.length; i++) {
let bernstein = bernsteinPolynomial(arr.length - 1, i, t);
x += bernstein * arr[i].x;
y += bernstein * arr[i].y;
}
return { x, y };
}
const radius = 20;
const colors = ['green', 'purple', 'orange'];
let t = 0.0;
let points = [getRandomPoints(), getRandomPoints(), getRandomPoints()];
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < points.length; i++) {
let pos = getPointOnBezierCurve(t, points[i]);
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, 2 * Math.PI);
ctx.fillStyle = colors[i];
ctx.fill();
}
t += 0.01;
if (t > 1) {
setTimeout(() => {
t = 0;
points = [getRandomPoints(), getRandomPoints(), getRandomPoints()];
requestAnimationFrame(animate);
}, 1000);
}
else {
requestAnimationFrame(animate);
}
}
animate();
</script>
</body>
</html>
展示

以上就是使用JavaScript实现小球按照贝塞尔曲线运动的详细内容,更多关于JavaScript小球曲线运动的资料请关注脚本之家其它相关文章!
