如何将鼠标X,Y坐标转换为在容器中移动HTML元素的位置,其中容器被旋转到某个角度

6
我正在开发一款基于HTML的纸牌游戏,可以向所有玩家实时展示视角和卡牌移动。
所有玩家通过socket.io连接。
背景:
如果有4个玩家,桌子是正方形的,桌子的HTML主体根据玩家在屏幕上的位置进行旋转。
结构如下:
<gameTable style=”position: relative;”> 

<card style=”position: absolute; top: 1%, left: 1%;”></card> 

</gameTable > 

现在为了移动卡片,玩家用鼠标选择一张卡片,然后:
1. 保存该位置
2. 基于移动,将卡片的移动(以像素为单位)转换为相对等量的百分比,然后将卡片在桌子上移动相应的位置。
这样,无论是使用大屏幕还是小屏幕,卡片的移动都将使用“%”作为顶部、左侧坐标,以便所有人都能看到同样的效果。
--正方形桌子:
当玩家处于0度桌子位置时,鼠标和卡片运动直接关联。
卡片顶部:--鼠标y,卡片左侧:--鼠标x
当玩家处于180度桌子位置时,鼠标运动是反向的:
卡片顶部:++鼠标y,卡片左侧:++鼠标x
对于90度:
卡片顶部:++鼠标x,卡片左侧:--鼠标y
类似地,270度旋转的桌子也可以使用类似的坐标系。
这个方法非常完美,因为通过这个翻译,代码中的鼠标方向翻译得非常准确。
--问题:
对于一个6人游戏,我想使用六边形桌子。在这个桌子上,玩家的桌子旋转到0度、60度、120度、180度、240度和300度。
0度和180度没问题,但我无法找出一个正确的公式来将鼠标坐标翻译成基于旋转的卡片坐标。
--目前的解决方法(不好):
目前,我进行了一些暴力编程,并像这样做:(60度桌子) 如果(鼠标向上/向下移动){
卡片顶部:--鼠标y/1.7,卡片左侧:--鼠标y
}
如果(鼠标左/右移动){
卡片顶部:++鼠标x,卡片左侧:--鼠标x/1.7
}
对于六边形桌子上的其他位置也采用类似的方法,只需要稍加修改即可。
这是一种暴力的方法,它阻止了我从上/下运动过渡到左/右状态,因为我必须停下鼠标然后重新开始。
简而言之,它没有起作用,我想知道是否可能有一个公式,可以根据旋转正确地将鼠标坐标翻译成卡片坐标。
请查看以下图片,以获得您所需的可视化表达。

正方形表格

六边形表格

1个回答

2

简而言之,请参考JSFiddle

从四个玩家到六个玩家,您需要了解支持四个玩家的数学知识并将其应用于六个玩家的情况。

四个玩家的情况

您可以将每个玩家的位置视为一个以桌子中心为原点的圆周上给定的角度(或弧度)。在四个玩家的情况下,第一个玩家(索引0)的角度为0度,第二个玩家为90度,等等。

要确定一个玩家的移动如何影响不处于同一角度的另一个人屏幕上的卡片的移动,您需要将卡片的运动分解为其基础。在这种情况下,基础由两个正交向量组成,它们定义了当前玩家的运动空间。第一个向量是使用角度生成的向量。第二个向量可以通过取当前角度并添加90度(Pi / 2弧度)来找到。对于四个玩家的情况,第一个玩家将具有0度的主基向量,即(1,0)。第一个玩家的次要基向量将是0 + 90 = 90度,即(0,1)。

要将鼠标移动(delta screen x和y)转换为使用任意基础的运动,您需要取delta screen x和y与基础中每个向量的x和y的点积。在四个玩家的情况下,当第一个玩家移动卡片时,主要和次要基础的点积分别得到x delta和y delta。

计算出当前玩家的这个值后,您可以按照其基础术语移动其他玩家屏幕上的卡片。为此,请将已转换为沿两个基础的运动应用于每个其他玩家的基础。在四个玩家的情况下,第二个玩家的主基础为90度=(1,0),次要基础为90 + 90度=(-1,0)。因此,要在第二个玩家的位置上镜像移动,原始x运动被转换为y运动,原始y运动被转换为-x运动。

