【问题标题】:KonvaJS - Rotate rectangle around cursor without using offsetKonvaJS - 围绕光标旋转矩形而不使用偏移
【发布时间】:2019-11-27 16:04:26
【问题描述】:

我正在使用 KonvaJS 将矩形拖放到预定义的插槽中。一些插槽需要旋转 90 度。我在垂直旋转的插槽周围有一个命中框,因此当用户将矩形拖入该区域时,它将自动旋转 90 度(以匹配方向)。当它旋转时,它会从鼠标下方移出。这可以通过偏移来解决,但是在捕捉后矩形不会在视觉上与框对齐。这可以(可能)通过附加代码来解决。

我尝试旋转矩形,然后在鼠标下移动它。由于用户仍在拖动它,这似乎没有按我的计划工作。

是否可以在不使用偏移的情况下强制矩形在鼠标下旋转?

这是一个显示问题的小提琴 - 可以通过将第一个变量设置为 true 来演示偏移问题。 https://jsfiddle.net/ChaseRains/1k0aqs2j/78/

var width = window.innerWidth;
var height = window.innerHeight;

var rectangleLayer = new Konva.Layer();
var holdingSlotsLayer = new Konva.Layer();
var controlLayer = new Konva.Layer();
var stage = new Konva.Stage({
  container: 'container',
  width: width,
  height: height,
  draggable: true
});
//vertical holding spot
holdingSlotsLayer.add(new Konva.Rect({
  x: 300,
  y: 25,
  width: 130,
  height: 25,
  fill: '#fff',
  draggable: false,
  rotation: 90,
  stroke: '#000'
}));

//horizontal holding spot
holdingSlotsLayer.add(new Konva.Rect({
  x: 25,
  y: 75,
  width: 130,
  height: 25,
  fill: '#fff',
  draggable: false,
  rotation: 0,
  stroke: '#000'
}));

//mask to set boundaries around where we wannt to flip the rectangle
controlLayer.add(new Konva.Rect({
  x: 215,
  y: 15,
  width: 150,
  height: 150,
  fill: '#fff',
  draggable: false,
  name: 'A',
  opacity: 0.5
}));
stage.add(holdingSlotsLayer, controlLayer);

//function for finding intersections
function haveIntersection(placeHolder, rectangle, zone) {
  if (rectangle.rotation == 0 || zone == true) {
    return !(
      rectangle.x > placeHolder.x + placeHolder.width ||
      rectangle.x + rectangle.width < placeHolder.x ||
      rectangle.y > placeHolder.y + placeHolder.height ||
      rectangle.y + rectangle.height < placeHolder.y
    );
  } else {
    return !(
      rectangle.x > placeHolder.x + 25 ||
      rectangle.x + rectangle.width < placeHolder.x ||
      rectangle.y > placeHolder.y + placeHolder.height + 90 ||
      rectangle.y + rectangle.height < placeHolder.y
    );
  }
}

//function to create rectangle group (so we can place text on the rectangle)
function spawnRectangle(angle) {
  var rectangleGroup = new Konva.Group({
    x: 95,
    y: 95,
    width: 130,
    height: 25,
    rotation: angle,
    draggable: true,
  });

  rectangleGroup.add(new Konva.Rect({
    width: 130,
    height: 25,
    fill: 'lightblue'
  }));

  rectangleGroup.add(new Konva.Text({
    text: '123',
    fontSize: 18,
    fontFamily: 'Calibri',
    fill: '#000',
    width: 130,
    padding: 5,
    align: 'center'
  }));

  //function tied to an on drag move event
  rectangleGroup.on('dragmove', (e) => {
    //shrink rectangle hitbox for use in placeholder intersection
    var dimensions = {
      "height": 3,
      "width": 5,
      "x": e.target.attrs.x,
      "y": e.target.attrs.y,
      'rotation': e.target.attrs.rotation
    };
    //loop over holding slots to see if there is an intersection.
    for (var i = 0; holdingSlotsLayer.children.length > i; i++) {
      //if true, change the look of the slot we are hovering
      if (haveIntersection(holdingSlotsLayer.children[i].attrs, dimensions, false)) {
        holdingSlotsLayer.children[i].attrs.fill = '#C41230';
        holdingSlotsLayer.children[i].attrs.dash = [10, 3];
        holdingSlotsLayer.children[i].attrs.stroke = '#000';
        //set attributes back to normal otherwise
      } else {
        holdingSlotsLayer.children[i].attrs.fill = '#fff';
        holdingSlotsLayer.children[i].attrs.dash = null;
        holdingSlotsLayer.children[i].attrs.stroke = null;
      }
    }

    //check to see if we are in a zone that requires the rectangle to be flipped 90 degrees 
    if (haveIntersection(controlLayer.children[0].attrs, dimensions, true)) {
      if (rectangleGroup.attrs.rotation != 90) {
        rectangleGroup.attrs.rotation = 90;
      }
    } else {
      rectangleGroup.attrs.rotation = 0;
    }

    stage.batchDraw();
  });

  rectangleGroup.on('dragend', (e) => {

    for (var i = 0; holdingSlotsLayer.children.length > i; i++) {
      //If the parking layer has an element that is lit up, then snap to position.. 
      if (holdingSlotsLayer.children[i].attrs.fill == '#C41230') {
        rectangleGroup.position({
          x: holdingSlotsLayer.children[i].attrs.x,
          y: holdingSlotsLayer.children[i].attrs.y
        });
        holdingSlotsLayer.children[i].attrs.fill = '#fff';
        holdingSlotsLayer.children[i].attrs.dash = null;
        holdingSlotsLayer.children[i].attrs.stroke = null;
      }
    }

    stage.batchDraw();
  });


  rectangleLayer.add(rectangleGroup);
  stage.add(rectangleLayer);
}
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #D3D3D3;
  background-size: cover;
}

