【问题标题】:How to constrain movement within the area of a circle如何限制圆区域内的运动
【发布时间】:2011-12-15 06:16:21
【问题描述】:

这可能更多是与几何相关的问题,但我试图将控制器限制在圆的区域内。我知道我必须接触 Math.sin() 和 Math.cos() 方法,但到目前为止我的尝试都没有结果。

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

【问题讨论】:

  • 您需要确保 x^2 + y^2
  • 我已将您的小提琴更新为:jsfiddle.net/vJHZz/35 - 我不是数学天才,所以我尝试了一种蛮力方法来找到有效的 x/y 组合。它还没有完成,但我没时间了。我希望您可以将其用作获得解决方案的起点-目标是摆脱循环中的安全网(中断子句)-愉快的编码;)

标签: javascript geometry


【解决方案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);
        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/

【讨论】:

    【解决方案2】:
    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/

    【讨论】:

    • 非常感谢!我真的需要点来“跟随”光标,即使它不在圆圈上,我会尝试阅读一些几何图形。
    【解决方案3】:

    只要您规范化每个数据点(预期位置),这个算法就很简单了,我在下面的函数中尝试过:

    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) 是 center 而不是左上角-手角);接下来,画布和圆都缩小,直到圆的半径 = 1。希望我已经在上面的函数中捕捉到了这一点。

    【讨论】:

    • 非常感谢您的解释。在else 语句中(光标在圆外),找到光标与圆心之间的角度并将点沿圆的圆周以相应角度放置是否是一个好策略?
    • 我想你可以,但它更容易,例如,将坐标对乘以某个折扣因子(小于一),然后通过距离函数评估这个新对。我假设您并不真正关心将光标放在圆圈内的哪个位置,只关心它在某个地方。
    • 我需要它位于圆的边缘,就好像它跟随光标一样。
    • 所以位于单位圆边缘的坐标对由 x = cos(theta) 和 y = sin(theta) 给出; theta 是逆时针方向测量的,从单位圆上的 (1, 0) 开始。
    【解决方案4】:

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

    你的 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>

    希望这对某人有所帮助。

    【讨论】:

    • 您的回答与问题无关。来自未来的问候)
    • 在说我的回复无关紧要之前,您是否查看了我在 jsFiddle 中提供的代码?只需比较我的代码(尤其是limitdistance 函数)和在他的回复中给出@Duopixel 的代码。我很高兴给他灵感来回答他自己的问题。
    • 小提琴很棒。但它既不包含在圆圈内移动也不包含移动点(小圆圈条件与小正方形条件不同)。对不起,但事实如此。然而,这不是犯罪,它可能对某人有用。犯罪答案应该被否决)
    猜你喜欢
    • 1970-01-01
    • 2021-12-19
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 2020-01-13
    • 2011-03-15
    相关资源
    最近更新 更多