KineticJS - 在接触/鼠标移动触发时交换形状位置 [英] KineticJS - Swapping shape positions upon contact/mousemove trigger

查看:703
本文介绍了KineticJS - 在接触/鼠标移动触发时交换形状位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用KineticJS,试图找到一些似乎很简单的东西:尝试获取一个形状,以改变当前被拖动的形状的位置。



想法是:

•在画布上拾取一个形状。这会触发一个 mousedown 事件侦听器,它保存您拾取的形状的当前位置。

•保持形状时,如果触发在另一个形状上的 mouseover ,根据当前形状的保存位置触发形状的事件并交换其位置。



只需设置板和调用所需的功能在这里。我不对stage,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 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);
}
}

Board渲染函数
这是我将所有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.movi​​ng = 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.movi​​ng = true;
}

// handleMouseUp(e){
// window.currentOrb = undefined;
// this.movi​​ng = 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 ++){

//与之前相同的代码

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



要明确,我的主要问题是知道哪个,y值应该改变。目前在 handleMouseMove 中,我一直尝试更改:

  e.target.attrs.x = newX; 
e.target.attrs.y = newY;
// newX和newY可以是任何数字

到,它没有效果。所以它会帮助我知道,如果我改变错误的事情/地方,例如,也许我应该改变动力学圈从我存储的数组?再次感谢。



编辑2:



但是,我不得不在 window.orbs 的窗口中设置 this.orbs 我做了:

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

这会导致x位置改变。但是把它放在窗口中似乎不是一个好的做法?



编辑3:



我得到的球体现在不断交换,除非当同一个球被再次交换,而 mouseover 继续射击。在 mouseup 然而,它可以再次交换。我还必须重新设置多个层,以获得 mouseover 事件工作,同时持有另一个球,但性能似乎有一点改进。



我将尝试弄清楚如何让他们能够在同一个鼠标保持不断交换,但在此期间,这里是我写的代码实现这一点: / p>

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

//和以前一样的代码,改变attr给予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;
}

em>

  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();
}
}
}

发布,目前,所有的球都重绘,所以他们都可以使用。我计划重构这个,所以只有orbs悬停在这个变化。

  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;
//这是被改变的值的目标球
//我们将它存储在targOrb中

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

//这里我们先前设置当前orb的位置为
//目标orb的位置

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

//现在移动了,我们可以设置newX和Y为
//目标orb的位置一旦mouseup
}
}

Orb交换逻辑,用于传递一次orbs,但如果它们再次传递

解决方案

hover何时正式发生?




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

      //伪代码 - non-dragging orb 
    var dx = mouseX-orb [n] .x;
    var dy = mouseY-orb [n] .y;
    if(dx * dx + dy * dy //改变orb [n]'sx,y到拖动的天体的x,y )
    }


  • 拖曳的天体与第二个天体相交?如果是,碰撞测试拖动的天体vs每个非拖动的天体:

      //伪代码 - 每个非拖动的天体
    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){
    //改变orb [n]'sx,y到拖动的orb的x,y b $ b}




一个球在所有其他球体,其他球将所有堆叠在拖曳的球体原始位置 - 是你想要什么?


I'm using KineticJS and trying to get something done that seems simple enough: trying to get a shape to change position with the shape that's currently being dragged.

The idea is that:
• You pick up a shape on the canvas. This triggers a mousedown event listener, which saves the current position of the shape you've picked up.
• While holding the shape, if you trigger the mouseover on another shape, that shape's event gets triggered and swaps its position, based on the current shape's saved position.

Here is the relevant code I've written to try to get that working:

Board setup: Simply just setting up the board and calling the needed functions here. I'm not doing anything with the stage, gameBoard, or ctx yet (part of it was for when I tried to get drawImage to work on multiple canvases, but have abandoned that for now).

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

Board setup functions: This is where I go about setting up the board and giving each Kinetic Circle the attributes it needs to render on the Layer. Maybe I'm missing something obvious here?

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

Board render function: This is where I add all the Kinetic Circle objects into new layers, and give those layers all its own attributes to work with when I call the event handlers. I also set up the event handlers here after adding the layers to the stage. Am I perhaps messing this up by adding too many layers?

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

Mouse event handlers: This is where I think I'm having my main problem. How I handle the event of moving the mouse to change orbs, perhaps I'm doing something terribly wrong?

  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;

I've tried looking at the KineticJS docs and many StackOverflow answers as I could in hopes of finding a solution that would work for me, but nothing I've seen and tried so far (including the suggestions that came up as I wrote this question) seemed to be of help, and I'm aware the way I've gone about this so far is probably far from the best way to accomplish what I want, so I'm open to any suggestions, pointers, answered questions, or whatever can point me in the right direction to what I'm missing in order to get this to work.

In case this is helpful, here is also a visualization of how things look when the board is rendered.

The circles are all Kinetic Circles (orbs for the purpose of what I'm going for), and clicking and dragging one to another, the one that isn't being dragged but hovered over should move to the original position of the dragged circles.

Thanks!

EDIT:

I made a few changes to the code since then. First off, I changed adding many layers to the stage, to just one:

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

I instead added listeners to the orb objects instead:

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

This has had the benefit of making drag and drop much much faster where before, it was going very slow, but I still can't get my objects to swap position.

To be clear, my main issue is knowing which x, y values I should be changing. At the moment in handleMouseMove, I've been trying to change:

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

However, no matter what I change it to, it has no effect. So it would help me to know if I'm changing the wrong thing/place, for example, maybe I'm supposed to be changing the Kinetic Circle from the array I've stored? Thanks again.

EDIT 2:

I think I got it! However, I had to take this.orbs and set it in the window at window.orbs, and to test it I did:

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

And this caused the x position to change. But putting it in a window does not seem like good practice?

EDIT 3:

I got the orbs to now swap continuously, except for when it's the same orb being swapped again while mouseover is continuing to fire. At mouseup however, it can be swapped again. I also had to set up multiple layers again to get the mouseover events to work while holding another orb, but the performance seems to have improved a little.

I'm going to try and figure out how to get them to be able to swap continuously on the same mouse hold, but in the meantime, here is the code I wrote to achieve this:

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;
}

Mouse down saving currentOrb by ID and its X and Y

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();
    }
  }
}

When mouse is released, currently, all orbs are redrawn so they can all be used. I plan to refactor this so only orbs hovered over have this change.

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 swapping logic which works for passing over orbs once, but not if they are passed over again in the same turn.

解决方案

When does the "hover" officially take place?

  • When the mouse event's position enters the second orb? If yes, hit test the mouse vs every non-dragging orb:

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

  • When the dragging orb intersects the second orb? If yes, collision test the dragging orb vs every non-dragging orb:

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

BTW, if you drag an orb over all other orbs, the other orbs will all stack on the dragging orbs original position -- is that what you want?

这篇关于KineticJS - 在接触/鼠标移动触发时交换形状位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