如何翻译鼠标 X,Y 坐标以在容器内移动 HTML 元素,其中容器旋转到某个角度

2024-01-04

我正在开发一款 HTML 纸牌游戏,可以向所有玩家显示实时视角和纸牌移动。

所有玩家都通过 socket.io 连接。

背景:

如果有 4 名玩家,桌子是方形的,并且桌子 HTML 主体根据玩家在屏幕上的位置旋转。

结构是这样的:

<gameTable style=”position: relative;”> 

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

</gameTable > 

现在为了移动卡片,玩家用鼠标选择一张卡片,然后它

1:保存该位置

2:基于移动,将卡片的移动(以px为单位)转换为相对相等的百分比,然后将卡片在桌子上移动那么多。

这样,无论某人使用大屏幕还是小屏幕,通过使用 % 作为上、左坐标,卡片的移动对于所有人来说都是相同的。

-- 方桌:

当玩家处于 0 度桌面位置时,鼠标和卡片移动直接关联。

卡顶: --mouse y,卡左:--mouse x

当玩家处于 180 度桌面位置时,鼠标移动相反:

卡顶: ++mouse y, 左卡: ++mouse x

对于 90 度:

卡顶: ++mouse x, 左卡: --mouse y

270 度旋转工作台的类似平移,坐标略有变化。

这与此翻译一样完美,它完美地翻译了代码中所有方向的鼠标。

问题:

对于 6 人游戏,我想使用六角形桌子。现在在此表中,玩家表旋转为:

0、60、120、180、240 和 300 度。

0 and 180度都很好,但我无法找出适当的公式来根据旋转将鼠标坐标转换为卡片坐标。

-- 当前解决方案(不好)

目前,我做了一些强力编程,如下所示:(60度工作台)

如果(鼠标向上/向下移动){

卡顶部:--mouse y/1.7,卡左侧:--mouse y

}

如果(鼠标向左/向右移动){

卡顶部:++mouse x,卡左侧:--mouse x/1.7

}

对于六角表上的其他位置也采用类似的方法,但有一些小的变化。

这是蛮力方法,它阻止我从上/下移动转换到左/右状态,因为我必须离开鼠标然后重新开始。

简而言之,它不起作用,我想知道是否有可能有一个公式可以根据旋转正确地将鼠标的鼠标坐标转换为卡片。

请查看以下图片以直观地表达我的意思

方桌 https://i.stack.imgur.com/T6aPg.png

六角桌 https://i.stack.imgur.com/4nCjJ.png


TL;博士 看到这个JSFiddle https://jsfiddle.net/bricksphd/z01umwvq/3/

要从四名玩家转变为六名玩家,您必须了解四名玩家情况下的数学原理,然后将其应用于六名玩家情况。

四人案例

您可以将每个玩家的位置视为围绕原点位于桌子中心的圆的给定度数(或弧度)。在四名玩家的情况下,第一个玩家(索引 0)将处于 0 度,第二个玩家将处于 90 度,依此类推。

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

要将鼠标移动(增量屏幕 x 和 y)转换为使用任意基的移动,您需要获取增量屏幕 x 和 y 以及基中每个向量的 x 和 y 的点积。在四名玩家的情况下,当第一位玩家移动一张牌时,主要和次要基础的点积分别产生 x 增量和 y 增量。

计算出当前玩家的牌数后,您可以根据其基础在其他玩家的屏幕上移动牌。为此,将已转换为沿两个基地的运动的运动并将其应用到其他玩家的基础上。在四名玩家的情况下,第二名玩家的主要基准为 90 度 = (1,0),次要基准为 90+90 度 = (-1,0)。因此,为了镜像第二个玩家位置的运动,原始 x 运动被转换为 y 运动,原始 y 运动被转换为 -x 运动。

六名(或任意数量)玩家

要将其更改为具有任意数量玩家且第一个玩家可能偏离 0 度的游戏,您需要遵循相同的数学计算。首先,计算每个玩家的底数。其次,使用点积来计算卡片以其基数为基础移动的增量。第三,将此动作应用到其他玩家的基地。

