当青训营遇上码上掘金,在「青训营 X 码上掘金」主题创作活动里我选择的主题是“遇见”,题目中说两个人相遇且相识的概率为 0.0000005,几率看起来好小,但是我们却很少因为相遇相识而感到庆幸。因为总是将大部分的精力花费在对于人际交往的揣测与不安中,盼望着每一次相遇,又在相识之前犹豫。
但是小球不会犹豫,不会不安,不会畏手畏脚。在他们的世界中,轨迹相接就值得庆祝,可以点进这个链接看看他们的相遇: 遇见 - 码上掘金 (juejin.cn)
代码实现
- 首先是实现小球的生成,实现的思路是通过
document.createElement来创建一个div,然后通过classList.add来添加类名,通过style来设置样式,最后通过appendChild来添加到页面中。对于小球生成的随机性,使用Math.random()函数为小球设置随机的颜色、位置、大小。
let ball = document.createElement("div");
ball.classList.add("ball"); //添加类名
ball.style.backgroundColor =
colors[Math.floor(Math.random() * colors.length)]; // 获取随机颜色
ball.style.left = Math.floor(Math.random() * 100) + "vw";
ball.style.top = Math.floor(Math.random() * 100) + "vh"; // 获取随机位置
ball.style.transform = "scale(" + Math.random() + 0.2 + ")"; // 获取随机大小
ball.style.width = Math.random() * 50 + 20 + "px";
ball.style.height = ball.style.width; // 获取随机宽高
balls.push(ball); // 将小球添加到数组中
document.body.appendChild(ball); // 将小球添加到页面中
const color = ball.style.backgroundColor;
ballColor.push(color); // 将小球颜色添加到数组中
-
有了这些参数之后,在随机创建小球的过程中就将其生成的信息存储在了balls数组中,同时将小球的颜色存储在了ballColor数组中,这样在后面的碰撞检测中就可以通过balls数组中的小球信息来获取小球的颜色。也是为了减少后面的计算量。
-
然后是小球的动画,通过animate函数来实现小球的动画,随机获取了每个小球的移动方向和移动速度,然后通过
requestAnimationFrame来实现动画的循环。
balls.forEach((ball, index) => {
ball.animate(
{
left: [
Math.floor(Math.random() * 100) + "vw",
Math.floor(Math.random() * 100) + "vw",
],
top: [
Math.floor(Math.random() * 100) + "vh",
Math.floor(Math.random() * 100) + "vh",
],
},
{
duration: Math.random() * 2000 + 6000,
direction: "alternate",
fill: "both",
iterations: Infinity,
}
);
});
- 为了表现出 “遇见” 这个主题,我们需要实现小球的碰撞检测,当两个小球发生碰撞时,就将两个小球的颜色改变为金色。这里的碰撞检测是通过计算两个小球的圆心距离来判断的,当两个小球的圆心距离小于两个小球的半径和时,就认为两个小球发生了碰撞。小球的半径通过之前生成小球时的宽高来计算,小球的圆心通过小球的位置和宽高来计算。
const crashed = () => {
const needChangeGold = []; // 存放需要改变颜色的小球
const points = [];
balls.forEach((ball, index) => {
ball.style.backgroundColor = ballColor[index]; // 每帧重新赋色
const Br = ball.scrollWidth / 2;
const point = {
r: Br,
x: ball.offsetLeft + Br,
y: ball.offsetTop + Br,
}; // 小球的信息
// 判断是否碰撞
points.forEach((p, i) => {
const distance = Math.sqrt(
Math.pow(point.x - p.x, 2) + Math.pow(point.y - p.y, 2)
);
if (distance < point.r + p.r) {
needChangeGold.push(index);
needChangeGold.push(i);
//console.log(needChangeGold);
}
});
points.push(point);
});
needChangeGold.forEach((index) => {
balls[index].style.backgroundColor = "#FFD700";
});
requestAnimationFrame(crashed);
};
requestAnimationFrame(crashed);
- 为了实时检测到每个小球的位置,每帧都会产生不小的计算量,整个页面的流畅度就会受到影响。我这里使用的方法时间复杂度为O(nlogn),我感觉好像还是很耗性能,但是我也没想到更好的方法了,如果有更好的方法,欢迎大家指出。