跳到主要内容

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';
});