【问题标题】:Plotly: double-clicking on empty space in a zoomed-in plot selects new point(s) as it resets axesPlotly:双击放大图中的空白区域会在重置轴时选择新点
【发布时间】:2020-08-29 18:54:31
【问题描述】:

我有一个点密集聚集的散点图。放大其中一些后,双击空白区域会重置轴,这就是我想要的。但在很多情况下,它也会选择一个新点——这不是我想要的。毕竟,我双击了空白区域。我无意选择一个新的点。

问题似乎是双重的。首先,each double click is also registered as a single click。其次,单击是在轴重置后坐标处注册的——而不是我双击时实际看到的坐标。并且轴重置后坐标映射到一个绘图点,即使我在放大时单击空白区域。我该如何解决这个问题?

这是一个最小的插图。 Plotly 图是从 R 生成的,但这似乎并不重要:

library(plotly)
x <- c(rnorm(3000, 0, 3), rnorm(1000, 0, 0.2))
y <- c(rnorm(3000, 0, 3), rnorm(1000, 0, 0.2))
groups <- rep(c("a", "b", "c", "d"), 1000)
myData <- highlight_key( data.frame(x, y, groups), ~groups )
myPlot <- plot_ly(
  x = ~x, y = ~y,
  color = ~groups,
  data  = myData)
highlight(myPlot, color = "red")

This animated GIF 显示了代码创建的图形,也说明了问题。

问题是noted before。但是我似乎无法通过在双击时抢占默认的点击选择功能或使用任何其他策略来解决它。我尝试过的一些事情:

  1. 触发plotly_doubleclick 事件后,更改存储的 JSON 数据,使 x > highlight > on 为空。然后用Plotly.newPlot()重绘。

  2. 触发plotly_doubleclick 事件后,使用remove.listener() 禁用plotly_click 事件。但是等到plotly_doubleclick被触发时,这个策略似乎已经来不及了:单击(plotly_click)事件已经被触发了。

  3. 更改布局 > 排序从“首先跟踪”到“优先布局”

  4. 当检测到双击时,将plotly_click 事件处理程序告诉return false。 (我使用this method 使plotly_click 事件处理程序检测激活它的单击是否是双击的一部分。)此策略可能适用于单击图例,但它似乎不适用于单击自己绘制。

这些都不起作用。但我认为必须有一个解决方案——有吗?

【问题讨论】:

    标签: plotly mouseevent r-plotly plotly.js


    【解决方案1】:

    有一个解决方案。它需要 (a) 覆盖默认的单击行为,以及 (b) 补充默认的双击行为。在这两种情况下,我们都需要编写自定义事件处理程序。

    似乎人们倾向于通过引入一些延迟来区分单击和双击,以确保任何给定的单击都不是双击中的第一次或最后一次。这是合理的,但是当在这样的应用程序中使用时,延迟是显着的:单击绘制的点后,在该点被突出显示之前有一个明显的延迟。出现延迟是因为单击 (plotly_click) 事件处理程序正在等待确保触发它的单击不是双击的一部分。

    幸运的是,我们不需要在此应用程序中引入这种延迟。关键是要认识到完全区分单击和双击是不必要的。我们只需要确保触发plotly_click 的点击不是双击中的第二次点击。为什么我们只需要检查这种情况,我不确定。但这已经足够了,我们可以检查这种情况,而不会在突出显示过程中引入任何明显的延迟。

    这是完成这项工作的代码。在 R 中:

    library(plotly)
    x <- c(rnorm(3000, 0, 3), rnorm(1000, 0, 0.2))
    y <- c(rnorm(3000, 0, 3), rnorm(1000, 0, 0.2))
    groups <- rep(c("a", "b", "c", "d"), 1000)
    myData <- data.frame(x, y, groups)
    myPlot <- plot_ly(
      x = ~x, y = ~y,
      color = ~groups,
      data  = myData)
    myPlot$elementId <- "myPlot"
    myPlot <- highlight(myPlot, on = NULL, off = "plotly_doubleclick") 
    onRender(myPlot, readLines("onRender.js"))
    

    “onRender.js”在哪里

    function singleClickHandler (data, el, COLORS_TRACE, OPACITY_START, OPACITY_DIM) {
      let t0 = Date.now();
    
      // If the triggering click wasn't the second click in a double click...
      if ((t0 - doubleClickTime) > interval) {
        highlightTrace(data, el, COLORS_TRACE, OPACITY_START, OPACITY_DIM);
      }
    }
    
    
    function highlightTrace (data, el, OPACITY_START, OPACITY_DIM) {
      // We want clicking on a point to "highlight" that point and all other  
      // points in the trace -- by dimming the points in all -other- traces.
    
      const numTraces = el.data.length;              // total # of traces in plot
      const traceNum  = data.points[0].curveNumber;  // number of clicked trace
    
      // Initialize array with one element for each trace
      let traceOpacity = new Array(numTraces).fill().map( () => OPACITY_DIM );
    
      // Set only the clicked-on trace to have normal (relatively high) opacity
      traceOpacity[traceNum] = OPACITY_START;
    
      // Restyle
      Plotly.restyle("myPlot", { "marker.opacity": traceOpacity } );
    }
    
    
    function onRender (el) {
    
      // Get opacity of first mark in first trace when figure is first displayed
      const OPACITY_START = el._fullData[0].marker.opacity;
      const OPACITY_DIM   = 0.2;
    
      // Set timing
      interval = 1000;  // two clicks within 1 second (1000 ms) is a double click
      doubleClickTime = 0;
    
      // Wrap the singleClickHandler() event handler in onSingleClick(). We do 
      // this so we can pass both event info ("data") and other objects to 
      // singleClickHandler(). 
      var onSingleClick = (data) => singleClickHandler(data, el, OPACITY_START, OPACITY_DIM);
      el.on('plotly_click', onSingleClick);
    
      el.on('plotly_doubleclick', function (d) {      
        doubleClickTime = Date.now();    
        Plotly.restyle("myPlot", { "marker.opacity": OPACITY_START } );
      });
    }
    
    
    onRender  
    
    

    【讨论】:

      猜你喜欢
      • 2013-07-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      • 1970-01-01
      相关资源
      最近更新 更多