跳到主要内容

Canvas 右键上下文菜单——为 Canvas 图形创建自定义菜单

需要为 Canvas 图形显示上下文菜单吗?

如需显示上下文菜单,必须:

  1. 监听 Canvas 容器(舞台)的 contextmenu 事件
  2. 阻止浏览器默认行为,以隐藏原生上下文菜单
  3. 使用 Konva 工具或常规 HTML 创建自定义上下文菜单

操作说明:双击舞台以创建圆形。右键单击图形,打开上下文菜单。

import Konva from 'konva';

// Create a div to use as a context menu
const menuNode = document.createElement('div');
menuNode.id = 'menu';
menuNode.style.display = 'none';
menuNode.style.position = 'fixed';
menuNode.style.width = '60px';
menuNode.style.backgroundColor = 'white';
menuNode.style.boxShadow = '0 0 5px grey';
menuNode.style.borderRadius = '3px';

// Create buttons for the menu
const pulseButton = document.createElement('button');
pulseButton.textContent = 'Pulse';
pulseButton.style.width = '100%';
pulseButton.style.backgroundColor = 'white';
pulseButton.style.border = 'none';
pulseButton.style.margin = '0';
pulseButton.style.padding = '10px';

const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.style.width = '100%';
deleteButton.style.backgroundColor = 'white';
deleteButton.style.border = 'none';
deleteButton.style.margin = '0';
deleteButton.style.padding = '10px';

// Add hover effects
pulseButton.addEventListener('mouseover', () => {
  pulseButton.style.backgroundColor = 'lightgray';
});
pulseButton.addEventListener('mouseout', () => {
  pulseButton.style.backgroundColor = 'white';
});

deleteButton.addEventListener('mouseover', () => {
  deleteButton.style.backgroundColor = 'lightgray';
});
deleteButton.addEventListener('mouseout', () => {
  deleteButton.style.backgroundColor = 'white';
});

// Add buttons to menu
menuNode.appendChild(pulseButton);
menuNode.appendChild(deleteButton);
document.body.appendChild(menuNode);

// Set up the stage
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);

// add default shape
const shape = new Konva.Circle({
  x: stage.width() / 2,
  y: stage.height() / 2,
  radius: 50,
  fill: 'red',
  shadowBlur: 10,
});
layer.add(shape);

let currentShape;

// Setup the menu functionality
pulseButton.addEventListener('click', () => {
  currentShape.to({
    scaleX: 2,
    scaleY: 2,
    onFinish: () => {
      currentShape.to({ scaleX: 1, scaleY: 1 });
    },
  });
});

deleteButton.addEventListener('click', () => {
  currentShape.destroy();
});

// Hide menu on document click
window.addEventListener('click', () => {
  menuNode.style.display = 'none';
});

// Add double click event to create new shapes
stage.on('dblclick dbltap', function () {
  // add a new shape
  const newShape = new Konva.Circle({
    x: stage.getPointerPosition().x,
    y: stage.getPointerPosition().y,
    radius: 10 + Math.random() * 30,
    fill: Konva.Util.getRandomColor(),
    shadowBlur: 10,
  });
  layer.add(newShape);
});

// Add context menu event
stage.on('contextmenu', function (e) {
  // prevent default behavior
  e.evt.preventDefault();
  if (e.target === stage) {
    // if we are on empty place of the stage we will do nothing
    return;
  }
  currentShape = e.target;
  // show menu
  menuNode.style.display = 'initial';
  const containerRect = stage.container().getBoundingClientRect();
  menuNode.style.top =
    containerRect.top + stage.getPointerPosition().y + 4 + 'px';
  menuNode.style.left =
    containerRect.left + stage.getPointerPosition().x + 4 + 'px';
});