一天上午起床,脑子里满是联动小球的模拟实验,然后灵光一闪,就想到了用canvas实现之的原理,( ╯□╰ )。然后赶紧下床开始用纸笔理清实现逻辑和定位计算,然后打开电脑coding,一气呵成就是这么嗨森。

实现的基本思路是将小球的当前坐标用小球位置数组记录。每一次位置变动都将小球的新位置重新写入这个小球位置数组,然后通过新数组中的位置坐标两两连线。注意的是canvas是一次次的绘图叠加的,所以我们需要在每一次绘制新图形前清空画布再进行绘制。除此之外基本就剩下拖拽事件的实现了。

Talk is a cheat, 还是来段代码实际点,如下:

HTML代码:

<canvas id="myCanvas" width="500" height="500"></canvas>

JavaScript代码:

// 获得Canvas元素的引用和绘图上下文
var myCanvas = document.getElementById("myCanvas");
var ctx = myCanvas.getContext("2d");
ctx.fillStyle = "rgba(200, 200, 100, .6)";

// 创建圆的构造函数
function Circle(x, y, radius) {
    this.x = x;
    this.y = y;
    this.radius = radius;
}

// 创建直线的构造函数
function Line(startPoint, endPoint, thickness) {
    this.startPoint = startPoint;
    this.endPoint = endPoint;
    this.thickness = thickness;
}

var circleRadius = 10;
var width = myCanvas.width;
var height = myCanvas.height;
var circlesCount = 5;

// 创建用于存储位置的数组
var untangleGame = {
    circle: [],
    thinLineThickness: 1,
    lines: []
};

// 绘画圆形的函数
function drawCircle(ctx, x, y, radius) {
    ctx.fillStyle = "rgba(200, 200, 100, 1)";
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fill();
}

// 绘制直线的函数
function drawLine(ctx, x1, y1, x2, y2, lineThickness) {
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.width = lineThickness;
    ctx.closePath();
    ctx.stroke();
}

function circleCreate() {
    // 随机放置几个圆
    for(var i = 0; i < circlesCount; i++) {
        var x = Math.random() * width;
        var y = Math.random() * height;
        drawCircle(ctx, x, y, circleRadius);
        untangleGame.circle.push(new Circle(x, y, circleRadius));
    }    
}
circleCreate();
// 生成圆之间的连线,并保存所有连线
function connectCircles() {
    untangleGame.lines.length = 0;
    for(var i = 0; i < untangleGame.circle.length; i++) {
        var startPoint = untangleGame.circle[i];
        for(var j = 0; j < i; j++) {
            var endPoint = untangleGame.circle[j];
            drawLine(ctx, startPoint.x, startPoint.y, endPoint.x, endPoint.y, 1);
            untangleGame.lines.push(new Line(startPoint, endPoint, untangleGame.thinLineThickness));
        }
    }
}
connectCircles();

// 添加鼠标事件监听器
// 鼠标按下事件
myCanvas.onmousedown = function(e) {
    var e = e || window.event;
    var mouseX = (e.pageX  - this.offsetLeft) || (e.clientX + document.documentElement.scrollLeft || document.body.scrollLeft);
    var mouseY = (e.pageY  - this.offsetLeft) || (e.clientY + document.documentElement.scrollTop || document.body.scrollTop);

    for(var i = 0; i < untangleGame.circle.length; i++) {
        var circleX = untangleGame.circle[i].x;
        var circleY = untangleGame.circle[i].y;
        var radius = untangleGame.circle[i].radius;
        if(Math.pow(mouseX - circleX, 2) + Math.pow(mouseY - circleY, 2) < Math.pow(radius, 2)) {
            untangleGame.targetCircle = i;
            console.log(untangleGame.targetCircle);
            break;
        }
    }
}

// 鼠标移动事件
myCanvas.onmousemove = function(e) {    
    gameloop();    
    if(untangleGame.targetCircle != undefined) {
        var e = e || window.event;
        var mouseX = (e.pageX  - this.offsetLeft) || (e.clientX + document.documentElement.scrollLeft || document.body.scrollLeft);
        var mouseY = (e.pageY  - this.offsetLeft) || (e.clientY + document.documentElement.scrollTop || document.body.scrollTop);
        var radius = untangleGame.circle[untangleGame.targetCircle].radius;
        untangleGame.circle[untangleGame.targetCircle] = new Circle(mouseX, mouseY, radius);
    }
    connectCircles();

}
myCanvas.onmouseup = function(e) {
    var e = e || window.event;
    untangleGame.targetCircle = undefined;
}

// 当鼠标移动小球时,修改当前圆和相连直线的存档,并清空画布;
function gameloop() {
    // 重绘前清空Canvas
    ctx.clearRect(0, 0, myCanvas.width, myCanvas.height);

    // 绘制所有保存的圆
    for(var i = 0; i < untangleGame.circle.length; i++) {
        var circle = untangleGame.circle[i];
        drawCircle(ctx, circle.x, circle.y, circle.radius);
    }
}

效果如下:

在线演示请戳这里~

源码下载请戳这里~

本文作者:子匠_Zijor,转载请注明出处:http://www.dengzhr.com/frontend/html/415