如何创建Konva反应的上下文菜单

问题描述 投票:1回答:2

据我所知没有一个简单的/内置有Konva方法来创建对象上右击上下文菜单。我忙上需要使用上下文菜单的一个项目,所以我觉得还是创建自己的。

不用说,我是相当新的Konva,所以我希望有人在SO可能有更多的经验来帮助我在过去的障碍。

我已创建一个沙箱中,位于HERE

要求是:

  1. 一个对象应该是可拖动。 (我复制的工作示例关闭Konva沙箱)。
  2. 一个对象应显示上下文菜单时,点击右键时。
  3. 上下文菜单应该是动态的,从而允许多个项目,每个项目执行其自己的回调时,点击时。
  4. 一旦已经做出选择,在上下文菜单中应该关闭。

到目前为止,我已经得到大部分是正确的,但我挣扎的事情是:

  1. 我无法弄清楚如何在一个上下文菜单项徘徊,有它突出显示,然后移动到下一个应该被突出显示,并且旧恢复到原来的设置。
  2. 走出上下文菜单中的重画整个对象。我不明白为什么。
  3. 点击一个物品触发两个项目的回调。为什么?我的靶向这是点击的菜单选项,但得到两者兼而有之?
  4. 这一点是更小的错误,更是我不确定如何进行:我将如何防止多个上下文菜单是创建如果用户用鼠标右击对象上多次?从概念上讲我明白,我可以搜索在层(?)与上下文菜单的名称的任何项目,关闭它,但我不知道如何做到这一点。

我将不胜感激任何帮助。提前致谢。

javascript reactjs contextmenu konvajs konvajs-reactjs
2个回答
1
投票

不知道如果我迟到了,但我会用React Portals,那里有一个例子关于它的反应,konva页:https://konvajs.github.io/docs/react/DOM_Portal.html

我叉你的沙箱中如何做到这一点来完成:https://codesandbox.io/s/km0n1x8367


0
投票

没有反应,但纯JS我很害怕,但它照在一些你必须做什么光。

单击粉圆,然后采取选项2,然后单击子选项2。

地区需要更多的工作:

  • 传送通过JSON菜单配置数据
  • 使在类中添加回调的方法
  • 加上隐藏超时,让摇摇欲坠的鼠标手
  • 如何处理隐藏的子菜单,当用户鼠标移开或点击另一个选择
  • 添加揭示和隐藏动画

// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 600, height: 300});

// Add a layer some sample shapes
var layer = new Konva.Layer({draggable: false});
stage.add(layer);

// draw some shapes.
var circle = new Konva.Circle({ x: 80, y: 80, radius: 30, fill: 'Magenta'});
layer.add(circle);

var rect = new Konva.Rect({ x: 80, y: 80, width: 60, height: 40, fill: 'Cyan'});
layer.add(rect);

stage.draw();

// that is the boring bit over - now menu fun

// I decided to set up a plain JS object to define my menu structure - could easily receive from async in JSON format. [Homework #1]
var menuData = { options: [
  {key: 'opt1', text: 'Option 1', callBack: null},
  {key: 'opt2', text: 'Option 2', callBack: null, 
    options: [ 
      {key: 'opt2-1', text: 'Sub 1', callBack: null}, 
      {key: 'opt2-2', text: 'Sub 2', callBack: null} 
   ]
  },
  {key: 'opt3', text: 'Option 3', callBack: null},
  {key: 'opt4', text: 'Option 4', callBack: null}  
]};

