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