童年回忆,这个经典的泡泡屏保动画谁还记得?

说在前面

还记得小时候在电脑上看到过这样一个泡泡屏保动画,那时候就觉得很有趣,感觉这个动画可以看一天。现在好像没怎么看到这个屏保动画了,今天就让我们一起来回味一下童年,实现一个这个经典的泡泡屏保动画。

泡泡样式绘制

泡泡基础样式

css 复制代码
.bubble {
  position: absolute;
  border-radius: 50%;
}

高光效果

css 复制代码
.bubble::before {
  content: "";
  position: absolute;
  top: 12%;
  left: 20%;
  width: 15%;
  height: 6%;
  border-radius: 30%;
  background: linear-gradient(135deg, rgba(255,255,255,0.9) 40%, rgba(255,255,255,0.3) 80%, rgba(255,255,255,0) 95%);
  transform: rotate(332deg);
  opacity: 0.8;
}
  • 通过伪元素实现高光区域
  • 线性渐变模拟光影过渡
  • 332度旋转产生倾斜效果

阴影类

设置几个不同颜色的阴影类,随机给泡泡加上

css 复制代码
.bubble-shadow1 {
  box-shadow:
    inset 0 0 10px rgba(250, 188, 123, 1),
    0 0 5px rgba(250, 188, 123, 0.8);
}
.bubble-shadow2 {
  box-shadow:
    inset 0 0 10px rgba(21, 165, 237, 1),
    0 0 5px rgba(21, 165, 237, 0.8);
}
.bubble-shadow3 {
  box-shadow:
    inset 0 0 10px rgba(123, 24, 125, 1),
    0 0 5px rgba(123, 24, 125, 0.8);
}
.bubble-shadow4 {
  box-shadow:
    inset 0 0 10px rgba(146, 241, 2, 1),
    0 0 5px rgba(146, 241, 2, 0.8);
}
.bubble-shadow5 {
  box-shadow:
    inset 0 0 10px rgba(245, 58, 58, 1),
    0 0 5px rgba(245, 58, 58, 0.8);
}

泡泡动画实现

初始化参数

javascript 复制代码
const list = [
  {
    r: 200,
    left: 0,
    top: 0,
    leftStep: 1,
    topStep: 2,
    flag: true,
  },
  {
    r: 120,
    left: 0,
    top: 0,
    leftStep: 2.5,
    topStep: 2.5,
    flag: true,
  }
  ...
];
  • r:泡泡的半径
  • left:泡泡的初始x坐标
  • top:泡泡的初始y坐标
  • leftStep:泡泡在x轴方向的初始速度
  • topStep:泡泡在y轴方向的初始速度
  • flag:判断泡泡是否与其它泡泡发生重叠

根据配置列表生成泡泡

javascript 复制代码
function initBubble() {
  list.forEach((item, index) => {
    const bubble = document.createElement("div");
    const num = getRandom(1, 5);
    bubble.id = "bubble-" + index;
    bubble.classList.add("bubble");
    bubble.classList.add("bubble-shadow" + num);
    bubble.style.width = item.r + "px";
    bubble.style.height = item.r + "px";
    bubble.style.left = item.left + "px";
    bubble.style.top = item.top + "px";
    document.body.appendChild(bubble);
  });
}

让泡泡动起来

1、泡泡运动控制
javascript 复制代码
item.left += item.leftStep;
item.top += item.topStep;
bubble.style.left = item.left + "px";
bubble.style.top = item.top + "px";
  • 位置计算:每个泡泡的位置按步长(leftStep/topStep)持续更新
  • DOM 更新:将计算后的位置同步到页面元素
2、边界反弹逻辑
  • 位置限制:确保泡泡始终在视口范围内
javascript 复制代码
item.left = Math.max(0, Math.min(item.left, window.innerWidth - item.r));
item.top = Math.max(0, Math.min(item.top, window.innerHeight - item.r));
  • 触壁反弹:触碰到边界的时候将移动方向置反
javascript 复制代码
if (item.left >= window.innerWidth - item.r || item.left <= 0) {
  item.leftStep *= -1;
}
if (item.top >= window.innerHeight - item.r || item.top <= 0) {
  item.topStep *= -1;
}
3、泡泡碰撞检测

比较圆心距离和两个半径和大小关系即可。

  • 圆心距离 == 半径和

  • 圆心距离 < 半径和

  • 圆心距离 > 半径和

javascript 复制代码
function isColliding(bubble1, bubble2) {
  const dx =
    bubble1.left + bubble1.r / 2 - (bubble2.left + bubble2.r / 2);
  const dy = bubble1.top + bubble1.r / 2 - (bubble2.top + bubble2.r / 2);
  const distance = Math.sqrt(dx * dx + dy * dy);
  return distance <= (bubble1.r + bubble2.r) / 2;
}
4、弹性碰撞计算
(1)原始数据计算

弹性碰撞是指在碰撞过程中,两个物体的总动能和总动量都保持不变。

  • 质量计算 :用泡泡的宽度乘以高度来近似表示泡泡的质量,即 m1m2。在实际物理场景中,质量与物体的密度和体积有关,但这里简化处理。
