KineticJS - 在 contact/mousemove 触发时交换形状位置

KineticJS - Swapping shape positions upon contact/mousemove trigger

我正在使用 KineticJS 并尝试完成一些看起来很简单的事情:尝试让一个形状随着当前被拖动的形状改变位置。

想法是:
• 你在canvas 上选择了一个形状。这会触发一个 mousedown 事件侦听器,它会保存您拾取的形状的当前位置。
• 按住形状时,如果您在另一个形状上触发mouseover,该形状的事件将被触发并根据当前形状的保存位置交换其位置。

这是我为使它正常工作而编写的相关代码:

电路板设置: 只需简单地设置电路板并在此处调用所需的功能即可。我还没有对舞台、游戏板或 ctx 做任何事情(其中一部分是为了让 drawImage 在多个 canvases 上工作,但现在已经放弃了)。

class BoardView {
  constructor(stage, gameBoard, ctx) {
    this.stage = stage;
    this.gameBoard = gameBoard;
    this.ctx = ctx;
    this.orbs = [[], [], [], [], []];
    this.setupBoard();
  }

板设置函数: 这是我设置板并为每个 Kinetic Circle 提供它需要在图层上渲染的属性的地方。也许我在这里遗漏了一些明显的东西?

  setupBoard () {
    for (let colIdx = 0; colIdx < 5; colIdx++) {
      this.addRow(colIdx);
    }

    this.renderBoard();
  }

  addRow (colIdx) {
    for (let rowIdx = 0; rowIdx < 6; rowIdx++) {
      let orbType = Math.round(Math.random() * 5);
      let orbColor;

      if (orbType === 0) {
          orbColor = "#990000";
        } else if (orbType === 1) {
          orbColor = "#112288";
        } else if (orbType === 2) {
          orbColor = "#005544";
        } else if (orbType === 3) {
          orbColor = "#776611";
        } else if (orbType === 4) {
          orbColor = "#772299";
        } else {
          orbColor = "#dd2277";
      }
      let orbject = new Kinetic.Circle({
        x: (rowIdx + 0.5) * 100, 
        y: (colIdx + 0.5) * 100,
        width: 100, 
        height: 100,
        fill: orbColor, 
        draggable: true
      });
      this.orbs[colIdx].push(orbject);
    }
  }

棋盘渲染函数: 这是我将所有 Kinetic Circle 对象添加到新层的地方,并为这些层提供所有自己的属性,以便在我调用事件处理程序时使用。在将图层添加到舞台后,我还在此处设置了事件处理程序。我是不是加了太多层把事情搞砸了?

  renderBoard () {

    for (let row = 0; row < this.orbs.length; row++) {
      for (let orb = 0; orb < this.orbs[row].length; orb++) {
        let layer = new Kinetic.Layer();

        layer.add(this.orbs[row][orb]);
        layer.moving = false;
        layer.orbId = `orb${row}${orb}`;
        layer.pos = [this.orbs[row][orb].attrs.x, this.orbs[row][orb].attrs.y];

        this.stage.add(layer);

        layer.on("mousedown", this.handleMouseDown);
        layer.on("mouseup", this.handleMouseUp);
        layer.on("mouseout", this.handleMouseOut);
        layer.on("mousemove", this.handleMouseMove);
      }
    }
  }

鼠标事件处理程序: 这是我认为我遇到的主要问题。我如何处理移动鼠标改变球体的事件,也许我做错了什么?

  handleMouseDown (e) {
    window.currentOrb = this;
    console.log(window.currentOrb.orbId);

    this.moving = true;
  }

  //handleMouseUp (e) {
  //  window.currentOrb = undefined;
  //  this.moving = false;
  //}

  //handleMouseOut (e) {
  //}

  handleMouseMove (e) {
    if (window.currentOrb !== undefined && window.currentOrb.orbId != this.orbId) {
      this.children[0].attrs.x = window.currentOrb.pos[0];
      this.children[0].attrs.y = window.currentOrb.pos[1];
      this.children[0].draw();
    } else {
    }
  }
}


module.exports = BoardView;

我已经尝试查看 KineticJS 文档和许多 Whosebug 答案,希望找到适合我的解决方案,但到目前为止我没有看到和尝试过任何东西(包括提出的建议当我写这个问题时)似乎有帮助,而且我知道到目前为止我所采用的方式可能远非实现我想要的最佳方式,所以我愿意接受任何建议和指示,已回答的问题,或任何可以为我指明正确方向以使其正常工作所缺少的东西。

如果这对您有帮助,这里还有一个可视化的图板渲染时的外观。

圆圈都是Kinetic Circles(我要的是球体),点击并拖动一个到另一个,没有被拖动但悬停的那个应该移动到原来的位置拖动的圆圈数。

谢谢!

编辑:

从那以后我对代码做了一些修改。首先,我将在舞台上添加很多层更改为只添加一层:

renderBoard () {
  let layer = new Kinetic.Layer();

  for (let row = 0; row < this.orbs.length; row++) {
    for (let orb = 0; orb < this.orbs[row].length; orb++) {

      layer.add(this.orbs[row][orb]);

      this.orbCanvases.push(orbCanvas.id);
    }
  }
  this.stage.add(layer);
} 

我改为向 orb 对象添加侦听器:

