【canvas】游离蜘蛛网效果

这个效果我曾多次在不同的博客中见到过,当时觉得很神奇,心想自己什么时候能自己做一个效果。终于在这两天,我又一次见到了这个效果,感觉自己有这个实现能力了,于是就算是完成以前的一个小小的心愿。

 一、原理分析

这种游离状态的蜘蛛网本身没有什么实现难度,就是在画布中添加自由运动的小点,然后在满足一定距离状态的小点之间添加连线,连线可以随着距离的变化而变化,以表现那种即将断裂的效果。

然后就是鼠标光标附近的小点向鼠标靠拢的效果。可以将鼠标所在位置想成一个没有运动的点,只不过它的引力范围大了一点,同时还能保证被吸附的小点被固定在它的周围一段距离。

这个效果我想了一下,有两种方式,一种是完全物理化的效果,即把所有的小球当成实际的球体,他们有质量,也就有了引力,同时随着质量变大引力也变大;另外一种就单纯单独处理鼠标点。

鉴于时间限制,我这里采用单独处理的方式。

二、代码实现

代码很简单,基本都有注释:

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