六(或任意数量)个玩家

要将此更改为具有任意数量的玩家并且第一个玩家可能偏离0度的游戏,您需要遵循相同的数学方法。首先,为每个玩家计算基础。其次,使用点积计算以其基础术语中移动的卡片的delta。第三,将此运动应用于其他玩家的基础。

下面的示例允许您更改玩家数量和起始偏移角度。卡片是div,覆盖在div上的是画布,用于显示每个基础的主轴(红色)和次要轴(蓝色)。

<div>
<div class="table-div">
</div>
<div>
  This example shows how the current players card (blue) can be shown moving on other players' (red "ghost" cards).
  Below you
  can change the number of players and the angle offset of the starting player.
</div>
<div>
  <p>Players</p>
  <input type="text" onchange="playerChange()" id="playerText" value="6">
</div>
<div>
  <p>Angle (Degrees)</p>
  <input type="text" onchange="angleChange()" id="angleText" value="45">
</div>
<canvas id="canv"></canvas>

body {
  margin: 0;
}

canvas {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 9;
}

.table-div {
  background-color: lightgreen;
  width: 400px;
  height: 400px;
}

.card {
  position: absolute;
  width: 50px;
  height: 70px;
}

.current {
  background-color: blue;
}

.ghost {
  background-color: red;
}

//A simple class to hold an x,y pair
class Vector2 {
  constructor(x, y) { [this.x, this.y] = [x, y] }
}

//Set up our parameters
let playerCount = 6; //Can be changed by the user
let offsetAngle = 45; //Can be changed by the user
let width = 400;
let height = width;
let radius = width / 2;
let cardWidth = 50;
let cardHeight = 50 * 1.4;//~70
let cards;

//Track the mouse
let lastMouse = null;
let currentMouse = null;
let mouseDown = false;

//The angle in radians between each player on the table
let angleDelta;

//Grab the table div
let tableElement = document.querySelector(".table-div");
let canvas = document.querySelector("canvas")
let ctx = canvas.getContext("2d");
canvas.style.width = width + "px";
canvas.style.height = height + "px"
canvas.width = width;
canvas.height = height;

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

  for (let i = 0; i < playerCount; i++) {
    //Draw the first axis
    ctx.strokeStyle = "red";
    ctx.beginPath();
    ctx.moveTo(radius, radius);
    ctx.lineTo(radius + Math.cos(angleDelta * i + offsetAngle) * radius, radius + Math.sin(angleDelta * i + offsetAngle) * radius)
    ctx.stroke();

    //Draw the second axis
    let secondAxisAngle = angleDelta * i + Math.PI / 2;
    let startX = radius + Math.cos(angleDelta * i + offsetAngle) * radius / 2;
    let startY = radius + Math.sin(angleDelta * i + offsetAngle) * radius / 2;
    let endX = Math.cos(secondAxisAngle + offsetAngle) * radius / 4;
    let endY = Math.sin(secondAxisAngle + offsetAngle) * radius / 4;

    ctx.strokeStyle = "green";
    ctx.beginPath();
    ctx.moveTo(startX, startY)
    ctx.lineTo(startX + endX, startY + endY)
    ctx.stroke();
  }
}

