情况如下:我有一个矩形网格。里面有一个点。位于左上方(位置:x:1和y:1)。此网格的坐标范围是1到4。使用旋转函数like described in this question。我能够根据矩形 Canvas 的中心旋转该点。为此,我使用(width + 1)/ 2和(height + 1)/ 2。数字1的加法与 Canvas 有关,只是在将坐标绘制到的矩形网格周围创建了白色边距/偏移/边框。此 Canvas 的尺寸范围是0到5。

沿方形网格旋转时。一切顺利。但是当宽度不等于其高度时。预期结果与预期不符。它应该像俄罗斯方块一样旋转。但是,俄罗斯方块的“点”移到其网格之外。

下面我更深入地了解了这个问题。红点是需要旋转的点。浅蓝色/绿色点是红点旋转的中心点。顺时针旋转90度。

更新:可视化问题
在正方形网格中会发生什么
javascript - 如何旋转内部带有点的矩形?-LMLPHP

在矩形网格中会发生什么
javascript - 如何旋转内部带有点的矩形?-LMLPHP

程式码片段

function rotate(cx, cy, x, y, angle) {
    var radians = (Math.PI / 180) * -angle,
        cos = Math.cos(radians),
        sin = Math.sin(radians),
        nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
        ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
    return {x:nx, y: ny};
}

rotate((width +1)/2, (height + 1)/2, x,y, 90)
var tempWidth = width;
width = height;
height = tempWidth;


将其内容旋转90度的正确方法是什么?

Update2:可视代码段

const svgHelper = {
    init: (width, height, scale) => {
        const svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
        svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink")
        svg.setAttribute("x", "0px")
        svg.setAttribute("y", "0px")
        svg.setAttribute("viewBox", `0 0 ${width + 1} ${height + 1}`)
        svg.setAttribute("xml:space", `preserve`)

        return svg;

    },

    addCircle: (element, color, x, y, radius, className) => {
          element.insertAdjacentHTML("beforeend", `\r<circle
          style="fill: ${color}"
          cx="${x}"
          cy="${y}"
          r="${radius}"
          class="${className}"
          />`);
          return element.lastChild;
    },
    addText: (element, string) => {
          element.insertAdjacentHTML("beforeend", `\r<text x="0.2" y=".2">${string}</text>`);
          return element.lastChild;
    }
}

const Grid = {
    init: (width, height, margin) => {
        const result = {
            margin: margin,
            array: []
        };
        for (var i = 0; i < height; i++) {
            result.array.push([]);
            for (var ii = 0; ii < width; ii++) {
                result.array[i].push(0);
            }
        }
        return result.array;
    },
    draw: (svg) => {
        const tmp = svg.getAttribute("viewBox").split(" ");
        const width = tmp[2]
        const height = tmp[3]

        for (var y = 1; y < height; y++) {
            for (var x = 1; x < width; x++) {
                var posX = x //grid.margin
                var posY = y //grid.margin

                svgHelper.addCircle(svg, "#eee", posX, posY, .1);
            }
        }
    }
}

const updateGrid = (width, height,element) => {
    element.setAttribute("viewBox", `0 0 ${width + 1} ${height + 1}`)

    // Remove old points
    var points = element.querySelectorAll("circle")
    for (var i = 0; i < points.length; i++) {
        points[i].remove();
    }
    Grid.draw(element);
}

function rotate(cx, cy, x, y, angle) {
    var radians = (Math.PI / 180) * -angle,
        cos = Math.cos(radians),
        sin = Math.sin(radians),
        nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
        ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
    return {x:nx, y: ny};
}


const draw = (width, height) => {

    const div = document.createElement("div");
    const element = svgHelper.init(width, height);
    const span = document.createElement("span");
    div.appendChild(element);
    div.appendChild(span);

    span.className = "text";
    let point = {x:1, y:1};
    document.querySelector("body").appendChild(div);

    Grid.draw(element);
    let prevPoint = svgHelper.addCircle(element, "#f00", point.x, point.y, .125, "point")

    setInterval(() => {

        var tmpWidth = width;
        width = height;
        height = tmpWidth;
        updateGrid(width, height, element)

        point = rotate((width + 1)/2, (height + 1)/2, point.x, point.y, 90)
        prevPoint = svgHelper.addCircle(element, "#f00", point.x, point.y, .125, "point")
        span.innerText = `x: ${Math.round(point.x)} y: ${Math.round(point.y)}`;

    }, 1000)
}

draw(4,4)
draw(1,4)
div {
    position: relative;
    display: inline-block;
}

svg {
    width: 135px;
    height: 135px;
    background-color: lightgray;
    margin: 10px;
}

