本文详解如何利用HTML5 Canvas结合物理力学原理,实现弹簧小球的动态交互效果。通过受力分析分解重力、弹力及空气阻力,利用向量合成与投影算法计算合力与加速度,最终在每一帧渲染中更新小球坐标,展示完整的物理模拟过程与代码实现。
先展示最终实现的视觉效果。
场景中包含两个小球及连接它们的弹簧线条。左侧小球位置固定,点击启动后,右侧小球在重力作用下进行自由落体运动,并受弹簧力影响产生摆动。

接下来梳理实现该效果的技术思路。
首先需要对系统进行受力分析。

通过受力分析可知,系统中主要存在三个作用力。我们需要分别计算这三个力的大小与方向,将其合成为一个总力,进而求出该总力产生的加速度。
首先是重力分析。
右侧小球在整个运动过程中始终受到竖直向下的重力作用,其计算公式为:
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() {
// 计算Δx
const 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)的夹角余弦值。将总力大小乘以对应的余弦值,得到投影到水平和垂直方向的分力大小,再分别乘以对应的单位向量,即可得到水平和垂直方向的分力向量。
// 获取力在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;
最终呈现出上述弹簧小球的交互效果。

至此,关于使用Canvas实现弹簧效果的教程介绍完毕。如需了解更多Canvas相关技术内容,请搜索本站往期文章或继续浏览后续相关文章,感谢支持。