javascript 复制代码
const m1 = bubble1.r * bubble1.r * Math.PI;
const m2 = bubble2.r * bubble2.r* Math.PI;
  • 速度获取v1xv1y 分别是泡泡1在水平和垂直方向上的速度分量;v2xv2y 分别是泡泡2在水平和垂直方向上的速度分量。
javascript 复制代码
const v1x = bubble1.leftStep;
const v1y = bubble1.topStep;
const v2x = bubble2.leftStep;
const v2y = bubble2.topStep;
  • 位置获取x1y1 是泡泡1的圆心坐标;x2y2 是泡泡2的圆心坐标。
javascript 复制代码
const x1 = bubble1.left + bubble1.r / 2;
const y1 = bubble1.top + bubble1.r / 2;
const x2 = bubble2.left + bubble2.r / 2;
const y2 = bubble2.top + bubble2.r / 2;
(2)计算两泡泡之间的距离和法向量
javascript 复制代码
const dx = x2 - x1;
const dy = y2 - y1;
const d = Math.sqrt(dx * dx + dy * dy);
const nx = dx / d;
const ny = dy / d;
  • 距离计算
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> d x = x 2 − x 1 dx = x_2 - x_1 </math>dx=x2−x1,表示两泡泡圆心在 <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x 轴方向上的距离。
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> d y = y 2 − y 1 dy = y_2 - y_1 </math>dy=y2−y1,表示两泡泡圆心在 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y 轴方向上的距离。
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> d = d x 2 + d y 2 d=\sqrt{dx^2 + dy^2} </math>d=dx2+dy2 ,表示两泡泡圆心之间的实际距离。
  • 法向量计算
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> n x = d x d n_x=\frac{dx}{d} </math>nx=ddx, <math xmlns="http://www.w3.org/1998/Math/MathML"> n y = d y d n_y=\frac{dy}{d} </math>ny=ddy, <math xmlns="http://www.w3.org/1998/Math/MathML"> n x n_x </math>nx 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> n y n_y </math>ny 是两泡泡圆心连线方向的单位法向量的分量。
(3)计算泡泡在法向和切向的速度分量
javascript 复制代码
const v1n = v1x * nx + v1y * ny;
const v1t = v1x * -ny + v1y * nx;
const v2n = v2x * nx + v2y * ny;
const v2t = v2x * -ny + v2y * nx;
  • 法向速度分量
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 1 n = v 1 x n x + v 1 y n y v_{1n}=v_{1x}n_x + v_{1y}n_y </math>v1n=v1xnx+v1yny,这是将泡泡1的速度向量投影到法向量方向上得到的法向速度分量。
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 2 n = v 2 x n x + v 2 y n y v_{2n}=v_{2x}n_x + v_{2y}n_y </math>v2n=v2xnx+v2yny,同理是泡泡2的法向速度分量。
  • 切向速度分量
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 1 t = − v 1 x n y + v 1 y n x v_{1t}=-v_{1x}n_y + v_{1y}n_x </math>v1t=−v1xny+v1ynx,这是将泡泡1的速度向量投影到与法向量垂直的切向方向上得到的切向速度分量。
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 2 t = − v 2 x n y + v 2 y n x v_{2t}=-v_{2x}n_y + v_{2y}n_x </math>v2t=−v2xny+v2ynx,同理是泡泡2的切向速度分量。
(4)根据弹性碰撞公式计算碰撞后的法向速度分量
javascript 复制代码
const v1nFinal = (v1n * (m1 - m2) + 2 * m2 * v2n) / (m1 + m2);
const v2nFinal = (v2n * (m2 - m1) + 2 * m1 * v1n) / (m1 + m2);

这两个公式是基于弹性碰撞的动量守恒和动能守恒定律推导出来的。对于一维弹性碰撞,设两个物体的质量分别为 <math xmlns="http://www.w3.org/1998/Math/MathML"> m 1 m_1 </math>m1 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> m 2 m_2 </math>m2,碰撞前的速度分别为 <math xmlns="http://www.w3.org/1998/Math/MathML"> u 1 u_1 </math>u1 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> u 2 u_2 </math>u2,碰撞后的速度分别为 <math xmlns="http://www.w3.org/1998/Math/MathML"> v 1 v_1 </math>v1 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> v 2 v_2 </math>v2,则有:

  • 动量守恒: <math xmlns="http://www.w3.org/1998/Math/MathML"> m 1 u 1 + m 2 u 2 = m 1 v 1 + m 2 v 2 m_1u_1 + m_2u_2 = m_1v_1 + m_2v_2 </math>m1u1+m2u2=m1v1+m2v2
  • 动能守恒: <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 2 m 1 u 1 2 + 1 2 m 2 u 2 2 = 1 2 m 1 v 1 2 + 1 2 m 2 v 2 2 \frac{1}{2}m_1u_1^2+\frac{1}{2}m_2u_2^2=\frac{1}{2}m_1v_1^2+\frac{1}{2}m_2v_2^2 </math>21m1u12+21m2u22=21m1v12+21m2v22

