【问题标题】:KineticJS - Swapping shape positions upon contact/mousemove triggerKineticJS - 在接触/鼠标移动触发时交换形状位置
【发布时间】:2016-12-29 15:38:06
【问题描述】:

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

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

这是我编写的相关代码以尝试使其正常工作:

电路板设置: 只需在此处设置板并调用所需的功能。我还没有对舞台、gameBoard 或 ctx 做任何事情(部分原因是我试图让 drawImage 在多个画布上工作,但现在已经放弃了)。

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 文档和许多 StackOverflow 答案,希望能找到适合我的解决方案,但到目前为止我没有看到和尝试过(包括我提出的建议)写了这个问题)似乎有帮助,而且我知道到目前为止我所做的事情可能远非完成我想要的最佳方式,所以我愿意接受任何建议、指点和回答问题,或者任何可以为我指明正确方向的问题,以便让这个工作正常进行。

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

这些圆圈都是动态圆圈(用于我要达到的目的的球体),单击并拖动一个到另一个,没有被拖动但悬停的那个应该移动到原始位置拖动圆圈。

谢谢!

编辑:

从那时起,我对代码进行了一些更改。首先,我将在舞台上添加许多层改为仅一层:

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

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

编辑 2:

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

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

这导致 x 位置发生变化。但是把它放在一个窗口中似乎不是一个好习惯?

编辑 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
  }
}

球体交换逻辑,适用于一次通过球体,但如果它们在同一回合中再次通过,则无效。

【问题讨论】:

    标签: javascript html canvas kineticjs


    【解决方案1】:

    “悬停”何时正式开始?

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

      // 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)
      }
      

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

    【讨论】:

    • 我想要的(暂时-我只想至少让球体交换工作)是前者。我对代码做了一些更改,一个好处是拖放速度更快,但交换仍然不起作用。我的主要问题是让球体完全交换,以及我改变哪个x,y来做到这一点。我将对我的 OP 进行更改,并更详细地解决我在上面想要的内容。谢谢。
    • 我已经有一段时间没有使用 K-JS 了,在拖动球体上应该有类似 onDragMove 的事件。这给了你拖动位置。不要费心使用内置的intersecting 方法——它很慢。相反,请使用我在回答中概述的两种方法之一。请记住,使用 K-JS,您必须重新绘制图层,以便悬停的球体在视觉上移动到保存的 x,y。如果您的球体没有移动,请确保在移动每个球体后执行 layer.redraw 之类的操作。
    • 谢谢,我现在已经设法让我的球体在鼠标接触到球体时交换位置(并且可以连续交换它们)。当您尝试在 mousemove 仍在触发时换回时,它不会这样做,所以我会弄清楚这一点。 .draw 似乎不适用于 mousemove,但它适用于 mouseup,但我会尝试弄清楚这一点。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2014-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    相关资源
    最近更新 更多