这个效果我曾多次在不同的博客中见到过,当时觉得很神奇,心想自己什么时候能自己做一个效果。终于在这两天,我又一次见到了这个效果,感觉自己有这个实现能力了,于是就算是完成以前的一个小小的心愿。
一、原理分析
这种游离状态的蜘蛛网本身没有什么实现难度,就是在画布中添加自由运动的小点,然后在满足一定距离状态的小点之间添加连线,连线可以随着距离的变化而变化,以表现那种即将断裂的效果。
然后就是鼠标光标附近的小点向鼠标靠拢的效果。可以将鼠标所在位置想成一个没有运动的点,只不过它的引力范围大了一点,同时还能保证被吸附的小点被固定在它的周围一段距离。
这个效果我想了一下,有两种方式,一种是完全物理化的效果,即把所有的小球当成实际的球体,他们有质量,也就有了引力,同时随着质量变大引力也变大;另外一种就单纯单独处理鼠标点。
鉴于时间限制,我这里采用单独处理的方式。
二、代码实现
代码很简单,基本都有注释:
for (var i = 0; i < balls.length; i++) {
var ball_1 = balls[i];
// 移动球
ball_1.x += ball_1.vx;
ball_1.y += ball_1.vy;
// 边界检查
if (ball_1.x - ball_1.r < 0) {
ball_1.x = ball_1.r;
ball_1.vx = Math.abs(ball_1.vx);
}
if (ball_1.x + ball_1.r > canvasEl.width) {
ball_1.x = canvasEl.width - ball_1.r;
ball_1.vx = -Math.abs(ball_1.vx);
}
if (ball_1.y - ball_1.r < 0) {
ball_1.y = ball_1.r;
ball_1.vy = Math.abs(ball_1.vy);
}
if (ball_1.y + ball_1.r > canvasEl.height) {
ball_1.y = canvasEl.height - ball_1.r;
ball_1.vy = -Math.abs(ball_1.vy);
}
// 与鼠标位置作用
if (mousePoint != null) {
if (getDistance(ball_1, mousePoint) < maxDistance * 2) {
// 球心连线与 x 轴正方向夹角
var radian_line = Math.atan2(mousePoint.y - ball_1.y, mousePoint.x - ball_1.x);
// 将球的位置坐标系的 x 轴旋转到与球心连线重合
var vx_1 = ball_1.vx * Math.cos(radian_line) + ball_1.vy * Math.sin(radian_line),
x_1 = ball_1.x * Math.cos(radian_line) + ball_1.y * Math.sin(radian_line),
y_1 = ball_1.y * Math.cos(radian_line) - ball_1.x * Math.sin(radian_line);
var x_2 = mousePoint.x * Math.cos(radian_line) + mousePoint.y * Math.sin(radian_line);
var nx_1 = x_1 + ((x_2 - x_1) / maxDistance) * Math.abs(vx_1);
// 复原球的坐标系
ball_1.x = nx_1 * Math.cos(-radian_line) + y_1 * Math.sin(-radian_line);
ball_1.y = y_1 * Math.cos(-radian_line) - nx_1 * Math.sin(-radian_line);
context.strokeStyle = "rgba(100,100,100," + (maxDistance * 2 - getDistance(mousePoint, ball_1)) / maxDistance / 2 + ")";
context.beginPath();
context.moveTo(ball_1.x, ball_1.y);
context.lineTo(mousePoint.x, mousePoint.y);
context.stroke();
}
}
// 距离检测
for (var j = i + 1; j < balls.length; j++) {
var ball_2 = balls[j];
// 球连线
var distance = getDistance(ball_1, ball_2);
if (distance < maxDistance) {
context.strokeStyle = "rgba(100,100,100," + (maxDistance - distance) / maxDistance + ")";
context.beginPath();
context.moveTo(ball_1.x, ball_1.y);
context.lineTo(ball_2.x, ball_2.y);
context.stroke();
}
}
// 绘制球
context.fillStyle = "red";
context.beginPath();
context.arc(ball_1.x, ball_1.y, ball_1.r, 0, 2 * Math.PI);
context.fill();
}
点击这里查看完整 DEMO。