span {
    font-size: 10px;
    display: block;
    margin-left: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <link rel="stylesheet" href="./index.css">
</head>
<body>
<script src="index.js"></script>
</body>
</html>

最佳答案

出现问题是因为您的点旋转公式未考虑XY方向上的比例不同。我们该如何解决?

最简单的解决方案(IMHO)应包含三个步骤:

  • map 指向方格
  • 应用旋转并计算新的点坐标
  • map 指向矩形网格

  • 从关系中可以轻松找到映射功能
    x_new = (x_old - x_center) * scale + x_center
    x_old = (x_new - x_center) / scale + x_center
    
    y的关系完全相同,因此我忽略了它们。

    因此,您的旋转功能应进行如下调整:
    function rotate(cx, cy, x, y, angle, scaleX, scaleY) { // <= Adding scale factors here
      // Mapping initial points to a square grid
      x = (x - cx) / scaleX + cx;
      y = (y - cy) / scaleY + cy;
    
      // Applying rotation
      var radians = (Math.PI / 180) * -angle,
        cos = Math.cos(radians),
        sin = Math.sin(radians),
        nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
        ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
    
      // Mapping new point coordinates back to rectangular grid
      nx = (nx - cx) * scaleX + cx;
      ny = (ny - cy) * scaleY + cy;
    
      return {x: nx, y: ny};
    }
    

    这是动画,现在如何工作:
    javascript - 如何旋转内部带有点的矩形?-LMLPHP

    这是一个工作示例:

    function clearAll(container) {
      const ctx = container.getContext('2d');
      ctx.clearRect(0, 0, 300, 300);
    }
    
    function putPoint(container, x, y, r, color) {
      const ctx = container.getContext('2d');
      ctx.beginPath();
      ctx.fillStyle = color;
      ctx.arc(x, y, r, 0, 2 * Math.PI);
      ctx.fill();
    }
    
    function drawGrid(container, scaleX, scaleY, elapsedTime) {
      const scale = 60;
      const offset = 60;
    
      const centerX = offset + 1.5 * scale * scaleX;
      const centerY = offset + 1.5 * scale * scaleY;
    
      clearAll(container);
      putPoint(container, centerX, centerY, 3, 'cyan');
    
      for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 4; j++) {
          const x = offset + scaleX * scale * i;
          const y = offset + scaleY * scale * j;
          putPoint(container, x, y, 2, 'black');
        }
      }
    
      putPoint(container, offset, offset, 5, 'red');
      const newPosition1 = rotate(centerX, centerY, offset, offset, 90, scaleX, scaleY);
      putPoint(container, newPosition1.x, newPosition1.y, 5, 'red');
    
      const newPosition2 = rotate(centerX, centerY, offset, offset, 180, scaleX, scaleY);
      putPoint(container, newPosition2.x, newPosition2.y, 5, 'red');
    
      const newPosition3 = rotate(centerX, centerY, offset, offset, 270, scaleX, scaleY);
      putPoint(container, newPosition3.x, newPosition3.y, 5, 'red');
    
      const newPosition4 = rotate(centerX, centerY, offset, offset, 45, scaleX, scaleY);
      putPoint(container, newPosition4.x, newPosition4.y, 5, 'red');
    
      const newPositionDynamic = rotate(centerX, centerY, offset, offset, elapsedTime, scaleX, scaleY);
      putPoint(container, newPositionDynamic.x, newPositionDynamic.y, 5, 'red');
    
    
      requestAnimationFrame(() => {
        drawGrid(container, scaleX, scaleY, elapsedTime + 1);
      })
    }
    
    function rotate(cx, cy, x, y, angle, scaleX, scaleY) { // <= Adding scale factors here
      // Mapping initial points to a square grid
      x = (x - cx) / scaleX + cx;
      y = (y - cy) / scaleY + cy;
    
      // Applying rotation
      var radians = (Math.PI / 180) * -angle,
    cos = Math.cos(radians),
    sin = Math.sin(radians),
    nx = (cos * (x - cx)) + (sin * (y - cy)) + cx,
    ny = (cos * (y - cy)) - (sin * (x - cx)) + cy;
    
      // Mapping new point coordinates back to rectangular grid
      nx = (nx - cx) * scaleX + cx;
      ny = (ny - cy) * scaleY + cy;
    
      return {x: nx, y: ny};
    }
    
    const squareGrid = document.getElementById('square-grid');
    const rectangularGrid = document.getElementById('rectangular-grid');
    
    drawGrid(squareGrid, 1, 1, 0);
    drawGrid(rectangularGrid, 0.5, 0.9, 0);
    canvas {
      width: 300px;
      height: 300px;
      background-color: lightgray;
      margin: 10px;
    }
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
      <link rel="stylesheet" href="./index.css">
    </head>
    <body>
    <canvas width="300" height="300" id="square-grid"></canvas>
    <canvas width="300" height="300" id="rectangular-grid"></canvas>
    
    <script src="index.js"></script>
    </body>
    </html>


    请让我知道我是否理解错了,或者此解决方案不是您期望得到的解决方案

    关于javascript - 如何旋转内部带有点的矩形?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57737096/

    10-11 04:11
    查看更多