// Define a menu 'class' object.
var menu = function(menuData) {

  var optHeight = 20;  // couple of dimension constants. 
  var optWidth = 100;
  var colors = ['white','gold'];
  
  this.options = {}; // prepare an associative list accessible by key - will put key into the shape as the name so we can can get from click event to this entry

  this.menuGroup = new Konva.Group({}); // prepare a canvas group to hold the option rects for this level. Make it accessible externally by this-prefix

  var _this = this;  // put a ref for this-this to overcome this-confusion later. 

  // recursive func to add a menu level and assign its option components.
  var addHost = function(menuData, hostGroup, level, pos){  // params are the data for the level, the parent group, the level counter, and an offset position counter
    var menuHost = new Konva.Group({ visible: false});  // make a canvas group to contain new options
    hostGroup.add(menuHost); // add to the parent group

    // for every option at this level
    for (var i = 0; i < menuData.options.length; i = i + 1 ){
      var option = menuData.options[i]; // get the option into a var for readability

      // Add a rect as the background for the visible option in the menu.
      option.optionRect = new Konva.Rect({x: (level * optWidth), y: (pos + i) * optHeight, width: optWidth, height: optHeight, fill: colors[0], stroke: 'silver', name: option.key });
      option.optionText = new Konva.Text({x: (level * optWidth), y: (pos + i) * optHeight, width: optWidth, height: optHeight, text: ' ' + option.text, listening: false, verticalAlign: 'middle'})
  console.log(option.optionText.height())
      option.optionRect
        .on('mouseover', function(){
          this.fill(colors[1])
          layer.draw();
          })
        .on('mouseleave', function(){
          this.fill(colors[0])
          layer.draw();
          })
      
      // click event listener for the menu option 
      option.optionRect.on('click', function(e){

        var key = this.name(); // get back the key we stashed in the rect so we can get the options object from the lookup list 

        if (_this.options[key] && (typeof _this.options[key].callback == 'function')){ // is we found an option and it has a real function as a callback then call it.
          _this.options[key].callback();
        } 
        else {
          console.log('No callback for ' + key)
        }
        
      })
      menuHost.add(option.optionRect); // better add the rect and text to the canvas or we will not see it
      menuHost.add(option.optionText);       
      
      _this.options[option.key] = option; // stash the option in the lookup list for later retrieval in click handlers.

      // pay attention Bond - if this menu level has a sub-level then we call into this function again.  
      if (option.options){
        
        var optionGroup = addHost(option, menuHost, level + 1, i)  // params 3 & 4 are menu depth and popout depth for positioning the rects. 

        // make an onclick listener to show the sub-options
        option.callback = function(e){
          optionGroup.visible(true);
          layer.draw();
        }        
      }
    }
    return menuHost; // return the konva group 
  } 

  // so - now we can call out addHost function for the top level of the menu and it will recurse as needed down the sub-options.
  var mainGroup = addHost(menuData, this.menuGroup, 0, 0);

  // lets be nice and make a show() method that takes a position x,y too.
  this.show = function(location){
    location.x = location.x - 10;  // little offset to get the group under the mouse
    location.y = location.y - 10;
    
    mainGroup.position(location);
    mainGroup.show(); // notice we do not draw the layer here - leave that to the caller to avoid too much redraw.
  }

  // and if we have a show we better have a hide.
  this.hide = function(){
    mainGroup.hide();
  }
  
  // and a top-level group listener for mouse-out to hide the menu. You might want to put a timer on this [Homework #3]
  mainGroup.on('mouseleave', function(){
    this.hide();
    layer.draw();
  })
  
   
  // end of the menu class object.
  return this;
}


// ok - now we can get our menu data turned into a menu
var theMenu = new menu(menuData);
layer.add(theMenu.menuGroup); // add the returned canvas group to the layer
layer.draw();  // and never forget to draw the layer when it is time!

//
// now we can add some arbitrary callbacks to some of the options.
//
// make a trivial function to pop a message when we click option 1
var helloFunc = function(){
  alert('hello')
}
// make this the callback for opt1 - you can move this inside the menu class object as a setCallback(name, function()) method if you prefer [homework #2] 
theMenu.options['opt1'].callback = helloFunc;

// put a function on sub2 just to show it works.
theMenu.options['opt2-2'].callback = function(){ alert('click on sub-2') };

// and the original reason for this - make it a context menu on a shape.
circle.on('click', function(e){
  theMenu.show({x: e.evt.offsetX, y: e.evt.offsetY});
    layer.draw(); 
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<div id='container1' style="width: 300px, height: 200px; background-color: silver;"></div>
© www.soinside.com 2019 - 2024. All rights reserved.