前言
🍊缘由
沉浸式体验:感受小球碰撞的震撼!
大家好,我是JavaDog程序狗。
今天给大家整点好玩的——用纯前端实现圆形弹球无限分裂效果,看看你的电脑能否抗住!
正文
🎯主要目标
1. 搭建基础HTML结构
2. 设置基本样式
3. JavaScript实现步骤
4. 完整HTML代码
🍪目标讲解
一. 搭建小球分裂HTML结构
首先,得画个HTML容器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小球碰撞生成器</title>
<style>
/* 样式部分 */
</style>
</head>
<body>
<div id="container">
<div id="circle"></div>
<div id="counter">小球数量: 0</div>
</div>
<script>
// JavaScript代码
</script>
</body>
</html>
二. 设置基本样式
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
overflow: hidden;
}
#container {
position: relative;
width: 500px;
height: 500px;
}
#circle {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border: 2px solid #333;
box-sizing: border-box;
background-color: #fff;
}
.ball {
position: absolute;
border-radius: 50%;
background-color: #3498db;
transform: translate(-50%, -50%);
}
#counter {
position: absolute;
top: 20px;
left: -56px;
font-size: 18px;
color: #333;
background-color: rgba(255, 255, 255, 0.7);
padding: 5px 10px;
border-radius: 5px;
}
三. JavaScript实现步骤拆解
- 3.1 初始化变量和元素
const container = document.getElementById('container');
const circle = document.getElementById('circle');
const counter = document.getElementById('counter');
const circleRadius = circle.offsetWidth / 2;
const ballRadius = 5;
const maxBalls = 3000;
let ballCount = 0;
const balls = [];
- 3.2 创建初始小球
function createBall(x, y) {
if (ballCount >= maxBalls) return;
// 创建DOM元素
const ballElement = document.createElement('div');
ballElement.className = 'ball';
ballElement.style.width = `${ballRadius * 2}px`;
ballElement.style.height = `${ballRadius * 2}px`;
container.appendChild(ballElement);
// 随机速度方向
const angle = Math.random() * Math.PI * 2;
const speed = 2;
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed;
// 存储小球数据
const ball = {
x,
y,
vx,
vy,
element: ballElement
};
balls.push(ball);
ballCount++;
counter.textContent = `小球数量: ${ballCount}`;
// 如果是第一个小球,开始动画
if (balls.length === 1) {
animate();
}
}
// 创建初始小球
createBall(circleRadius, circleRadius);
- 3.3 实现动画循环
function animate() {
if (ballCount >= maxBalls) return;
for (let i = 0; i < balls.length; i++) {
const ball = balls[i];
// 移动小球
ball.x += ball.vx;
ball.y += ball.vy;
// 检查碰撞
const distanceFromCenter = Math.sqrt(
Math.pow(ball.x - circleRadius, 2) +
Math.pow(ball.y - circleRadius, 2)
);
// 碰撞处理
if (distanceFromCenter + ballRadius >= circleRadius) {
// 反弹逻辑
const angle = Math.atan2(ball.y - circleRadius, ball.x - circleRadius);
const normalX = Math.cos(angle);
const normalY = Math.sin(angle);
const dotProduct = ball.vx * normalX + ball.vy * normalY;
ball.vx = ball.vx - 2 * dotProduct * normalX;
ball.vy = ball.vy - 2 * dotProduct * normalY;
// 防止小球卡在边缘
ball.x = circleRadius + (circleRadius - ballRadius - 1) * Math.cos(angle);
ball.y = circleRadius + (circleRadius - ballRadius - 1) * Math.sin(angle);
// 创建新小球
if (ballCount < maxBalls) {
createBall(ball.x, ball.y);
}
}
// 更新DOM位置
ball.element.style.left = `${ball.x}px`;
ball.element.style.top = `${ball.y}px`;
}
requestAnimationFrame(animate);
}
- 3.4 内存管理机制
// 定期清理异常小球
setInterval(() => {
for (let i = balls.length - 1; i >= 0; i--) {
const ball = balls[i];
const distanceFromCenter = Math.sqrt(
Math.pow(ball.x - circleRadius, 2) +
Math.pow(ball.y - circleRadius, 2)
);
// 移除超出边界的小球
if (distanceFromCenter > circleRadius * 1.5) {
ball.element.remove();
balls.splice(i, 1);
ballCount--;
counter.textContent = `小球数量: ${ballCount}`;
}
}
}, 5000);
四. 完整步骤及全部HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小球碰撞生成器</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
overflow: hidden;
}
#container {
position: relative;
width: 500px;
height: 500px;
}
#circle {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border: 2px solid #333;
box-sizing: border-box;
background-color: #fff;
}
.ball {
position: absolute;
border-radius: 50%;
background-color: #3498db;
transform: translate(-50%, -50%);
}
#counter {
position: absolute;
top: 20px;
left: -56px;
font-size: 18px;
color: #333;
background-color: rgba(255, 255, 255, 0.7);
padding: 5px 10px;
border-radius: 5px;
}
</style>
</head>
<body>
<div id="container">
<div id="circle"></div>
<div id="counter">小球数量: 0</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('container');
const circle = document.getElementById('circle');
const counter = document.getElementById('counter');
const circleRadius = circle.offsetWidth / 2;
const ballRadius = 5;
const maxBalls = 3000;
let ballCount = 0;
// 存储所有小球的数组
const balls = [];
// 创建初始小球
createBall(circleRadius, circleRadius);
// 动画循环
function animate() {
if (ballCount >= maxBalls) {
return; // 达到最大数量后停止
}
// 更新所有小球位置
for (let i = 0; i < balls.length; i++) {
const ball = balls[i];
// 移动小球
ball.x += ball.vx;
ball.y += ball.vy;
// 检查碰撞
const distanceFromCenter = Math.sqrt(
Math.pow(ball.x - circleRadius, 2) +
Math.pow(ball.y - circleRadius, 2)
);
// 如果碰到边缘
if (distanceFromCenter + ballRadius >= circleRadius) {
// 反弹
const angle = Math.atan2(ball.y - circleRadius, ball.x - circleRadius);
// 计算反弹后的方向
const normalX = Math.cos(angle);
const normalY = Math.sin(angle);
const dotProduct = ball.vx * normalX + ball.vy * normalY;
ball.vx = ball.vx - 2 * dotProduct * normalX;
ball.vy = ball.vy - 2 * dotProduct * normalY;
// 确保小球不会卡在边缘
ball.x = circleRadius + (circleRadius - ballRadius - 1) * Math.cos(angle);
ball.y = circleRadius + (circleRadius - ballRadius - 1) * Math.sin(angle);
// 创建新小球
if (ballCount < maxBalls) {
createBall(ball.x, ball.y);
}
}
// 更新DOM元素位置
ball.element.style.left = `${ball.x}px`;
ball.element.style.top = `${ball.y}px`;
}
requestAnimationFrame(animate);
}
// 创建新小球
function createBall(x, y) {
if (ballCount >= maxBalls) return;
const ballElement = document.createElement('div');
ballElement.className = 'ball';
ballElement.style.width = `${ballRadius * 2}px`;
ballElement.style.height = `${ballRadius * 2}px`;
container.appendChild(ballElement);
// 随机速度方向
const angle = Math.random() * Math.PI * 2;
const speed = 2;
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed;
const ball = {
x,
y,
vx,
vy,
element: ballElement
};
balls.push(ball);
ballCount++;
counter.textContent = `小球数量: ${ballCount}`;
// 如果这是第一个小球,开始动画
if (balls.length === 1) {
animate();
}
}
// 内存管理:定期清理超出边界的小球(虽然理论上不应该发生)
setInterval(() => {
for (let i = balls.length - 1; i >= 0; i--) {
const ball = balls[i];
const distanceFromCenter = Math.sqrt(
Math.pow(ball.x - circleRadius, 2) +
Math.pow(ball.y - circleRadius, 2)
);
// 如果小球超出边界很远(异常情况),移除它
if (distanceFromCenter > circleRadius * 1.5) {
ball.element.remove();
balls.splice(i, 1);
ballCount--;
counter.textContent = `小球数量: ${ballCount}`;
}
}
}, 5000);
});
</script>
</body>
</html>
总结
这篇文章介绍了如何用纯前端技术实现圆形弹球无限分裂效果。
从HTML结构搭建开始,创建了一个圆形容器和计数器;然后通过CSS设置基本样式,包括圆形边界和小球样式;JavaScript部分实现了小球创建、碰撞检测、反弹逻辑和无限分裂效果,并添加了内存管理机制防止性能问题。
最终效果是小球在圆形边界内不断碰撞分裂,数量指数级增长,直到达到上限3000个。
文章提供了完整的代码实现,适合前端开发者学习动画原理和性能优化技巧。
🍈猜你想问
如何与博主联系进行探讨
关注公众号【JavaDog程序狗】
公众号回复【入群】或者【加入】,便可成为【程序员学习交流摸鱼群】的一员,问题随便问,牛逼随便吹,目前群内已有超过380+个小伙伴啦!!!
2. 踩踩博主博客
里面有博主的私密联系方式呦 !,大家可以在里面留言,随意发挥,有问必答😘
🍯猜你喜欢
文章推荐
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
【项目实战】SpringBoot+uniapp+uview2打造H5+小程序+APP入门学习的聊天小项目
【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序
【模块分层】还不会SpringBoot项目模块分层?来这手把手教你!