Plotly:双击放大图中的空白 space 会在重置轴时选择新点
Plotly: double-clicking on empty space in a zoomed-in plot selects new point(s) as it resets axes
我有一个散点图,其中点密集地聚集在一起。放大其中一些后,双击空 space 重置轴,这就是我想要的。但在许多情况下,它也 select 是一个新点——这不是我想要的。毕竟我双击空space。我无意 select 提出新观点。
问题似乎是双重的。首先,each double click is also registered as a single click。其次,单击是在 post-axis-reset 坐标处注册的——而不是我双击时实际看到的坐标。 post-axis-reset 坐标映射到标绘点,即使我在放大时单击空 space。我该如何解决这个问题?
这是一个最小的插图。 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。但我似乎无法通过在双击时抢占默认的点击 select 功能或使用任何其他策略来解决它。我尝试过的一些事情:
触发plotly_doubleclick
事件后,更改存储的JSON数据,使x>highlight>on为空。然后用Plotly.newPlot()
.
重新绘制
触发plotly_doubleclick
事件后,使用remove.listener()
禁用plotly_click
事件。但是当 plotly_doubleclick
被触发时,这个策略似乎已经来不及起作用了:单击 (plotly_click
) 事件已经被触发了。
更改布局 > 排序从 "traces first" 到 "layout first"
检测到双击时将 plotly_click
事件处理程序通知 return false
。 (我使用 this method 让 plotly_click
事件处理程序检测激活它的点击是否是双击的一部分。)这个策略可能适用于点击图例,但它似乎不起作用点击情节本身。
None 这行得通。但我认为一定有解决办法——有吗?
有解决办法。它需要 (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
我有一个散点图,其中点密集地聚集在一起。放大其中一些后,双击空 space 重置轴,这就是我想要的。但在许多情况下,它也 select 是一个新点——这不是我想要的。毕竟我双击空space。我无意 select 提出新观点。
问题似乎是双重的。首先,each double click is also registered as a single click。其次,单击是在 post-axis-reset 坐标处注册的——而不是我双击时实际看到的坐标。 post-axis-reset 坐标映射到标绘点,即使我在放大时单击空 space。我该如何解决这个问题?
这是一个最小的插图。 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。但我似乎无法通过在双击时抢占默认的点击 select 功能或使用任何其他策略来解决它。我尝试过的一些事情:
触发
plotly_doubleclick
事件后,更改存储的JSON数据,使x>highlight>on为空。然后用Plotly.newPlot()
. 重新绘制
触发
plotly_doubleclick
事件后,使用remove.listener()
禁用plotly_click
事件。但是当plotly_doubleclick
被触发时,这个策略似乎已经来不及起作用了:单击 (plotly_click
) 事件已经被触发了。更改布局 > 排序从 "traces first" 到 "layout first"
检测到双击时将
plotly_click
事件处理程序通知return false
。 (我使用 this method 让plotly_click
事件处理程序检测激活它的点击是否是双击的一部分。)这个策略可能适用于点击图例,但它似乎不起作用点击情节本身。
None 这行得通。但我认为一定有解决办法——有吗?
有解决办法。它需要 (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