Fabric.js右键单击

10
有没有办法在Fabric.js画布上接收鼠标右键事件? 下面的代码只适用于左键点击:
canvas.observe('mouse:down', function(){console.log('mouse down'));

只需在画布上观察点击事件(不使用fabric而是使用DOM方法),然后检查它是否为右键而不是左键。 - kangax
对于最新版本(在 Fabric 2.7.0 中测试),您只需要在画布配置中启用这些事件即可。请参见我下面的答案 - Wilt
5个回答

27

注意: 以上大多数回答已经过时,此回答适用于最新的 Fabric 版本 2.7.0


为Fabric画布启用右键/中键点击事件

在画布中触发右键和中键点击事件的配置可以在这里fireRightClick这里fireMiddleClick找到,并默认设置为false。这意味着右键和中键点击事件默认情况下被禁用。 参数stopContextMenu可以在这里找到,用于停止在画布上右键单击时出现上下文菜单。

您可以通过在创建画布时设置这些值来轻松启用它们:

var canvas = new fabric.Canvas('canvas', {
  height: height,
  width: width,
  fireRightClick: true,  // <-- enable firing of right click events
  fireMiddleClick: true, // <-- enable firing of middle click events
  stopContextMenu: true, // <--  prevent context menu from showing
});

现在你的mousedown事件将针对所有点击触发,并可以通过使用事件上的按钮标识符来区分它们:
对于canvas:
canvas.on('mouse:down', (event) => {
    if(event.button === 1) {
        console.log("left click");
    }
    if(event.button === 2) {
        console.log("middle click");
    }
    if(event.button === 3) {
        console.log("right click");
    }
}

对于对象:

object.on('mousedown', (event) => {
    if(event.button === 1) {
        console.log("left click");
    }
    if(event.button === 2) {
        console.log("middle click");
    }
    if(event.button === 3) {
        console.log("right click");
    }
}

当点击对象时,您可以通过 event.e 访问“真实”的鼠标 DOM 事件:

if(event.button === 3){
  console.log(event.e);
}

阻止上下文菜单对我不起作用。我错过了什么?无论是IE还是Chrome,在画布右键单击时都会显示上下文菜单(控制台日志正常工作)。 - ps0604
@ps0604 你确定在DOM事件(event.e)上调用了 preventDefault() 吗?你可以通过将其记录到控制台来双重检查DOM事件是哪个对象。 - Wilt
如果您在控制台中收到了该错误消息和输出,则表示事件变量是DOM事件。如果同时添加event.preventDefault(); event.stopPropagation();会发生什么?我将在fiddle中尝试一次。您使用的canvas.js版本是什么? - Wilt
这里有一个演示它的代码片段。我还必须重新编号按钮才能正确显示。之前我一直在使用2.4.6版本,但是看到这篇帖子后转换到了2.7.0版本。两个版本都产生同样的效果:https://jsfiddle.net/morrowsend/qzr1s2a7/5/ - morrows_end
1
上面的 event.button 代码是错误的 - 它们从0到2,而不是1到3,请参见 https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events#determining_button_states - Nigel
显示剩余4条评论

4
我扩展了fabric.Canvas类来实现右键点击。请查看这里的_onMouseDown方法。
基本上,fabricjs默认禁用了对象的右键按下事件。

嗨,那很好,但我仍然不明白。如果我在您的库中使用此扩展_onMouseDown事件,在右键单击时会发生什么。问题的上下文中所需的行为是 - 如果我们有一个上下文菜单插件,我们希望覆盖此.canvas.on('mouse:down',(e)=> someFunc(e))上的默认行为,并触发来自插件的函数以显示上下文菜单。同时,在右键单击时,不应出现默认的浏览器上下文菜单。这应该是插件本身的责任,但只是想知道我们是否还需要在fabricJs中添加其他内容。 - Virto111
个人而言,我使用Angular 6,但$('#my_canvas').bind('contextmenu')对我无效。我更喜欢以TypeScript方式(面向对象的方式)“干净”地扩展事件和对象。 - Virto111

4

如果您想处理画布或其对象上的右键单击事件,则应在上层画布元素上设置上下文菜单监听器。使用画布方法findTarget,您可以检查是否单击了任何目标,如果是,则可以检查目标的类型。

let scope = this;
jQuery(".upper-canvas").on('contextmenu', function (options: any) {
    let target: any = scope.canvas.findTarget(options, false);
    if (target) {
        let type: string = target.type;
        if (type === "group") {
            console.log('right click on group');
        } else {
            scope.canvas.setActiveObject(target);
            console.log('right click on target, type: ' + type);
        }
    } else {
        scope.canvas.discardActiveObject();
        scope.canvas.discardActiveGroup();
        scope.canvas.renderAll();
        console.log('right click on canvas');
    }
    options.preventDefault();
});

你的回答确实值得一些解释。请参考http://stackoverflow.com/help/how-to-answer。 - J. Chomel
很棒,稍作修改,移除 discardActiveGroup 并消除 "group" 和 "activeSelection" 之间的类型区别。 - Diosney

3
我是这样做的,监听整个画布上的右键单击事件,并将单击事件的x,y坐标与当前位于给定位置的对象匹配。这种解决方案感觉有点像一个hack,但是它能够正常工作!
$('#my_canvas').bind('contextmenu', function (env) {
    var x = env.offsetX;
    var y = env.offsetY;
    $.each (canvas._objects, function(i, e) {
        // e.left and e.top are the middle of the object use some "math" to find the outer edges
        var d = e.width / 2;
        var h = e.height / 2;
        if (x >= (e.left - d) && x <= (e.left+d)) {
            if(y >= (e.top - h) && y <= (e.top+h)) {
                console.log("clicked canvas obj #"+i);
                //TODO show custom menu at x, y
                return false; //in case the icons are stacked only take action on one.
            }
        }
    });
    return false; //stops the event propigation
});

@jsbeckr 它的作用在于找到与那些坐标相匹配的第一个画布对象,然后停止。你可以更聪明地建立一个在该x/y位置上的对象列表,然后选择其中一个来采取行动(例如,最高z-index的对象?) - Chris Montgomery
是的,我做了那个。我发现你的解决方案没有考虑缩放的对象,这相当令人沮丧... :D 但很容易解决:var d = e.width * e.scaleX / 2;var h = e.height * e.scaleY / 2; - jsbeckr
@jsbeckr,旋转的对象怎么样了?你也解决了这个问题吗? - Bor691
@Bor691 抱歉,我不需要那个。 :/ - jsbeckr

2

以下是我所做的,利用了一些内置的面料物体检测代码:

$('.upper-canvas').bind('contextmenu', function (e) {
    var objectFound = false;
    var clickPoint = new fabric.Point(e.offsetX, e.offsetY);

    e.preventDefault();

    canvas.forEachObject(function (obj) {
        if (!objectFound && obj.containsPoint(clickPoint)) {
            objectFound = true;
            //TODO: whatever you want with the object
        }
    });
});

嗨!由于某种原因,当在画布上点击“返回”时,菜单不会消失。有什么想法吗? - Tahmid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接