先上效果
兩個小球之間有一根彈簧,這里有一條線表示,其中左球固定,在點擊開始后,右球開始做自由落體
思路
先做受力分析
經過受力分析
可以發現,整個系統一共有三個力
在起作用,我們分別把他們求出來并合成為一個力
,然后這個力
的加速度
即可
重力
右邊小球全程受到方向向下的重力
,其公式為
G = m * g
彈力
右邊小球全程受到從自身中心指向左邊小球中心方向的的彈力
,其公式為
// 胡克定律
F = -△x * k
空氣阻力
可以簡化成和速度方向
相反,乘一個小于1的阻力系數
的力
// dragCoefficient值越大說明阻力越大
F = -v * dragCoefficient
我們先創建重力
的單位向量代表重力
的方向,因為重力
永遠是垂直向下的所以向量
其單位向量是(0, 1),然后給定一個重力
大小,根據重力
公式,質量乘重力加速度
即可,這里質量
我給了 10,重力加速度
給 9.8,代碼如下
// 獲取重力單位向量
function getGravityNorVector() {return {x: 0,y: 1,};
}// 獲取重力大小
function getGravitySize() {return m * g;
}
然后創建彈力
的單位向量
表示彈力
的方向,其方向永遠是右球的球心指向左球的球心,其大小根據胡克定律
給到,即形變距離
乘以 彈簧彈性系數
// 獲取彈力單位向量
function getElasticityNorVector() {// 左球球心坐標減右球球心坐標,然后做向量歸一化return normalized(createVector(originPosition, targetPosition));
}// 獲取彈力大小
function getElasticitySize() {// 計算Δxconst offsetX =length(createVector(originPosition, targetPosition)) - originElasticitySize;// 胡克定律,只要大小,所以取絕對值return Math.abs(-offsetX * K);
}
然后我們計算空氣阻力
,其方向和速度方向相反,阻力系數
我給了個 0.1,由于一開始初速度為 0,所以阻力為 0,只用等到動起來有速度的時候才會產生阻力
// 空氣阻力,dragCoefficient為阻力系數,輸出是一個向量
const other = {x: -vx * dragCoefficient,y: -vy * dragCoefficient,
};
現在我們所有力的大小和方向都求出來了,然后根據向量平行四邊形法則
,先合成彈力
和重力
// 單位向量 * 向量長度可以得到矢量力,輸出是一個向量
const elasticityAndGravity = add(mul(getElasticitySize(), getElasticityNorVector()),mul(getGravitySize(), getGravityNorVector())
);
然后再和空氣阻力
合成最終的力
// 所有力的合成,輸出是一個向量
const total = add(elasticityAndGravity, other);
然后再把合成后的力分解成水平和垂直的兩個分力
,這里用到向量投影
算法就能實現,即可以先通過向量點積
的形式,求出與水平單位向量
(1,0)和垂直單位向量
(0,1)的余弦值
,然后用合成力
的大小乘以這個余弦值
得到投影
到水平方向和垂直方向的大小,再用這個大小分別乘以水平單位向量
(1,0)和垂直單位向量
(0,1)就能得到水平和垂直的兩個分力向量
了
// 獲取力在x軸和y軸分量向量
function getXYAxisVector(fNorVector, fSize) {const yAxisNorVector = createVector({x: targetPosition.x,y: targetPosition.y - 1,},targetPosition);const xAxisNorVector = createVector({x: targetPosition.x - 1,y: targetPosition.y,},targetPosition);// 分別點擊求向量在水平單位向量和垂直單位向量的余弦值const cosYAxis = dot(yAxisNorVector, fNorVector);const cosXAxis = dot(xAxisNorVector, fNorVector);return {yVector: mul(fSize * cosYAxis, yAxisNorVector),xVector: mul(fSize * cosXAxis, xAxisNorVector),};
}// 獲取水平和垂直分力向量
const result = getXYAxisVector(normalized(total), length(total));
然后根據公式
F = m * a;
把得到的兩個分力
分別除以質量 m
,目前 m
給的是 10,得到 x 和 y 方向的加速度
// 獲取水平和垂直方向加速度
const xAcceleration = result.xVector.x / m;
const yAcceleration = result.yVector.y / m;
然后再每一幀渲染的時候,把加速度
加到速度
上,右球的坐標
加上速度
,即可以渲染出效果,這里除以100是防止速度太快
vx += xAcceleration;
vy += yAcceleration;
targetPosition.x += vx / 100;
targetPosition.y += vy / 100;
就能得到最終這個效果了
源碼
可以關注下我的公眾號,對應的文章有源碼,還有其他有趣的干貨文章哦~