悬停时的图像和点击时的视频 - Rplotly

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

我有一张带有 2 条痕迹的绘图地图。我想在悬停时显示轨迹 1 的图像,并在单击时显示轨迹 2 的视频。由于这篇文章中的答案,悬停效果非常好。对于视频,我使用了此处描述的方法。有点作用。

我感兴趣的升级是:

  1. 如何使点击视频仅适用于轨迹 2?那么单击轨迹 1 不会执行任何操作?
  2. 如何使视频变小并显示在地图区域内,而不是显示在地图下方?
  3. 如何使视频可关闭?目前我无法让它消失,除非我点击迹线 1 4
  4. 此练习后地图似乎不再可缩放......

非常感谢您的任何想法!

下面的玩具示例。数据、函数和 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))
javascript r plotly interactive
1个回答
1
投票

更新

虽然我最初答案中的推理和解释是正确的,但我已经编写了您的两个不同

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>');

© www.soinside.com 2019 - 2024. All rights reserved.