学习整理fabric.js实现自定义右击菜单键:复制、锁定、取消锁定、上移一层、下移一层、置顶、置底、删除

188 篇文章 2 订阅
17 篇文章 5 订阅

学习整理fabric.js实现自定义右击菜单键

右击键

经别人仔细观察,发现 mouse:down 事件里有个 button 属性:

左键:button 的值为 1
右键:button 的值为 3
中键(也就是点击滚轮),button 的值为 2,前提需要设置 fireMiddleClick: true

原图

在这里插入图片描述

效果图

在这里插入图片描述

index.html

<!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>

script.js

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

}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值