HTML5 Canvas形状选择、缩放和旋转

Transformer 是一种特殊的 Konva.Group。它允许你轻松地缩放和旋转任何节点或节点集。

要启用它,你需要:

  1. 使用 new Konva.Transformer() 创建新实例
  2. 将其添加到图层
  3. 使用 transformer.nodes([shape]); 附加到节点
  4. 使用 layer.batchDraw() 更新图层

注意: 变换工具在缩放节点时不会改变节点的 widthheight 属性。相反,它会改变 scaleXscaleY 属性。

说明:尝试缩放和旋转形状。点击空白区域以取消选择。使用 SHIFT 或 CTRL 添加/移除选择中的形状。尝试在画布上选择区域。

Konva 图形变换和选择演示view raw
<!DOCTYPE html>
<html>
<head>
<!-- USE DEVELOPMENT VERSION -->
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Select and Transform Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>

<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;

var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});

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

var rect1 = new Konva.Rect({
x: 60,
y: 60,
width: 100,
height: 90,
fill: 'red',
name: 'rect',
draggable: true,
});
layer.add(rect1);

var rect2 = new Konva.Rect({
x: 250,
y: 100,
width: 150,
height: 90,
fill: 'green',
name: 'rect',
draggable: true,
});
layer.add(rect2);

var tr = new Konva.Transformer();
layer.add(tr);

// by default select all shapes
tr.nodes([rect1, rect2]);

// add a new feature, lets add ability to draw selection rectangle
var selectionRectangle = new Konva.Rect({
fill: 'rgba(0,0,255,0.5)',
visible: false,
// disable events to not interrupt with events
listening: false,
});
layer.add(selectionRectangle);

var x1, y1, x2, y2;
var selecting = false;
stage.on('mousedown touchstart', (e) => {
// do nothing if we mousedown on any shape
if (e.target !== stage) {
return;
}
e.evt.preventDefault();
x1 = stage.getPointerPosition().x;
y1 = stage.getPointerPosition().y;
x2 = stage.getPointerPosition().x;
y2 = stage.getPointerPosition().y;

selectionRectangle.width(0);
selectionRectangle.height(0);
selecting = true;
});

stage.on('mousemove touchmove', (e) => {
// do nothing if we didn't start selection
if (!selecting) {
return;
}
e.evt.preventDefault();
x2 = stage.getPointerPosition().x;
y2 = stage.getPointerPosition().y;

selectionRectangle.setAttrs({
visible: true,
x: Math.min(x1, x2),
y: Math.min(y1, y2),
width: Math.abs(x2 - x1),
height: Math.abs(y2 - y1),
});
});

stage.on('mouseup touchend', (e) => {
// do nothing if we didn't start selection
selecting = false;
if (!selectionRectangle.visible()) {
return;
}
e.evt.preventDefault();
// update visibility in timeout, so we can check it in click event
selectionRectangle.visible(false);
var shapes = stage.find('.rect');
var box = selectionRectangle.getClientRect();
var selected = shapes.filter((shape) =>
Konva.Util.haveIntersection(box, shape.getClientRect())
);
tr.nodes(selected);
});

// clicks should select/deselect shapes
stage.on('click tap', function (e) {
// if we are selecting with rect, do nothing
if (selectionRectangle.visible()) {
return;
}

// if click on empty area - remove all selections
if (e.target === stage) {
tr.nodes([]);
return;
}

// do nothing if clicked NOT on our rectangles
if (!e.target.hasName('rect')) {
return;
}

// do we pressed shift or ctrl?
const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
const isSelected = tr.nodes().indexOf(e.target) >= 0;

if (!metaPressed && !isSelected) {
// if no key pressed and the node is not selected
// select just one
tr.nodes([e.target]);
} else if (metaPressed && isSelected) {
// if we pressed keys and node was selected
// we need to remove it from selection:
const nodes = tr.nodes().slice(); // use slice to have new copy of array
// remove node from array
nodes.splice(nodes.indexOf(e.target), 1);
tr.nodes(nodes);
} else if (metaPressed && !isSelected) {
// add the node into selection
const nodes = tr.nodes().concat([e.target]);
tr.nodes(nodes);
}
});
</script>
</body>
</html>