如何将移动限制在圆形区域内,涉及IT技术。

5
这可能更多是几何相关的问题,但我试图将控制器限制在圆形区域内。我知道我必须触及Math.sin()和Math.cos()方法,但到目前为止我的尝试都无功而返。

enter image description here

这里是 jsfiddle: 到目前为止,我已经能够将其限制在一个不可见的正方形中。http://jsfiddle.net/maGVK/


你需要确保 x^2 + y^2 <= r^2,其中 r 是圆的半径。对于这样简单的任务,无需使用三角函数。 - Thomas
我已经更新了你的 fiddle,链接为:http://jsfiddle.net/vJHZz/35/ - 我不是一个数学天才,所以我尝试了一种暴力方法来寻找有效的x/y组合。它还没有完成,但是时间已经不够了。我希望你可以把它当作跳板来解决你的问题 - 目标是在循环中去掉安全网(break子句) - 祝编码愉快;) - Martin Jespersen
4个回答

9

所以最终我在大家的帮助下完成了这个项目。

var pointerEl = document.getElementById("pointer");
var canvasEl = document.getElementById("canvas");
var canvas = {
    width: canvasEl.offsetWidth,
    height: canvasEl.offsetHeight,
    top: canvasEl.offsetTop,
    left: canvasEl.offsetLeft
};
canvas.center = [canvas.left + canvas.width / 2, canvas.top + canvas.height / 2];
canvas.radius = canvas.width / 2;


window.onmousemove = function(e) {
    var result = limit(e.x, e.y);
        pointer.style.left = result.x + "px";
        pointer.style.top = result.y + "px";
}

function limit(x, y) {
    var dist = distance([x, y], canvas.center);
    if (dist <= canvas.radius) {
        return {x: x, y: y};
    } 
    else {
        x = x - canvas.center[0];
        y = y - canvas.center[1];
        var radians = Math.atan2(y, x)
           return {
               x: Math.cos(radians) * canvas.radius + canvas.center[0],
               y: Math.sin(radians) * canvas.radius + canvas.center[1]
           }
        } 
    }

function distance(dot1, dot2) {
    var x1 = dot1[0],
        y1 = dot1[1],
        x2 = dot2[0],
        y2 = dot2[1];
    return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}

你可以在这里查看结果:

http://jsfiddle.net/7Asn6/


1
var pointerEl = document.getElementById("pointer");
var canvasEl = document.getElementById("canvas");
var canvas = {
    width: canvasEl.offsetWidth,
    height: canvasEl.offsetHeight,
    top: canvasEl.offsetTop,
    left: canvasEl.offsetLeft
};
canvas.center = [canvas.left + canvas.width / 2, canvas.top + canvas.height / 2];
canvas.radius = canvas.width / 2;


window.onmousemove = function(e) {
    var result = limit(e.x, e.y);
    if (!result.limit) {
        pointer.style.left = result.x + "px";
        pointer.style.top = result.y + "px";
    }
}

function limit(x, y) {
    var dist = distance([x, y], canvas.center);
    if (dist <= canvas.radius) {
        return {x: x, y: y};
    } else {
        return {limit: true};
    }
}

function distance(dot1, dot2) {
    var x1 = dot1[0],
        y1 = dot1[1],
        x2 = dot2[0],
        y2 = dot2[1];
    return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}  

虽然运动不太平滑,但这可以完成工作...需要更多的几何知识来解决这个问题...
示例:http://jsfiddle.net/cRxMa/


非常感谢!即使光标不在圆圈上方,我确实需要点“跟随”光标,我会尝试阅读一些几何知识。 - methodofaction

1

只要您对每个数据点(预期位置)进行归一化,这个算法就变得很简单了,我已经在下面的函数中尝试过:

function locatePoint(canvas_size, next_position) {
    // canvas_size & next_position are both 2-element arrays
    // (w, h) & (x, y)
    dist = function(x, y) {
        return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
    };
    x = next_position[0];
    y = next_position[1];
    rescaledX = x/(canvas_size[0]/2);
    rescaledY = y/(canvas_size[1]/2);
    if (distance(x, y) <= 1) {
    // the base case; position is w/in the circle
    } 
    else {
    // position is outside the circle, so perhaps
    // do something like random select a new position, then
    // call this function again (recursively) passing in 
    // that new position
    }   
}

在下面的简单图中,我只是在一个边长为r*2的正方形内刻画了一个单位圆(r=1)。但您的画布尺寸不一定非得是正方形。为了进一步简化计算,您只需要考虑其中的一个象限——比如说右上象限。原因是欧几里得距离公式会对每个坐标值进行平方运算,所以负数变成了正数。
换句话说,最简单的方法是想象一个圆被刻画在您的画布上,并且其圆心也是您的画布中心(所以(0, 0)是中心而不是左上角);接下来,画布和圆都会被“缩小”,直到圆的半径=1。希望我已经在上面的函数中表达清楚了这一点。