addRow (colIdx) {
  for (let rowIdx = 0; rowIdx < 6; rowIdx++) {

    //same code as before

    let orbject = new Kinetic.Circle({
      x: (rowIdx + 0.5) * 100, y: (colIdx + 0.5) * 100,
      width: 100, height: 100,
      fill: orbColor, draggable: true, pos: [rowIdx, colIdx]
    });
    orbject.on("mousedown", this.handleMouseDown);
    orbject.on("mouseup", this.handleMouseUp);
    orbject.on("mouseout", this.handleMouseOut);
    orbject.on("mousemove", this.handleMouseMove);

    this.orbs[colIdx].push(orbject);
  }
}

这样做的好处是可以使拖放速度更快,而以前拖放速度非常慢,但我仍然无法让我的对象交换位置。

明确地说,我的主要问题是知道应该更改哪些 x、y 值。目前在handleMouseMove,我一直在努力改变:

e.target.attrs.x = newX;
e.target.attrs.y = newY;
// newX and newY can be any number

但是,无论我改成什么,都没有效果。所以它会帮助我知道我是否更改了错误的 thing/place,例如,也许我应该从我存储的数组中更改 Kinetic Circle?再次感谢。

编辑 2:

我想我明白了!但是,我必须使用 this.orbs 并将其设置在 window.orbs 的 window 中,然后我进行了测试:

window.orbs[0][0].x(450);
window.orbs[0][0].draw();

这导致 x 位置发生变化。但是把它放在 window 中似乎不是很好的做法?

编辑 3:

我让球体现在可以连续交换,除非在 mouseover 继续发射时再次交换同一个球体。然而,在 mouseup 处,它可以再次交换。我还必须再次设置多个图层才能让 mouseover 事件在持有另一个球体的同时工作,但性能似乎有所提高。

我将尝试弄清楚如何让它们能够在同一鼠标按住时连续交换,但与此同时,这是我为实现此目的而编写的代码:

addRow (colIdx) {
  for (let rowIdx = 0; rowIdx < 6; rowIdx++) {

    // same code as before, changed attr given to Kinetic.Circle

    let orbject = new Kinetic.Circle({
      x: (rowIdx + 0.5) * 100, y: (colIdx + 0.5) * 100,
      width: 100, height: 100,
      fill: orbColor, draggable: true, orbId: `orb${colIdx}${rowIdx}`
    });
  }
}

handleMouseDown (e) {
  window.currentOrb = window.orbs[e.target.attrs.orbId];
  window.newX = e.target.attrs.x;
  window.newY = e.target.attrs.y;
}

按下鼠标按 ID 及其 X 和 Y 保存 currentOrb

handleMouseUp (e) {
  window.currentOrb.x(window.newX);
  window.currentOrb.y(window.newY);
  window.currentOrb.parent.clear();
  window.currentOrb.parent.draw();
  window.currentOrb.draw();
  window.currentOrb = undefined;
  for (let i = 0; i < 5; i++) {
    for (let j = 0; j < 6; j++) {
      window.orbs[`orb${i}${j}`].draw();
    }
  }
}

当鼠标松开时,目前,所有球体都会重新绘制,因此它们都可以使用。我计划对此进行重构,因此只有悬停在上方的球体才会有此更改。

handleMouseMove (e) {

  if (window.currentOrb !== undefined && (window.currentOrb.attrs.orbId !== e.target.attrs.orbId)) {
    window.orbMove.pause();
    window.currentTime = 0;
    window.orbMove.play();
    let targOrbX = e.target.attrs.x;
    let targOrbY = e.target.attrs.y;
    // This is the target orb that's being changed's value
    // We're storing this in targOrb

    e.target.x(window.newX);
    e.target.y(window.newY);
    e.target.parent.clear();
    e.target.parent.draw();
    e.target.draw();

    // Here we have the previously set current orb's position becoming
    // the target orb's position

    window.newX = targOrbX;
    window.newY = targOrbY;

    // Now that the move is made, we can set the newX and Y to be the
    // target orb's position once mouseup
  }
}

Orb 交换逻辑适用于传递一次 orb,但如果它们在同一回合中再次传递则无效。

"hover"什么时候正式举行?

  • 当鼠标事件的位置进入第二个球体时?如果是,请点击测试鼠标与每个非拖动球体:

    // pseudo-code -- make this test for every non-dragging orb
    var dx=mouseX-orb[n].x;
    var dy=mouseY-orb[n].y; 
    if(dx*dx+dy*dy<orb[n].radius){
        // change orb[n]'s x,y to the dragging orb's x,y (and optionally re-render)
    }
    
  • 当拖动球体与第二个球体相交时?如果是,则对拖动球体与每个非拖动球体进行碰撞测试:

    // pseudo-code -- make this test for every non-dragging orb
    var dx=orb[draggingIndex].x-orb[n].x;
    var dy=orb[draggingIndex].y-orb[n].y;
    var rSum=orb[draggingIndex].radius+orb[n].radius;
    if(dx*dx+dy*dy<=rSum*rSum){
        // change orb[n]'s x,y to the dragging orb's x,y (and optionally re-render)
    }
    

顺便说一句,如果你将一个球体拖到所有其他球体上,其他球体将全部堆叠在拖动球体的原始位置上——这是你想要的吗?