#desc {
  position: absolute;
  top: 5px;
  left: 5px;
}
<script src="https://unpkg.com/konva@4.0.18/konva.min.js"></script>

<body>
  <div id="container"></div>
  <div id="desc">
    <button onclick="spawnRectangle(0)">spawn rectangle</button>
  </div>
</body>

【问题讨论】:

  • 您甚至可以在 mousedown 中将组偏移量移动到鼠标位置。偏移量是旋转点。然后,当应用旋转时,它会使矩形围绕鼠标旋转。您可能必须考虑重叠代码中的位置变化。 PS。一个很好的问题。
  • 哈哈 - 只需重新阅读问题并注意不抵消的请求!那么,你还能做什么......好吧,生活是画布上的一个矩形,所以要获得相同的效果,你需要在发生旋转的时间点从鼠标指针计算矢量,然后计算出要旋转的矢量旋转后移动矩形的原点。这只是数学。

标签: javascript konvajs


【解决方案1】:

这里有一个简单的函数来旋转鼠标下的矩形,不使用 konva offset()。我使用补间来应用移动,但如果您更喜欢在没有补间的情况下使用它,只需应用 rect.rotate() 然后应用 newPos x & y 作为位置。

编辑:OP指出,如果您在矩形完成动画时单击并按住鼠标,然后拖动,则矩形会跳开。是什么赋予了 ?好吧,当 mousedown 事件运行时,Konva 会记录形状在其内部拖动功能中的初始位置。然后当我们开始实际拖动鼠标时,Konva 会尽职尽责地在它计算出的位置重新绘制形状。现在,“我们”知道我们将形状移入了代码,但我们没有让 Konva 参与我们的诡计。

解决方法是调用

 rect.stopDrag(); 
 rect.startDrag();

设置新位置后立即。因为我使用的是补间,所以我在其中一个补间的 onFinish() 回调函数中执行此操作 - 如果您应用多个补间,您将希望确保它是最终补间。我侥幸逃脱,因为我的补间在同一时期运行。如果您不使用补间,只需在对形状应用最后一个 rotate() 或 position() 调用时立即调用上述方法。

function rotateUnderMouse(){


  // Get the stage position of the mouse
  var mousePos = stage.getPointerPosition();

  // get the stage position of the mouse
  var shapePos = rect.position();

  // compute the vector for the difference
  var rel = {x: mousePos.x - shapePos.x, y: mousePos.y - shapePos.y} 

  // Now apply the rotation
  angle = angle + 90;


  // and reposition the shape to keep the same point in the shape under the mouse 
  var newPos = ({x: mousePos.x  + rel.y , y: mousePos.y - rel.x}) 

  // Just for fun, a tween to apply the move: See https://konvajs.org/docs/tweens/Linear_Easing.html
  var tween1 = new Konva.Tween({
    node: rect,
    duration: 0.25,
    x:  newPos.x,
    y: newPos.y,
    easing: Konva.Easings.Linear,
    onFinish: function() { rect.stopDrag(); rect.startDrag();}
  });
  
  // and a tween to apply the rotation 
  tween2 = new Konva.Tween({
    node: rect,
    duration: 0.25,
    rotation: angle,
    easing: Konva.Easings.Linear
  });
    
  
  tween2.play();
  tween1.play();


  
}


function setup() {

// Set up a stage and a shape
stage = new Konva.Stage({
  container: 'canvas-container',
  width: 650,
  height: 300
});


layer = new Konva.Layer();
stage.add(layer);

newPos = {x: 80, y: 40};
rect = new Konva.Rect({
   width: 140, height: 50, x: newPos.x, y: newPos.y, draggable: true, stroke: 'cyan', fill: 'cyan'
  })
layer.add(rect);

stage.draw()

rect.on('mousedown', function(){
  rotateUnderMouse()
})

}

var stage, layer, rect, angle = 0;

setup()
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/4.0.13/konva.js"></script>

<p>Click the rectangle - it will rotate under the mouse.</p>

<div id="canvas-container"></div>

【讨论】:

    猜你喜欢
    • 2023-04-04
    • 1970-01-01
    • 2018-08-24
    • 1970-01-01
    • 1970-01-01
    • 2015-11-06
    • 2021-11-07
    • 2015-03-29
    • 1970-01-01
    相关资源
    最近更新 更多