联立这两个方程求解可得:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 1 = ( m 1 − m 2 ) u 1 + 2 m 2 u 2 m 1 + m 2 v_1=\frac{(m_1 - m_2)u_1 + 2m_2u_2}{m_1 + m_2} </math>v1=m1+m2(m1−m2)u1+2m2u2
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 2 = ( m 2 − m 1 ) u 2 + 2 m 1 u 1 m 1 + m 2 v_2=\frac{(m_2 - m_1)u_2 + 2m_1u_1}{m_1 + m_2} </math>v2=m1+m2(m2−m1)u2+2m1u1

这里将上述公式应用到法向速度分量上, <math xmlns="http://www.w3.org/1998/Math/MathML"> v 1 n F i n a l v_{1nFinal} </math>v1nFinal 是泡泡1碰撞后的法向速度分量, <math xmlns="http://www.w3.org/1998/Math/MathML"> v 2 n F i n a l v_{2nFinal} </math>v2nFinal 是泡泡2碰撞后的法向速度分量。

(5)计算碰撞后的水平和垂直速度分量
javascript 复制代码
const v1xFinal = v1nFinal * nx - v1t * ny;
const v1yFinal = v1nFinal * ny + v1t * nx;
const v2xFinal = v2nFinal * nx - v2t * ny;
const v2yFinal = v2nFinal * ny + v2t * nx;

这是将碰撞后的法向速度分量和切向速度分量重新组合成水平和垂直方向的速度分量。具体来说,是通过向量的合成来实现的。

  • 对于泡泡 1:
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 1 x F i n a l = v 1 n F i n a l n x − v 1 t n y v_{1xFinal}=v_{1nFinal}n_x - v_{1t}n_y </math>v1xFinal=v1nFinalnx−v1tny
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 1 y F i n a l = v 1 n F i n a l n y + v 1 t n x v_{1yFinal}=v_{1nFinal}n_y + v_{1t}n_x </math>v1yFinal=v1nFinalny+v1tnx
  • 对于泡泡 2:
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 2 x F i n a l = v 2 n F i n a l n x − v 2 t n y v_{2xFinal}=v_{2nFinal}n_x - v_{2t}n_y </math>v2xFinal=v2nFinalnx−v2tny
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> v 2 y F i n a l = v 2 n F i n a l n y + v 2 t n x v_{2yFinal}=v_{2nFinal}n_y + v_{2t}n_x </math>v2yFinal=v2nFinalny+v2tnx
(6)更新泡泡的速度
javascript 复制代码
if (!bubble1.flag) {
  bubble1.leftStep = v1xFinal;
  bubble1.topStep = v1yFinal;
}
if (!bubble2.flag) {
  bubble2.leftStep = v2xFinal;
  bubble2.topStep = v2yFinal;
}

这里使用 flag 标志用于判断当前泡泡是否有与其它泡泡发生重叠的情况,如果有重叠,就先不处理该跑跑的弹性碰撞。反之则更新泡泡在水平和垂直方向上的速度分量,即 leftSteptopStep

通过简单的物理计算模拟了两个泡泡之间的弹性碰撞,并根据计算结果更新它们的速度,从而粗略地实现了碰撞后的反弹效果。

源码

组件源码已上传到gitee ,有兴趣的也可以到这里看看:gitee.com/zheng_yongt...


  • 🌟觉得有帮助的可以点个star~

  • 🖊有什么问题或错误可以指出,欢迎pr~

  • 📬有什么想要实现的组件或想法可以联系我~


公众号

关注公众号『 前端也能这么有趣 』,获取更多有趣内容。

发送 加群 还可以加入群聊,一起来学习(摸鱼)吧~

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

相关推荐
未脱发程序员1 小时前
【前端】每日一道面试题3:如何实现一个基于CSS Grid的12列自适应布局?
前端·css
三天不学习1 小时前
Visual Studio Code 前端项目开发规范合集【推荐插件】
前端·ide·vscode
爱分享的程序猿-Clark1 小时前
【前端分享】CSS实现3种翻页效果类型,附源码!
前端·css
Code哈哈笑2 小时前
【图书管理系统】深度讲解:图书列表展示的后端实现、高内聚低耦合的应用、前端代码讲解
java·前端·数据库·spring boot·后端
无名之逆2 小时前
Hyperlane: Unleash the Power of Rust for High-Performance Web Services
java·开发语言·前端·后端·http·rust·web
数据潜水员2 小时前
`待办事项css样式
前端·css·css3
_处女座程序员的日常2 小时前
css媒体查询及css变量
前端·css·媒体
GanGuaGua4 小时前
CSS:盒子模型
开发语言·前端·css·html
GalenWu10 小时前
对象转换为 JSON 字符串(或反向解析)
前端·javascript·微信小程序·json
GUIQU.10 小时前
【Vue】微前端架构与Vue(qiankun、Micro-App)
前端·vue.js·架构