document.body.addEventListener("mousemove", e => {
  //Keep track of the last mouse position so we can calculate a delta
  lastMouse = currentMouse;
  currentMouse = new Vector2(e.clientX, e.clientY);
  if (lastMouse && mouseDown) {
    let mouseDelta = new Vector2(currentMouse.x - lastMouse.x, currentMouse.y - lastMouse.y);
    if (mouseDown) {
      //Determine the movement in the current player's basis
      let primaryAxisCurrent = new Vector2(Math.cos(angleDelta * 0 + offsetAngle), Math.sin(angleDelta * 0 + offsetAngle))
      let secondAxisCurrent = new Vector2(Math.cos(angleDelta * 0 + Math.PI / 2 + offsetAngle), Math.sin(angleDelta * 0 + Math.PI / 2 + offsetAngle))
      //Determine the movement in terms of the primary axes
      let primary = (primaryAxisCurrent.x * mouseDelta.x + primaryAxisCurrent.y * mouseDelta.y)// * mouseDelta.x;
      let second = (secondAxisCurrent.x * mouseDelta.x + secondAxisCurrent.y * mouseDelta.y)// * mouseDelta.y;

      //Update all the cards using the primary and secondary motion
      for (let i = 0; i < playerCount; i++) {
        //Get the axes for this card
        let primaryAxis = new Vector2(Math.cos(angleDelta * i + offsetAngle), Math.sin(angleDelta * i + offsetAngle))
        let secondAxis = new Vector2(Math.cos(angleDelta * i + Math.PI / 2 + offsetAngle), Math.sin(angleDelta * i + Math.PI / 2 + offsetAngle))
        //Translate the motion into this card's axes
        let primaryAxisMovement = new Vector2(primaryAxis.x * primary, primaryAxis.y * primary)
        let secondAxisMovement = new Vector2(secondAxis.x * second, secondAxis.y * second)

        //Get the current location (strip the 'px') and add the change in position.
        let div = cards[i];
        let location = new Vector2(parseFloat(div.style.left) + primaryAxisMovement.x + secondAxisMovement.x, parseFloat(div.style.top) + primaryAxisMovement.y + secondAxisMovement.y)
        //Reappend 'px'
        div.style.left = location.x + "px"
        div.style.top = location.y + "px"
      }
    }
  }
})

document.body.addEventListener("mousedown", () => mouseDown = true)
document.body.addEventListener("mouseup", () => mouseDown = false)

function initDivs() {
  //Create all the cards
  cards = [];
  tableElement.innerHTML = "";
  for (let i = 0; i < playerCount; i++) {
    let div = document.createElement("div");
    tableElement.appendChild(div);
    div.classList.add("card")
    if (i == 0) div.classList.add("current")
    else div.classList.add("ghost")

    let radians = angleDelta * i;
    div.style.left = (radius - cardWidth / 2 + Math.cos(radians + offsetAngle) * (radius - cardWidth / 2)) + "px";
    div.style.top = (radius - cardHeight / 2 + Math.sin(radians + offsetAngle) * (radius - cardHeight / 2)) + "px"
    cards.push(div);
  }
}
function playerChange() {
  playerCount = +document.querySelector("#playerText").value;
  angleDelta = Math.PI * 2 / playerCount;
  initDivs();
  angleChange();
}
function angleChange() {
  //Convert from degrees to radians
  offsetAngle = parseFloat(document.querySelector("#angleText").value) * Math.PI / 180;
  initDivs();
  update();
}

playerChange();

我无法感谢你们的付出!你花时间给了我最巧妙的解决方案!我已经在我的游戏中使用它,一切都很顺利。但是,我不明白其中一个修改(我数学不太好)。我的做法是,在每个玩家屏幕上计算卡牌坐标,然后传输给其他人。唯一让我困惑的是偏移角度。我必须在两个区域中以不同的方式使用它: ..下一个评论。 - Hayder Ameen
计算所使用的偏移角度为:offsetAngle = (parseFloat(document.querySelector("#angleText").value) * Math.PI) / 180;其值如下:offsetAngles = { // key: tableRotation, value: value used inside the code above 0: 0, 60: 0, 120: 180, 180: 0, 240: 180, 300: 0, }; - Hayder Ameen
然后,对于计算运动,以下代码行中使用的偏移角度为:angles = { // key: tableRotation, value: value used inside the code 0: 180, 60: 60, 120: 120, 180: 180, 240: 240, 300: 300, }; offsetAngle = (angles[tableRotation] * Math.PI) / 180;使用1的值似乎不起作用。我确定我在这里缺少一些数学知识。 - Hayder Ameen
嗨@bricksphd,我能请你看一下这个问题吗?提前谢谢! :) https://stackoverflow.com/questions/67377180/coordinates-calculation-how-to-translate-an-object-on-a-rotated-html-body-respe - Hayder Ameen

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