经别人仔细观察,发现 mouse:down 事件里有个 button 属性:
左键:button 的值为 1
右键:button 的值为 3
中键(也就是点击滚轮),button 的值为 2,前提需要设置 fireMiddleClick: true
DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Fabric.js 右键菜单设置title>
<script src="../fabric5.2.1.js">script>
<style>
.box {
position: relative;
}
#imageCanvas {
border: 1px solid #ccc;
}
.menu-x {
visibility: hidden;
z-index: -100;
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
background-color: #fff;
}
.menu-li {
box-sizing: border-box;
padding: 4px 8px;
border-bottom: 1px solid #ccc;
cursor: pointer;
}
.menu-li:hover {
background-color: antiquewhite;
}
.menu-li:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.menu-li:last-child {
border-bottom: none;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
style>
head>
<body>
<div class="box">
<canvas id="imageCanvas" width="300" height="300">canvas>
<div id="menu" class="menu-x">
<div class="menu-li" onclick="copyEl()">复制div>
<div class="menu-li" onclick="lockEl()">锁定div>
<div class="menu-li" onclick="unlockEl()">取消锁定div>
<div class="menu-li" onclick="moveElUp()">上移一层div>
<div class="menu-li" onclick="moveElDown()">下移一层div>
<div class="menu-li" onclick="moveElTop()">置于顶层div>
<div class="menu-li" onclick="moveElBottom()">置于底层div>
<div class="menu-li" onclick="delEl()">删除div>
div>
div>
<a id="lnkDownload" href="#">
<button> 保存图片button>
a>
<script type="text/javascript">
window.onload = function () {
// 禁止在菜单上的默认右键事件
menu.oncontextmenu = function (e) {
e.preventDefault()
}
}
script>
<script src="script.js">script>
body>
html>
var canvas = new fabric.Canvas('imageCanvas', {
backgroundColor: 'rgb(240,240,240)',
includeDefaultValues: false,// 指示toObject/toDatalessObject是否应该包含默认值,如果设置为false,则优先于对象值
perPixelTargetFind: true, //这一句说明选中的时候以图形的实际大小来选择而不是以边框来选择
hasBorders: false,
fireRightClick: true, // 启用右键,button的数字为3
stopContextMenu: true, // 禁止默认右键菜单
// 元素对象被选中时保持在当前z轴,不会跳到最顶层
preserveObjectStacking: true // 默认false
});
canvas.setWidth(500);
canvas.setHeight(500);
// 菜单 DOM
var menu = document.getElementById('menu');
// 矩形
var rect = new fabric.Rect({
top: 30,
left: 30,
fill: 'orange',
width: 100,
height: 100,
});
canvas.add(rect);
// 三角形
var triangle = new fabric.Triangle({
top: 80,
left: 30,
width: 80,
height: 100,
fill: 'blue'
});
canvas.add(triangle);
// 圆形
var circle = new fabric.Circle({
radius: 50,
fill: 'green',
top: 50,
left: 60,
controls: false, // 不可编辑
hasControls: false, // 控件将不显示,并且不能用于操作对象
});
canvas.add(circle);
// 使用 IText,可编辑文本
var text = new fabric.IText(
'奇葩,www.qipa250.com',
{
left: 100,
top: 330,
fontSize: 25,
fontFamily: 'Comic Sans'
}
);
canvas.add(text);
// 按下鼠标
canvas.on('mouse:down', canvasOnMouseDown);
// 鼠标在画布上的点击事件
function canvasOnMouseDown(opt) {
// 判断:右键,且在元素上右键
// opt.button: 1-左键;2-中键;3-右键
// 在画布上点击:opt.target 为 null
if (opt.button === 3 && opt.target) {
// 获取当前元素
activeEl = opt.target;
menu.domReady = function () {
console.log(123);
}
// 显示菜单,设置右键菜单位置
// 获取菜单组件的宽高
const menuWidth = menu.offsetWidth;
const menuHeight = menu.offsetHeight;
// 当前鼠标位置
let pointX = opt.pointer.x;
let pointY = opt.pointer.y;
// 计算菜单出现的位置
// 如果鼠标靠近画布右侧,菜单就出现在鼠标指针左侧
if (canvas.width - pointX <= menuWidth) {
pointX -= menuWidth;
}
// 如果鼠标靠近画布底部,菜单就出现在鼠标指针上方
if (canvas.height - pointY <= menuHeight) {
pointY -= menuHeight;
}
// 将菜单展示出来
menu.style = `
visibility: visible;
left: ${pointX}px;
top: ${pointY}px;
z-index: 100;
`;
} else {
hiddenMenu();
}
}
// 隐藏菜单
function hiddenMenu() {
menu.style = `
visibility: hidden;
left: 0;
top: 0;
z-index: -100;
`;
activeEl = null;
}
// 剪切元素
function cutEl() {
canvas.remove(activeEl);
hiddenMenu();
}
// 复制元素
function copyEl() {
canvas.getActiveObject().clone(function (cloned) {
pasteEl(cloned);
});
canvas.renderAll();
hiddenMenu();
}
// 粘贴元素
function pasteEl(_clipboard) {
// clone again, so you can do multiple copies.
let canvas = this.canvas;
_clipboard.clone(function (clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 20,
top: clonedObj.top + 20,
evented: true,
});
if (clonedObj.type === 'activeSelection') {
// active selection needs a reference to the canvas.
clonedObj.canvas = canvas;
clonedObj.forEachObject(function (obj) {
canvas.add(obj);
});
// this should solve the unselectability
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
_clipboard.top += 20;
_clipboard.left += 20;
canvas.setActiveObject(clonedObj);
// canvas.requestRenderAll();
});
hiddenMenu();
}
// 锁定元素
function lockEl() {
activeEl.lockMovementX = true;
activeEl.lockMovementY = true;
hiddenMenu();
}
// 取消锁定元素
function unlockEl() {
activeEl.lockMovementX = false;
activeEl.lockMovementY = false;
hiddenMenu();
}
// 元素上移一层
function moveElUp() {
// 方法1
canvas.bringForward(activeEl);
hiddenMenu();
}
// 元素下移一层
function moveElDown() {
canvas.sendBackwards(activeEl);
hiddenMenu();
}
// 元素置于顶层
function moveElTop() {
canvas.bringToFront(activeEl);
hiddenMenu();
}
// 元素置于底层
function moveElBottom() {
canvas.sendToBack(activeEl);
hiddenMenu();
}
// 删除元素
function delEl() {
canvas.remove(activeEl);
hiddenMenu();
}
var imageSaver = document.getElementById('lnkDownload');
imageSaver.addEventListener('click', saveImage, false);
function saveImage() {
console.log('toJSON==', canvas.toJSON());
console.log('toObject==', canvas.toObject()); // 输出序列化的内容
this.href = canvas.toDataURL({
format: 'png',
quality: 0.8
});
this.download = 'canvas.png';
}