前端canvas粒⼦动画背景(带⿏标跟随和点击散开)
⽬录
闲聊
⼀年前觉得别⼈写的贼酷贼神奇的canvas粒⼦动画背景,⼀年后⾃⼰写了⼀个更nb的,hahahaha!
好吧,其实也没啥难的,前后⼤概花了俩⼩时,只不过是最近才正⼉⼋经学了⼀下canvas,写个东西来练练⼿。
⾔归正传,这个粒⼦背景的粒⼦移动和粒⼦直接的连线以及和⿏标的连线都很简单,两个难点在于⿏标跟随和点击散开,下⾯的介绍中我将重点说⼀下这两个功能点。
看下效果
没有⿏标?截图给隐藏掉了,位置就不⽤我说了吧。
gif好糊啊= =
先贴下代码吧
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Starry</title>
</head>
<body>
<div >
<div >
<canvas id="canvas" ></canvas>  </div>
<script type="text/javascript">
const canvas = ElementById('canvas')
const ctx = Context('2d')
let width = window.innerWidth
let height = window.innerHeight
let dotsNum = 80 // 点的数量svg和canvas的区别
let radius = 1 // 圆的半径,连接线宽度的⼀半
let fillStyle = 'rgba(255,255,255,0.5)' // 点的颜⾊
let lineWidth = radius * 2
let connection = 120 // 连线最⼤距离
let followLength = 80 // ⿏标跟随距离
let dots = []
let animationFrame = null
let mouseX = null
let mouseY = null
function addCanvasSize () { // 改变画布尺⼨
width = window.innerWidth
height = window.innerHeight
canvas.width = width
canvas.height = height
ctx.clearRect(0, 0, width, height)
dots = []
if (animationFrame) window.cancelAnimationFrame(animationFrame)
initDots(dotsNum)
moveDots()
}
function mouseMove (e) {
mouseX = e.clientX
mouseY = e.clientY
}
function mouseOut (e) {
mouseX = null
mouseY = null
}
function mouseClick () {
for (const dot of dots) dot.elastic()
}
class Dot {
constructor(x, y) {
this.x = x
this.y = y
this.speedX = Math.random() * 2 - 1
this.speedY = Math.random() * 2 - 1
this.follow = false
}
draw () {
ctx.beginPath()
ctx.arc(this.x, this.y, radius, 0, 2 * Math.PI)
ctx.fill()
ctx.closePath()
}
move () {
if (this.x >= width || this.x <= 0) this.speedX = -this.speedX
if (this.y >= height || this.y <= 0) this.speedY = -this.speedY
this.x += this.speedX
this.x += this.speedX
this.y += this.speedY
if (this.speedX >= 1) this.speedX--
if (this.speedX <= -1) this.speedX++
if (this.speedY >= 1) this.speedY--
if (this.speedY <= -1) this.speedY++
this.draw()
}
correct () { // 根据⿏标的位置修正
if (!mouseX || !mouseY) return
let lengthX = mouseX - this.x
let lengthY = mouseY - this.y
const distance = Math.sqrt(lengthX ** 2 + lengthY ** 2)
if (distance <= followLength) this.follow = true
else if (this.follow === true && distance > followLength && distance <= followLength + 8) {        let proportion = followLength / distance
lengthX *= proportion
lengthY *= proportion
this.x = mouseX - lengthX
this.y = mouseY - lengthY
} else this.follow = false
}
connectMouse () { // 点与⿏标连线
if (mouseX && mouseY) {
let lengthX = mouseX - this.x
let lengthY = mouseY - this.y
const distance = Math.sqrt(lengthX ** 2 + lengthY ** 2)
if (distance <= connection) {
opacity = (1 - distance / connection) * 0.5
ctx.strokeStyle = `rgba(255,255,255,${opacity})`
ctx.beginPath()
ctx.lineTo(mouseX, mouseY);
ctx.stroke();
ctx.closePath()
}
}
}
elastic () { // ⿏标点击后的弹射
let lengthX = mouseX - this.x
let lengthY = mouseY - this.y
const distance = Math.sqrt(lengthX ** 2 + lengthY ** 2)
if (distance >= connection) return
const rate = 1 - distance / connection // 距离越⼩此值约接近1
this.speedX = 40 * rate * -lengthX / distance
this.speedY = 40 * rate * -lengthY / distance
}
}
function initDots (num) { // 初始化粒⼦
ctx.fillStyle = fillStyle
ctx.lineWidth = lineWidth
for (let i = 0; i < num; i++) {
const x = Math.floor(Math.random() * width)
const y = Math.floor(Math.random() * height)
const dot = new Dot(x, y)
dot.draw()
dots.push(dot)
}
}
function moveDots () { // 移动并建⽴点与点之间的连接线
ctx.clearRect(0, 0, width, height)
for (const dot of dots) {
}
for (let i = 0; i < dots.length; i++) {
for (let j = i; j < dots.length; j++) {
const distance = Math.sqrt((dots[i].x - dots[j].x) ** 2 + (dots[i].y - dots[j].y) ** 2)
if (distance <= connection) {
opacity = (1 - distance / connection) * 0.5
ctx.strokeStyle = `rgba(255,255,255,${opacity})`
ctx.beginPath()
ctx.lineTo(dots[j].x, dots[j].y);
ctx.stroke();
ctx.closePath()
}
}
}
animationFrame = questAnimationFrame(moveDots)
}
addCanvasSize()
initDots(dotsNum)
moveDots()
</script>
</body>
</html>
⼤概说⼀下流程
1. 初始化基础属性
2. 添加⿏标移动事件并实时更新⿏标坐标
3. 通过随机数⽣成粒⼦的坐标和横纵轴速度
4. 渲染粒⼦并将粒⼦对象保存在数组中
5.  调⽤requestAnimationFrame启动动画,使粒⼦移动起来
6. 通过横纵坐标和速度计算粒⼦位置
7. 计算与⿏标距离进⾏坐标的修正
8. 计算与⿏标距离并进⾏连线
9. 计算粒⼦直接的距离并进⾏连线
10. 添加⿏标点击事件并调⽤粒⼦的散开事件
11. 通过与⿏标的距离和相对位置进⾏计算来重新给粒⼦添加速度
12. 监听页⾯⼤⼩变化来初始化画布
下⾯让我来详细说⼀下
1、初始化基础属性
在这⾥初始化⼀些基础属性,粒⼦⼤⼩啊、颜⾊啊、数量啊叭啦叭啦的,先过过眼,下⾯都会⽤到。
const canvas = ElementById('canvas')
const ctx = Context('2d')
let width = window.innerWidth
let height = window.innerHeight
let dotsNum = 80 // 点的数量
let radius = 1 // 圆的半径,连接线宽度的⼀半
let fillStyle = 'rgba(255,255,255,0.5)' // 点的颜⾊
let lineWidth = radius * 2
let connection = 120 // 连线最⼤距离
let followLength = 80 // ⿏标跟随距离
let dots = [] // 粒⼦集合
let animationFrame = null
let mouseX = null
let mouseY = null
2、添加⿏标移动事件并实时更新⿏标坐标
在这⾥实时更新全局的⿏标坐标值,为下⾯与⿏标连线的处理做准备。
function mouseMove (e) {
mouseX = e.clientX
mouseY = e.clientY
}
function mouseOut (e) {
mouseX = null
mouseY = null
}
3、通过随机数⽣成粒⼦的坐标和横纵轴速度
这⼀步⽐较简单,在构造函数⾥通过随机数对粒⼦的横纵坐标和速度进⾏初始化,这⾥注意,速度是有正负值的,我在这把速度限制在-1到1之间,另外有个follow属性⾮常重要,在接下来我会讲到。
class Dot {
constructor(x, y) {
this.x = x
this.y = y
this.speedX = Math.random() * 2 - 1
this.speedY = Math.random() * 2 - 1
this.follow = false
}
}
4、渲染粒⼦并将粒⼦对象保存在数组中
这⼀步循环⽣成粒⼦对象并调⽤粒⼦对象的draw⽅法进⾏渲染,然后把粒⼦存⼊dots中以备后⾯使⽤。到这步完成,页⾯上就已经可以出现好多粒⼦了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。