html部分
<canvas id="canvas">浏览器不支持</canvas>
css部分
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
}
javascript部分
const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
canvas.width = document.documentElement.clientWidth
canvas.height = document.documentElement.clientHeight
window.addEventListener('resize', () => {
canvas.width = document.documentElement.clientWidth
canvas.height = document.documentElement.clientHeight
})
// 控制小球
let ballArr = [];
class Ball {
x = Math.floor(Math.random() * canvas.width) //小球生成位置,坐标是圆心
y = Math.floor(Math.random() * canvas.height) //小球生成位置,坐标是圆心
r = 10 //小球的半径
color = 'gray'
dx = (Math.floor(Math.random() * 10) - 5) || 1
dy = (Math.floor(Math.random() * 10) - 5) || 1
constructor() {
ballArr.push(this)
}
// 小球渲染
render() {
// 创建一个路径
ctx.beginPath()
// 透明度
ctx.globalAlpha = 1 //小球透明度
// 画小球
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, false)
ctx.fillStyle = this.color
ctx.fill();
}
//小球更新
update() {
this.x += this.dx; //x轴更新
// 纠正x,因为x是画布最左边到圆心距离,r是圆半径,所以左边距离不能小于圆半径,否则小球就会超出画布
if (this.x <= this.r) {
this.x = this.r;
} else if (this.x >= canvas.width - this.r) { //如果左边距离大于画布减去半径距离,那么左边x就等于画布宽度减半径
this.x = canvas.width - this.r
}
this.y += this.dy;
// 纠正y,同上,也是防止超出画布
if (this.y <= this.r) {
this.y = this.r
} else if (this.y >= canvas.height - this.r) { //如果画布y距离大于画布高度减去半径距离,那么y就等于画布高度减半径
this.y = canvas.height - this.r
}
// 碰壁返回
//左边距离加半径等于或超过画布宽度就让速度变相反数(右边反弹) 或者左边距离(画布左到圆心)减去半径(左边反弹))
if (this.x + this.r >= canvas.width || this.x - this.r <= 0) {
this.dx *= -1;
}
// 最下方反弹 上方反弹
if (this.y + this.r >= canvas.height || this.y - this.r <= 0) {
this.dy *= -1;
}
}
}
// 创建20个小球
for (let i = 0; i < 20; i++) {
new Ball()
}
let x, y;
canvas.addEventListener('mousemove', (e) => {
x = e.offsetX
y = e.offsetY
})
canvas.addEventListener('mouseout', (e) => {
console.log('鼠标移出页面');
x = null
y = null
})
// 生成线
function makeLine(distance, moveTo, lineTo) {
// 两个小球距离小于150,就画上直线。超过150不需要清除线,因为画布每次都会清空
if (distance <= 150) {
ctx.strokeStyle = "#000"
ctx.beginPath();
/*
线的透明度,根据当前已经连线的小球距离进行线的透明度设置
距离越近透明度越大,越远越小
*/
ctx.globalCompositeOperation = 'destination-over' //让线在小球下面盖住
ctx.globalAlpha = 1 - distance / 150; //这里是线透明度
ctx.moveTo(moveTo.x, moveTo.y)
ctx.lineTo(lineTo.x, lineTo.y)
ctx.closePath();
ctx.stroke();
}
}
setInterval(() => {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height)
// 小球渲染更新
ballArr.forEach(ballObj => {
ballObj.update();
ballObj.render();
});
// 画线逻辑
for (let i = 0; i < ballArr.length; i++) {
for (let j = i + 1; j < ballArr.length; j++) {
// 计算第i个小球和其他小球的距离,这里使用勾股定理。sqrt求平方根,pow是一个底数的几次方,这里是平方
let distance = Math.sqrt(Math.pow(ballArr[i].x - ballArr[j].x, 2) + Math.pow(ballArr[i].y - ballArr[j].y,
2))
// 两个小球距离小于150,就画上直线。超过150不需要清除线,因为画布每次都会清空
makeLine(distance, ballArr[i], ballArr[j])
}
// 鼠标到每个希求距离
let mouseDistance = Math.sqrt(Math.pow(ballArr[i].x - x, 2) + Math.pow(ballArr[i].y - y, 2))
makeLine(mouseDistance, ballArr[i], {
x,
y
})
}
}, 1000 / 60);
效果图
图一
![](https://img-blog.csdnimg.cn/img_convert/e5c25b576bd80d67a3441fa96800d9f3.png)
图二
![](https://img-blog.csdnimg.cn/img_convert/a209a0d04713411783ee471c512dfe8b.png)