我有一张带有 2 条痕迹的绘图地图。我想在悬停时显示轨迹 1 的图像,并在单击时显示轨迹 2 的视频。由于这篇文章中的答案,悬停效果非常好。对于视频,我使用了此处描述的方法。有点作用。
我感兴趣的升级是:
非常感谢您的任何想法!
下面的玩具示例。数据、函数和 JS 位:
```{r}
library(dplyr)
library(plotly)
library(htmltools)
library(htmlwidgets)
df <- data.frame(x = c(-81.026, -81.025, -81.028), y = c(44.13, 44.14, 44.15),
url = c("https://upload.wikimedia.org/wikipedia/commons/6/6f/Beethoven.jpg",
"https://upload.wikimedia.org/wikipedia/commons/4/47/Croce-Mozart-Detail.jpg",
"https://upload.wikimedia.org/wikipedia/commons/6/6a/Johann_Sebastian_Bach.jpg"))
d3 <- htmltools::htmlDependency(
"d3", "7.3",
src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/"),
script = "d3.min.js"
)
js2 <- '
function(el) {
var tooltip = d3.select("#" + el.id + " .svg-container") /* identify container */
.append("div")
.attr("class", "my-custom-tooltip");
el.on("plotly_hover", function(d) { /* add content on hover */
pt = d.points[0];
console.log(pt);
img = "<img src=\\\"" + pt.customdata + "\\\" width=100>"; /* image in hover */
/* the position of the map on the webpage */
bx = document.querySelector("div.mapboxgl-map").getBoundingClientRect();
var h = window.innerHeight; /* get viewers screen size */
var w = window.innerWidth;
tooltip.html(img) /* calculate % left/right map to screen position */
.style("position", "absolute")
.style("left", (bx.left/w * 100) + "vw")
.style("top", (bx.top/h * 100) + "vh");
tooltip.transition()
.duration(300)
.style("opacity", 1);
})
el.on("plotly_unhover", function(d) { /* collapse image when tooltip collapses */
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
}
'
js3 <- "function(el) {
var container = document.getElementById('video');
el.on('plotly_click', function(d) {
var url = d.points[0].customdata;
renderReactPlayer(container, {url: url, playing: true});
})
}"
react_player <- runpkg::download_files(
"react-player",
"dist/ReactPlayer.standalone.js"
)
```
剧情:
```{r}
video <- tags$div(id = "video", align = "center")
p <- plot_ly(df, lon = ~x, lat = ~y, type = "scattermapbox", mode = "markers", hoverinfo = ~x, customdata = ~url) %>%
add_trace(data = df, lon = ~x + 0.1, lat = ~y, type = "scattermapbox", mode = "markers", hoverinfo = ~x, customdata = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley") %>%
layout(mapbox = list(style = "white-bg", sourcetype = 'raster', zoom = 4,
center = list(lon = -81 ,lat= 44),
layers = list(list(below = 'traces', sourcetype = "raster",
source = list("https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}"))))) %>%
onRender(js2) %>%
onRender(js3)
p$dependencies <- c(p$dependencies, list(d3))
p
# create the HTML page
browsable(tagList(p, video, react_player))
```
编辑
使用答案中的修复程序将整个代码组合在一起。将鼠标悬停在轨迹 1 点上时显示损坏的视频链接...
library(dplyr)
library(plotly)
library(htmltools)
library(htmlwidgets)
df <- data.frame(x = c(-81.026, -81.025, -81.028), y = c(44.13, 44.14, 44.15),
url = c("https://upload.wikimedia.org/wikipedia/commons/6/6f/Beethoven.jpg",
"https://upload.wikimedia.org/wikipedia/commons/4/47/Croce-Mozart-Detail.jpg",
"https://upload.wikimedia.org/wikipedia/commons/6/6a/Johann_Sebastian_Bach.jpg"))
d3 <- htmltools::htmlDependency(
"d3", "7.3",
src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/"),
script = "d3.min.js"
)
js2 <- '
function(el) {
var tooltip = d3.select("#" + el.id + " .svg-container") /* identify container */
.append("div")
.attr("class", "my-custom-tooltip");
el.on("plotly_hover", function(d) { /* add content on hover */
pt = d.points[0];
console.log(pt);
img = "<img src=\\\"" + pt.customdata + "\\\" width=100>"; /* image in hover */
/* the position of the map on the webpage */
bx = document.querySelector("div.mapboxgl-map").getBoundingClientRect();
var h = window.innerHeight; /* get viewers screen size */
var w = window.innerWidth;
tooltip.html(img) /* calculate % left/right map to screen position */
.style("position", "absolute")
.style("left", (bx.left/w * 100) + "vw")
.style("top", (bx.top/h * 100) + "vh");
tooltip.transition()
.duration(300)
.style("opacity", 1);
})
el.on("plotly_unhover", function(d) { /* collapse image when tooltip collapses */
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
}
'
# replacement of `js3`, most mods are taken from `js2`
js3N <- "function(el) {
$('head').append('<style>iframe[id^=\"widget\"] { width: 100%; } </style>');
var container = document.getElementById('video');
el.on('plotly_click', function(d) {
url = d.points[0].customdata; /* changed to global (removed var) */
var h = window.innerHeight;
var w = window.innerWidth;
container.setAttribute( /* calculate % left/right map to screen position */
'style',
'position: absolute; left:' + (bx.left/w * 100) + 'vw; ' +
'top: ' + (bx.top/h * 100) + 'vh;');
$('[id^=widget]').css('opacity', 1); /* opac = 0 stop video; opac = 1 play */
renderReactPlayer(container, {url: url, playing: true});
})
el.on('plotly_doubleclick', function(d) { /* collapse image when container collapses */
renderReactPlayer($('#video')[0], {url: url, playing: false}); /* stop video */
$('[id^=widget]').css('opacity', 0); /* hide video */
});
}"
# replacement of `video` -- removed align = "center"
video <- tags$div(id = "video")
react_player <- runpkg::download_files(
"react-player",
"dist/ReactPlayer.standalone.js"
)
p <- plot_ly(df, lon = ~x, lat = ~y, type = "scattermapbox", mode = "markers",
hoverinfo = ~x, customdata = ~url) %>%
add_trace(data = df, lon = ~x + 0.1, lat = ~y, type = "scattermapbox",
mode = "markers", hoverinfo = ~x,
customdata = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley") %>%
layout(mapbox = list(style = "white-bg", sourcetype = 'raster', zoom = 4,
center = list(lon = -81 ,lat= 44),
layers = list(list(below = 'traces', sourcetype = "raster",
source = list("https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}"))))) %>%
onRender(js2) %>%
# onRender(js3)
onRender(js3N) %>%
config(doubleClickDelay = 1500) #<---- I'm new!!
p$dependencies <- c(p$dependencies, list(d3))
# create the HTML page
browsable(tagList(p, video, react_player))
虽然我最初答案中的推理和解释是正确的,但我已经编写了您的两个不同
onRender
调用的综合版本。区分你是视频还是图像的标准是curveNumber
,你可以按照痕迹来思考。例如,如果您首先调用地图,然后调用点,则这将无法正常工作。 (您必须切换标准 pt.curveNumber === 0
与 pt.curveNumber === 1
。)
这是代码和绘图的调用。
jsC <- "
function(el) {
var tooltip = d3.select('#' + el.id + ' .svg-container') /* identify container */
.append('div')
.attr('class', 'my-custom-tooltip');
$('head').append('<style>iframe[id^=\"widget\"] { width: 100%; } </style>');
var container = document.getElementById('video');
el.on('plotly_hover', function(d) { /* add content on hover */
pt = d.points[0];
var h = window.innerHeight; /* get viewers screen size */
var w = window.innerWidth;
bx = document.querySelector('div.mapboxgl-map').getBoundingClientRect();
if(pt.curveNumber === 0) { /* you have an image; first trace */
/* if hovering on point that contains an image, halt/hide video - playing */
if($('[id^=widget]').length) {
renderReactPlayer($('#video')[0], {url: pt.customdata, playing: false}); /* stop video */
$('[id^=widget]').css('opacity', 0); /* hide video */
}
img = '<img src=\\\'' + pt.customdata + '\\\' width=100>'; /* image in hover */
/* the position of the map on the webpage */
tooltip.html(img) /* calculate % left/right map to screen position */
.style('position', 'absolute')
.style('left', (bx.left/w * 100) + 'vw')
.style('top', (bx.top/h * 100) + 'vh');
tooltip.transition()
.duration(300)
.style('opacity', 1);
} else if (pt.curveNumber === 1) { /* you have a video; second trace */
container.setAttribute( /* calculate % left/right map to screen position */
'style',
'position: absolute; left:' + (bx.left/w * 100) + 'vw; ' +
'top: ' + (bx.top/h * 100) + 'vh;');
$('[id^=widget]').css('opacity', 1); /* opac = 0 stop video; opac = 1 play */
renderReactPlayer(container, {url: pt.customdata, playing: true});
}
});
el.on('plotly_unhover', function(d) { /* collapse image when tooltip collapses */
tooltip.transition()
.duration(500)
.style('opacity', 0);
});
el.on('plotly_doubleclick', function(d) { /* collapse image when container collapses */
renderReactPlayer($('#video')[0], {url: pt.customdata, playing: false}); /* stop video */
$('[id^=widget]').css('opacity', 0); /* hide video */
});
}"
然后只需将两个
onRender
调用替换为一个即可。
p <- plot_ly(df, lon = ~x, lat = ~y, type = "scattermapbox", mode = "markers",
hoverinfo = ~x, customdata = ~url) %>%
add_trace(data = df, lon = ~x + 0.1, lat = ~y, type = "scattermapbox",
mode = "markers", hoverinfo = ~x,
customdata = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley") %>%
layout(mapbox = list(style = "white-bg", sourcetype = 'raster', zoom = 4,
center = list(lon = -81 ,lat= 44),
layers = list(list(below = 'traces', sourcetype = "raster",
source = list("https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}"))))) %>%
# onRender(js2) %>%
# onRender(js3)
# onRender(js3N) %>%
onRender(jsC) %>% #<---- I'm new!!
config(doubleClickDelay = 1500)
如果您遇到任何问题,请告诉我。
答案:
如何使点击视频仅适用于轨迹 2?那么点击轨迹 1 没有任何反应?
您有 3 个标记用于迹线 0,3 个标记用于迹线 1。对于迹线 0,您为 3 个标记提供了 3 个字符串。对于迹线 1,您为 3 个标记提供了一个字符串。如果您只想让第二个标记呈现视频,请修改参数:
customdata = c('', url, '')
如何使视频变小并显示在地图区域内,而不是显示在地图下方?
有些事情必须发生。
你还想让它居中吗? (您在创建视频元素时调用了
align = 'center'
。)我假设您宁愿将其放在与图像相同的位置。在我对对象 js3
的修改中,我使用了 window.innerHeight
等,如 js2
中所示。如果您希望它位于不同的位置,您可以更改我将提供的代码中为 left
和 top
设置的值。
您想要视频的尺寸是多少?在
js3
的新版本中,您将看到 <style>
元素的创建。在此元素中,我将宽度设置为 100%。您可以根据需要更改此值。在代码中,它看起来像这样:
$('head').append('<style>iframe[id^=\"widget\"] { width: 100%; } </style>');
它将继续保持宽高比,因此只需要调整宽度。但是,当您缩小视频时,最终可能会出现一些难看的填充。如果您有一堆填充,您始终可以添加与宽度相匹配的高度调用。例如:
$('head').append('<style>iframe[id^=\"widget\"] { width: 50%; height: 50%} </style>');
要更改大小和位置,您需要修改对象
js3
和 video
。在解决了您的其他问题后,我将回到这个话题。
如何使视频可关闭?目前,除非我单击迹线 1 4
,否则我无法让它消失我添加了
plotly_doubleclick
来关闭视频。
当我使用此功能时,大多数时候我无法足够快地单击鼠标来安抚 Plotly,因此我还添加了
config(doubleClickDelay = 1500)
,这样我双击的速度就不会出现问题。您将看到此函数调用已添加到对象中p
。
此练习后地图似乎不再可缩放... 我无法复制这个问题。如果在这些更改之后它仍然不适合您,请告诉我。
# replacement of `js3`, most mods are taken from `js2`
js3N <- "function(el) {
$('head').append('<style>iframe[id^=\"widget\"] { width: 100%; } </style>');
var container = document.getElementById('video');
el.on('plotly_click', function(d) {
url = d.points[0].customdata; /* changed to global (removed var) */
var h = window.innerHeight;
var w = window.innerWidth;
container.setAttribute( /* calculate % left/right map to screen position */
'style',
'position: absolute; left:' + (bx.left/w * 100) + 'vw; ' +
'top: ' + (bx.top/h * 100) + 'vh;');
$('[id^=widget]').css('opacity', 1); /* opac = 0 stop video; opac = 1 play */
renderReactPlayer(container, {url: url, playing: true});
})
el.on('plotly_doubleclick', function(d) { /* collapse image when container collapses */
renderReactPlayer($('#video')[0], {url: url, playing: false}); /* stop video */
$('[id^=widget]').css('opacity', 0); /* hide video */
});
}"
# replacement of `video` -- removed align = "center"
video <- tags$div(id = "video")
# replacement of `p`, the ONLY changes after `onRender(js2)`
p <- plot_ly(df, lon = ~x, lat = ~y, type = "scattermapbox", mode = "markers",
hoverinfo = ~x, customdata = ~url) %>%
add_trace(data = df, lon = ~x + 0.1, lat = ~y, type = "scattermapbox",
mode = "markers", hoverinfo = ~x,
customdata = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley"
) %>%
layout(mapbox = list(style = "white-bg", sourcetype = 'raster', zoom = 4,
center = list(lon = -81 ,lat= 44),
layers = list(list(below = 'traces', sourcetype = "raster",
source = list("https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}"))))) %>%
onRender(js2) %>%
# onRender(js3)
onRender(js3N) %>% config(doubleClickDelay = 1500) #<---- I'm new!!
如果将其更改为 50%,则结果如下:
$('head').append('<style>iframe[id^=\"widget\"] { width: 50%; height: 50%} </style>');