Skip to main content

HTML5 Canvas 形状选择、调整大小和旋转

Transformer 是一种特殊类型的 Konva.Group。它允许您轻松调整大小和旋转任何节点或节点集。

要启用它,您需要:

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

注意: 变换工具在调整节点大小时不会更改节点的 widthheight 属性。相反,它会更改 scaleXscaleY 属性。

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

import Konva from 'konva';

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

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

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

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

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

// 创建变换器
const tr = new Konva.Transformer();
layer.add(tr);

// 添加一个新功能,让我们添加绘制选择矩形的能力
let selectionRectangle = new Konva.Rect({
  fill: 'rgba(0,0,255,0.5)',
  visible: false,
});
layer.add(selectionRectangle);

let x1, y1, x2, y2;
stage.on('mousedown touchstart', (e) => {
  // 如果我们在任何形状上按下鼠标,则不执行任何操作
  if (e.target !== stage) {
  return;
  }
  x1 = stage.getPointerPosition().x;
  y1 = stage.getPointerPosition().y;
  x2 = stage.getPointerPosition().x;
  y2 = stage.getPointerPosition().y;

  selectionRectangle.setAttrs({
    x: x1,
    y: y1,
    width: 0,
    height: 0,
    visible: true,
  });
});

stage.on('mousemove touchmove', () => {
  // 如果我们没有开始选择,则不执行任何操作
  if (!selectionRectangle.visible()) {
  return;
  }
  x2 = stage.getPointerPosition().x;
  y2 = stage.getPointerPosition().y;

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

stage.on('mouseup touchend', () => {
  // 如果我们没有开始选择,则不执行任何操作
  if (!selectionRectangle.visible()) {
    return;
  }
  // 在超时中更新可见性,以便我们可以在点击事件中检查它
  setTimeout(() => {
    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);
});

// 点击应该选择/取消选择形状
stage.on('click tap', function (e) {
// 如果我们正在使用矩形选择,则不执行任何操作
if (selectionRectangle.visible() && selectionRectangle.width() > 0 && selectionRectangle.height() > 0) {
  return;
}

  // 如果点击空白区域 - 移除所有选择
  if (e.target === stage) {
    tr.nodes([]);
    return;
  }

  // 如果点击的不是我们的矩形,则不执行任何操作
  if (!e.target.hasName('rect')) {
    return;
  }

  // 我们按下了 shift 还是 ctrl?
  const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
  const isSelected = tr.nodes().indexOf(e.target) >= 0;

  if (!metaPressed && !isSelected) {
    // 如果没有按下键且节点未被选中
    // 只选择一个
    tr.nodes([e.target]);
  } else if (metaPressed && isSelected) {
    // 如果我们按下了键且节点已被选中
    // 我们需要将其从选择中移除:
    const nodes = tr.nodes().slice(); // 使用 slice 获取数组的新副本
    // 从数组中移除节点
    nodes.splice(nodes.indexOf(e.target), 1);
    tr.nodes(nodes);
  } else if (metaPressed && !isSelected) {
    // 将节点添加到选择中
    const nodes = tr.nodes().concat([e.target]);
    tr.nodes(nodes);
  }
});