下面的示例允许您更改玩家数量和起始偏移角度。这些卡片是 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();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何翻译鼠标 X,Y 坐标以在容器内移动 HTML 元素,其中容器旋转到某个角度 的相关文章

  • 使用 css 简单地将对象居中,无需修改

    我想使用 CSS 将对象居中 而不需要任何技巧 这可能吗 如何实现 我已经尝试过了 但是我的 p 标签消失了 centered position fixed top 50 left 50 有多种方法可以使元素居中 但这取决于您的元素是什么以
  • 使用 JavaScript 禁用第三方 cookie

    我正在努力根据所有在欧盟运营的公司的数据保护规则实施新的 Cookie 政策合规性 根据该规则 用户在使用任何网站时必须能够拒绝 接受除必需的 Cookie 之外的所有内容 在我客户的网站中 我可以看到正在存储以下第三方 cookie ga
  • 悬停此元素时隐藏元素后的伪元素

    我的菜单垂直放置在页面左侧和菜单之间 li 我有一个 after那是一个分隔符 我想要的是当我悬停元素本身 如果它是第一个元素 时隐藏 after 元素 或者当它是中间元素时隐藏上面和底部的元素 如果它是最后一个子元素 则隐藏 after前
  • 如何格式化 Highcharts 的 (x,y) 对数据的日期时间

    我的序列化方法会产生如下所示的日期时间字符串 2014 07 09T12 30 41Z 为什么下面的代码不起作用 function container highcharts xAxis type datetime series data x
  • 引导程序提前输入未填充承诺的响应

    我的引导程序预输入如下
  • php - 解析html页面

    div divbox div p para1 p p para2 p p para3 p table class table tr td td tr table p para4 p p para5 p 有人可以告诉我如何解析这个 html
  • 网站的主体和元素固定在 980px 宽度上,不会缩小

    我试图在 Rails 应用程序顶部启动前端 仅 HTML CSS 页面 但在使用 320px 视口时遇到问题 有些元素不会按比例缩小 我不明白为什么 我已经完成了检查元素 为各种元素提供了max width 100 and or width
  • 为什么我们在打字稿中使用 HTMLInputElement ?

    我们为什么使用 document getElementById ipv as HTMLInputElement value 代替 document getElementById ipv value 功能getElementById返回具有类
  • 有没有办法在 onclick 触发时禁用 iPad/iPhone 上的闪烁/闪烁?

    所以我有一个有 onclick 事件的区域 在常规浏览器上单击时 它不会显示任何视觉变化 但在 iPad iPhone 上单击时 它会闪烁 闪烁 有什么办法可以阻止它在 iPad iPhone 上执行此操作吗 这是一个与我正在做的类似的示例
  • 页面上使用 HTML Editor Extender 进行回发会导致 IE11 中出现 JavaScript 错误

    我已将 HTML 编辑器扩展程序添加到我正在处理的页面中 现在每当我在页面上发回帖子时 都会收到以下 Javascript 错误 JavaScript 运行时错误 参数无效 之后什么也没有发生 这在 IE10 或更低版本以及我所知道的所有其
  • Vuejs 2:去抖动不适用于手表选项

    当我在 VueJs 中反跳此函数时 如果我提供毫秒数作为原语 它就可以正常工作 但是 如果我将其提供为对 prop 的引用 它会忽略它 这是道具的缩写版本 props debounce type Number default 500 这是不
  • 可以设置标题样式吗? (并且使用CSS或js?)[重复]

    这个问题在这里已经有答案了 我想知道是否可以设计一个title a href title This is a title Hello a 样式问题有两个方面 文本格式 编码 我猜这是可能的 所以在问题中这样做 工具提示样式 你能把它弄大一点
  • Three.js 各种大小的粒子

    我是 Three js 的新手 正在尝试找出添加 1000 个粒子的最佳方法 每个粒子都有不同的大小和颜色 每个粒子的纹理是通过绘制画布创建的 通过使用粒子系统 所有粒子都具有相同的颜色和大小 为每个粒子创建一个粒子系统是非常低效的 有没有
  • 代码镜像错误:未捕获错误:扩展集中无法识别扩展值([对象对象])

    全部 我目前正在从事一个React Electron项目 该项目的目标是完成一个Markdown编辑器 当我配置codemirror 该程序报告错误说 Uncaught Error Unrecognized extension value
  • 表格行未扩展到全宽

    我有一个表格 当我将表格的宽度设置为 100 并将表格行的宽度设置为 100 时 没有任何反应或宽度发生变化 Table Normal position relative display block margin 10px auto pad
  • 如何使用 crypto-js 解密 AES ECB

    我正在尝试将加密数据从 flash 客户端 发送到服务器端的 javascript 在 asp 中作为 jscript 运行 有几个 javascript Aes 库 但它们实际上没有文档记录 我正在尝试使用 crypto js 但无法让代
  • 带参数的事件监听器

    我想将参数传递给 JavaScript 中的事件侦听器 我已经找到了解决方案 但我无法理解它们为什么或如何工作以及为什么其他解决方案不起作用 我有 C C 背景 但是 Javascript 函数的执行有很大不同 您能否帮助我理解以下示例如何
  • 在 CKEditor 中设置字体大小和字体系列

    我正在使用 ckeditor 我想问一下这个插件如何设置font family和font size 我尝试过使用 CKEDITOR config font defaultLabel Arial CKEDITOR config fontSiz
  • Jquery - 选择选项后如何获取选项的特定数据类型?

    我将直接跳到标记 然后解释我想要做什么 HTML 选择选项
  • 如何使用asm.js进行测试和开发?

    最近我读到asm js规范 看起来很酷 但是是否有任何环境 工具来开发和测试这个工具 这还只是处于规范阶段吗 您可以尝试使用 emscripten 和 ASM JS 1 并从侧分支在 firefox 构建中运行它 有关 asm js 的链接

随机推荐