How to add context menu on Raphael element?

戏子无情 提交于 2019-12-11 02:09:07

问题


I am working with ExtJS 4 framework and also using Raphael.js in my main application panel.

First I was trying to prevent the regular context menu from showing, and I found out very easy solution.

raphael_element.node.oncontextmenu = function()
{
    return false;
} 

This is working great, but before returning false I want to add some code to show my own custom menu.
Does anyone know how to do that?

I have something in html, that was thinking to use in the menu:

<ul id="rect_menu" class="contextMenu">
    <li><a href="call_to_js_function">Set aaa</a></li>
    <li><a href="call_to_js_function">Set xxx</a></li>
    <li><a href="call_to_js_function">Set ccc</a></li>
</ul>

As you can see, I have css class (code is not here, it irrelevant) for styling and id=rect_menu to show up when user right clicks on the i.e. Raphael rectangle object.

Can anyone show a way, maybe small demo that does not involve jquery? Thnaks


回答1:


You can use Ext to display your custom HTML in a floating component:

var menu = Ext.create('Ext.Component', {
    floating: true
    ,html: ['<ul id="rect_menu" class="contextMenu">',
        '<li><a href="call_to_js_function">Set aaa</a></li>',
        '<li><a href="call_to_js_function">Set xxx</a></li>',
        '<li><a href="call_to_js_function">Set ccc</a></li>',
        '</ul>'].join('')
});

Ext.get(raphael_element.node).on({
    contextmenu: function(e) {
        menu.showAt(e.getXY());
        e.preventDefault();
    }
});

However, showing the menu is not the tricky part. The logic to have your menu hidden, either when the user click outside of it, or when the ESC key is pressed is a lot more involved. That's why I would use a regular Menu to let Ext do the heavy lifting for us.

var menu = Ext.create('Ext.menu.Menu', {
    items: [{
        text: 'Set aaa'
        ,handler: function() {
            // logic for aaa
        }
    }, {
        text: 'Set bbb'
        ,handler: function() {
            // logic for bbb
        }
    }]
});

Ext.get(raphael_element.node).on({
    contextmenu: function(e) {
        menu.showAt(e.getXY());
        e.preventDefault();
    }
});

Now, if you're not interested in using all the menu system, with Ext menu items, etc., and you really want to render your own HTML, you can still leverage Ext menu to benefit from its hiding mechanics:

var menu = Ext.create('Ext.menu.Menu', {
    // This will prevent the icon gutter from showing, and your component from
    // being left padded
    plain: true
    ,items: [{
        xtype: 'component'
        ,html: ['<ul id="rect_menu" class="contextMenu">',
            '<li><a href="call_to_js_function">Set aaa</a></li>',
            '<li><a href="call_to_js_function">Set xxx</a></li>',
            '<li><a href="call_to_js_function">Set ccc</a></li>',
            '</ul>'].join('')
    }]
});

Ext.get(raphael_element.node).on({
    contextmenu: function(e) {
        menu.showAt(e.getXY());
        e.preventDefault();
    }
});

Here's a fiddle using that last example. It doesn't use Raphael, but from the moment you get a DOM element that won't bear any importance.

Edit Example with actual Raphael elements

I've updated my fiddle with actual Raphael elements. It does work exactly as intended.

What is cool is that the click event of the element totally respects the vectorial path bounds, meaning you can precisely decide where your menu will pop or not. What is less cool is that with, for example, the famous tiger, that would mean 240 event handlers... Kinda ouch performance-wise.... You can always add the handler to the SVG element instead of individual shape elements, but the menu will be bound to the whole drawing rectangle instead of individual parts of course.

// --- Creating a ball (see http://raphaeljs.com/ball.html)
Raphael.fn.ball = function (x, y, r, hue) {
    hue = hue || 0;
    return this.set(
        this.ellipse(x, y + r - r / 5, r, r / 2).attr({fill: "rhsb(" + hue + ", 1, .25)-hsb(" + hue + ", 1, .25)", stroke: "none", opacity: 0}),
        this.ellipse(x, y, r, r).attr({fill: "r(.5,.9)hsb(" + hue + ", 1, .75)-hsb(" + hue + ", .5, .25)", stroke: "none"}),
        this.ellipse(x, y, r - r / 5, r - r / 20).attr({stroke: "none", fill: "r(.5,.1)#ccc-#ccc", opacity: 0})
    );
};

var R = Raphael("holder"), x = 310, y = 180, r = 150;
var ball = R.ball(x, y, r, Math.random());

// --- Creatin a custom menu
var menu = Ext.create('Ext.menu.Menu', {
    plain: true
    ,cls: 'myContextMenu'
    ,items: [{
        xtype: 'component'
        ,html: ['<ul id="rect_menu" class="contextMenu">',
            '<li><a href="call_to_js_function">Set aaa</a></li>',
            '<li><a href="call_to_js_function">Set xxx</a></li>',
            '<li><a href="call_to_js_function">Set ccc</a></li>',
            '</ul>'].join('')
    }]
});

// --- Adding menu handler to all ball's elements

var contextMenuHandler = function(e) {
    menu.showAt(e.getXY());
    e.preventDefault();
};

Ext.each(ball, function(raphaelElement) {
    Ext.fly(raphaelElement.node).on({
        contextmenu: contextMenuHandler
    });
});



回答2:


This works good as well, and is under MIT license

https://swisnl.github.io/jQuery-contextMenu//

JsFiddle (example copied from link above)

$(function() {
    $.contextMenu({
        selector: '.context-menu-one', 
        callback: function(key, options) {
            var m = "clicked: " + key;
            window.console && console.log(m) || alert(m); 
        },
        items: {
            "edit": {name: "Edit", icon: "edit"},
            "cut": {name: "Cut", icon: "cut"},
           copy: {name: "Copy", icon: "copy"},
            "paste": {name: "Paste", icon: "paste"},
            "delete": {name: "Delete", icon: "delete"},
            "sep1": "---------",
            "quit": {name: "Quit", icon: function(){
                return 'context-menu-icon context-menu-icon-quit';
            }}
        }
    });

    $('.context-menu-one').on('click', function(e){
        console.log('clicked', this);
    })    
});


来源:https://stackoverflow.com/questions/18023714/how-to-add-context-menu-on-raphael-element

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!