使用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小球曲线运动的资料请关注脚本之家其它相关文章!