我正在使用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/[email protected]/konva.min.js"></script>
<body>
<div id="container"></div>
<div id="desc">
<button onclick="spawnRectangle(0)">spawn rectangle</button>
</div>
</body>
这里是一个简单的函数,无需使用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>