canvas实现粒子背景特效思路总结
canvas实现粒子背景特效思路总结
效果

源码
html
点击查看代码
复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>粒子背景</title>
<style>
canvas {
margin: 50px auto;
display: block;
box-shadow: 0 0 10px rgb(0 0 0 / 50%);
}
</style>
</head>
<body>
<canvas width="800" height="500"></canvas>
<script src="./js/粒子背景.js"></script>
<script>
// 粒子集合
let particles = []
// 粒子数量
const particleCount = 150
// 鼠标位置对象
let mouseOffset = null
// 粒子画线的最小距离
const minDistance = 100
// 边界碰撞检测
const collideDetect = true
// 是否有鼠标交互
const hasInteractMouse = true
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
createParticles()
draw()
handleCanvasEvent()
function draw() {
// 清空画布
canvas.width = canvas.width
drawParticles(ctx)
ctx.lineWidth = 0.1
drawLines(ctx)
requestAnimationFrame(draw)
}
// 创建粒子对象
function createParticles() {
for (let index = 0; index < particleCount; index++) {
particles.push(
new Particle(
getRandomRange(0, canvas.width, canvas.height),
getRandomRange(0, canvas.height)
)
)
}
}
// 将粒子对象画到画布上
function drawParticles(ctx) {
ctx.beginPath()
for (let index = 0; index < particles.length; index++) {
const particle = particles[index]
particle.draw(ctx)
// 没有开启边界碰撞检测,越界的粒子,更新位置并保证其新位置不越界
if (!collideDetect) {
if (
particle.x > canvas.width ||
particle.x < 0 ||
particle.y > canvas.height ||
particle.y < 0
) {
// 粒子直径
const diameter = particle.radius * 2
particle.updateCoordinate(
getRandomRange(diameter, canvas.width - diameter),
getRandomRange(diameter, canvas.height - diameter)
)
}
} else {
handleCollide(particle)
}
particle.x += particle.speedX
particle.y += particle.speedY
}
ctx.fill()
}
// 绘制粒子连线
function drawLines(ctx) {
ctx.beginPath()
let arr = [...particles]
mouseOffset && (arr = [mouseOffset].concat(arr))
for (let index = 0; index < particles.length; index++) {
const particle = particles[index]
for (let j = arr.length - 1; j >= 0; j--) {
const particle2 = arr[j]
if (particle === particle2) {
continue
}
const distance = calDistance(
particle.x,
particle2.x,
particle.y,
particle2.y
)
if (distance < minDistance) {
// 如果是鼠标,则让粒子向鼠标的位置移动,距离-10 保证粒子与鼠标之间的最小间距
if (particle2 === mouseOffset && distance > minDistance - 10) {
const xc = particle.x - particle2.x
const yc = particle.y - particle2.y
// 0.03 向鼠标坐标移动的速率
particle.x -= xc * 0.03
particle.y -= yc * 0.03
}
ctx.moveTo(particle.x, particle.y)
ctx.lineTo(particle2.x, particle2.y)
}
}
// 去掉重复比较
arr.splice(arr.indexOf(particle), 1)
}
ctx.stroke()
}
// 粒子边界碰撞检测
function handleCollide(particle) {
if (
(particle.speedX && particle.x + particle.radius > canvas.width) ||
(particle.speedX < 0 && particle.x < particle.radius)
) {
particle.speedX *= -1
}
if (
(particle.speedY > 0 &&
particle.y + particle.radius >= canvas.height) ||
(particle.speedY < 0 && particle.y <= particle.radius)
) {
particle.speedY *= -1
}
}
// 处理canvas的事件
function handleCanvasEvent() {
if (hasInteractMouse) {
canvas.onmousemove = function (e) {
if (!mouseOffset) {
mouseOffset = {}
}
mouseOffset.x = e.offsetX
mouseOffset.y = e.offsetY
}
}
}
function calDistance(x1, x2, y1, y2) {
return Math.hypot(x1 - x2, y1 - y2)
}
function getRandomRange(min, max) {
return Math.random() * (max - min) + min
}
</script>
</body>
</html>
粒子背景.js
点击查看代码
class Particle {
constructor(x, y) {
this.x = x
this.y = y
this.radius = 0.5
this.speedX = this._getRandomRange(-1, 1)
this.speedY = this._getRandomRange(-1, 1)
}
draw(ctx) {
ctx.moveTo(this.x, this.y)
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
}
updateCoordinate(x, y) {
this.x = x
this.y = y
return this
}
_getRandomRange(min, max) {
return Math.random() * (max - min) + min
}
}
主要思路
现象
- 粒子之间、鼠标与粒子之间,小于一定的值会连线
- 粒子的移动是随机的
- 粒子向鼠标移动时,有一个加速的过程
思路梳理
-
抽象圆形粒子类
- 坐标,x,y。由外部传入
- 移速,x,y。自己内部随机定义
- 半径。内部定义
-
准备粒子,画布上一直只有定量的粒子
- 使用一个数组,承载创建的定量的粒子
-
画布有鼠标移动事件的监听
- 储存鼠标的位置,用来判断粒子与鼠标位置间是否应该连线
-
批量绘制粒子
- 绘制粒子时对粒子进行边界碰撞检测。或者到边界后更新粒子的位置(确保其不越界)
-
批量绘制粒子间、鼠标和粒子间的连线
-
如果粒子间的球心距离小于限定的连线距离,则在这两个粒子中间连线
-
如果粒子和鼠标的距离小于限定的连线距离,则在鼠标和粒子中间连线
-
连线前,如果粒子与鼠标位置间的距离小于一定间距,粒子向鼠标位置移动(有一个粒子围绕着鼠标的效果)
-
移动的距离,有正、负。用鼠标的坐标和粒子的坐标做对应方向上的加减运算,再乘以一个值(表示移动速率,体现在粒子向鼠标移动的效果上)
-
计算示例实现鼠标交互的重点
-
// 如果粒子本就在鼠标附近,不用再做移动(一定间距的限制,有一个粒子围绕鼠标但不挨着鼠标的效果) if (粒子与鼠标间的距离 < 连线限定距离 && 粒子与鼠标间的距离 > 一定间距) { // xDistance 正负不定 const xDistance = 粒子坐标x - 鼠标坐标.x // yDistance 正负不定 const yDistance = 粒子坐标y - 鼠标坐标.y const rate = 0.03 // 无论xDistance、yDistance正负如何,这里都要保证是粒子在向鼠标移动 粒子坐标.x -= xDistance * rate 粒子坐标.y -= yDistance * rate }
-
-
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异