使用JavaScript實現(xiàn)小球按照貝塞爾曲線運動
介紹
要在 JavaScript 中實現(xiàn)一個按照貝塞爾曲線運動的小球,關(guān)鍵是要掌握貝塞爾公式的基本原理和實現(xiàn)方式,以及使用 JavaScript 處理動畫和物理運算。
以下是實現(xiàn)的核心步驟:
構(gòu)建
HTML。繪制小球。
實現(xiàn)貝塞爾曲線路徑。
實現(xiàn)動畫循環(huán)。
接下來,我們將詳細介紹這些步驟。
構(gòu)建 HTML
首先,我們需要構(gòu)建一個簡單的 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();
}
由于我們需要讓小球沿著貝塞爾曲線運動,所以在調(diào)用 drawBall 函數(shù)時,需要傳遞最新的坐標位置,因此我們需要給 drawBall 傳遞 x 和 y 兩個參數(shù)。
貝塞爾公式
關(guān)于貝塞爾公式,網(wǎng)上講解的文章博客多如牛毛,這里就不贅述了,直接上結(jié)論。

實現(xiàn)貝塞爾曲線路徑
接下來,我們來實現(xiàn) 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 函數(shù)第一個參數(shù) t 取值范圍是 0到 1,該參數(shù)決定了曲線上的點的位置。第二個參數(shù) arr 是控制點數(shù)組,該參數(shù)決定了貝塞爾曲線的階數(shù)(即 N),例如:arr 傳入的是 [p0, p1, p2, p3],那么對應的貝塞爾曲線的階數(shù) N就等于 3。
實現(xiàn)小球運動
接下來,我們需要實現(xiàn)小球沿著貝塞爾曲線運動,即不斷更新小球的坐標位置,并調(diào)用 drawBall 函數(shù)來繪制小球。
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>
擴展
在此基礎(chǔ)上,我們可以添加更多的小球,讓它們沿著隨機化的貝塞爾曲線運動,從而形成更加復雜的動畫效果。
<!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實現(xiàn)小球按照貝塞爾曲線運動的詳細內(nèi)容,更多關(guān)于JavaScript小球曲線運動的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解JS中定時器setInterval和setTImeout的this指向問題
在js中setTimeout和setInterval都是用來定時的一個功能,下面這篇文章主要給介紹了JS中setInterval和setTImeout的this指向問題,文中通過示例介紹的很詳細,有需要的朋友可以參考借鑒,一起來看看吧。2017-01-01
vscode?對?typescript代碼調(diào)試的步驟
在VS?Code中,要對?TypeScript代碼進行調(diào)試,需要先編譯?TypeScript?代碼為JavaScript代碼,這篇文章主要介紹了vscode對typescript代碼調(diào)試的方法,需要的朋友可以參考下2023-03-03
Javascript 生成指定范圍數(shù)值隨機數(shù)
查手冊后才知道, 介紹的信息少得可憐吶, 沒有介紹生成 m-n 范圍的隨機數(shù)..., 就只是給你一個 Math.random() 了事.2009-01-01
使用element-ui的upload組件上傳代碼包時遇到的問題小結(jié)
這篇文章主要介紹了使用element-ui的upload組件上傳代碼包時遇到的問題及總結(jié),本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12
探究Javascript模板引擎mustache.js使用方法
這篇文章主要為大家介紹了Javascript模板引擎mustache.js使用方法,mustache.js是一個簡單強大的Javascript模板引擎,使用它可以簡化在js代碼中的html編寫,壓縮后只有9KB,非常值得在項目中使用,感興趣的小伙伴們可以參考一下2016-01-01
JavaScrip如何安全使用Payment Request API詳解
這篇文章主要為大家介紹了JavaScrip如何安全使用Payment Request API詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10