enter image description here


非常感谢您的解释。在 else 语句中(光标在圆形外部),是否将光标与中心之间的角度计算出来,并将点放置在相应角度处的圆周上,是一个不错的策略吗? - methodofaction
我想你可以这样做,但更容易的方法是将坐标对乘以某个折扣因子(小于1),然后通过距离函数评估这个新的坐标对。我假设你并不真正关心光标放在圆内的具体位置,只要它在里面就行了。 - doug
我需要它位于圆形边缘,就像它在跟随鼠标一样。 - methodofaction
因此,沿着单位圆边缘的坐标对由 x = cos(theta) 和 y = sin(theta) 给出;theta 逆时针测量,从单位圆上的 (1,0) 开始。 - doug

0

你好,感谢分享你的解决方案。

你的 jsfiddle 帮助我很多,约束旋转手柄的移动。

这是我的解决方案,使用 jQuery:

function getBall(xVal, yVal, dxVal, dyVal, rVal, colorVal) {
  var ball = {
    x: xVal,
    lastX: xVal,
    y: yVal,
    lastY: yVal,
    dx: dxVal,
    dy: dyVal,
    r: rVal,
    color: colorVal,
    normX: 0,
    normY: 0
  };

  return ball;
}

var canvas = document.getElementById("myCanvas");
var xLabel = document.getElementById("x");
var yLabel = document.getElementById("y");
var dxLabel = document.getElementById("dx");
var dyLabel = document.getElementById("dy");

var ctx = canvas.getContext("2d");

var containerR = 200;
canvas.width = containerR * 2;
canvas.height = containerR * 2;
canvas.style["border-radius"] = containerR + "px";

var balls = [
  getBall(containerR, containerR * 2 - 30, 2, -2, 20, "#0095DD"),
  getBall(containerR, containerR * 2 - 50, 3, -3, 30, "#DD9500"),
  getBall(containerR, containerR * 2 - 60, -3, 4, 10, "#00DD95"),
  getBall(containerR, containerR * 2 / 5, -1.5, 3, 40, "#DD0095")
];

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  for (var i = 0; i < balls.length; i++) {
    var curBall = balls[i];
    ctx.beginPath();
    ctx.arc(curBall.x, curBall.y, curBall.r, 0, Math.PI * 2);
    ctx.fillStyle = curBall.color;
    ctx.fill();
    ctx.closePath();
    curBall.lastX = curBall.x;
    curBall.lastY = curBall.y;
    curBall.x += curBall.dx;
    curBall.y += curBall.dy;
    var dx = curBall.x - containerR;
    var dy = curBall.y - containerR;
    var distanceFromCenter = Math.sqrt(dx * dx + dy * dy);

    if (distanceFromCenter >= containerR - curBall.r) {
      var normalMagnitude = distanceFromCenter;
      var normalX = dx / normalMagnitude;
      var normalY = dy / normalMagnitude;
      var tangentX = -normalY;
      var tangentY = normalX;
      var normalSpeed = -(normalX * curBall.dx + normalY * curBall.dy);
      var tangentSpeed = tangentX * curBall.dx + tangentY * curBall.dy;
      curBall.dx = normalSpeed * normalX + tangentSpeed * tangentX;
      curBall.dy = normalSpeed * normalY + tangentSpeed * tangentY;
    }

    xLabel.innerText = "x: " + curBall.x;
    yLabel.innerText = "y: " + curBall.y;
    dxLabel.innerText = "dx: " + curBall.dx;
    dyLabel.innerText = "dy: " + curBall.dy;
  }
  requestAnimationFrame(draw);
}

draw();
canvas { background: #eee; }
<div id="x"></div>
<div id="y"></div>
<div id="dx"></div>
<div id="dy"></div>
<canvas id="myCanvas"></canvas>

希望这能帮助到某个人。


你的回答与问题无关。来自未来的问候) - shukshin.ivan
在你说我的回答不相关之前,你有没有看过我在jsFiddle中提供的代码?只需比较我的代码(特别是“limit”和“distance”函数)和@Duopixel在他的回答中提供的代码即可。我很高兴能够给他启发,让他自己回答自己的问题。 - TwystO
这个小工具很好用。但是它没有包含在圆形内移动或者一个移动的点(小圆形条件与小正方形条件不同)。抱歉,但这是事实。虽然这并不是犯罪行为,但对于某些人来说可能会有用。回答错误应该被投票否决。 - shukshin.ivan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接