Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R plotly: Display image on hover

I am trying to reproduce this simple plotly graph that shows images on datapoint hover. The code is available here.

However, I am failing to reproduce it. This is what I have so far:

library(htmlwidgets)
library(magrittr)
library(plotly)

js <- "
function(el) {
    var tooltip = Plotly.d3.select('#' + el.id + ' .svg-container')
      .append(\"div\")
      .attr(\"class\", \"my-custom-tooltip\");
  
    el.on('plotly_hover', function(d) {
      var pt = d.points[0];
      // Choose a location (on the data scale) to place the image
      // Here I'm picking the top-left corner of the graph
      var x = pt.xaxis.range[0];
      var y = pt.yaxis.range[1];
      // Transform the data scale to the pixel scale
      var xPixel = pt.xaxis.l2p(x) + pt.xaxis._offset;
      var yPixel = pt.yaxis.l2p(y) + pt.yaxis._offset;
      // Insert the base64 encoded image
      var img = \"<img src='\" +  pt.customdata + \"' width=100>\";
      tooltip.html(img)
        .style(\"position\", \"absolute\")
        .style(\"left\", xPixel + \"px\")
        .style(\"top\", yPixel + \"px\");
      // Fade in the image
      tooltip.transition()
        .duration(300)
        .style(\"opacity\", 1);
    });
  
    el.on('plotly_unhover', function(d) {
      // Fade out the image
      tooltip.transition()
        .duration(500)
        .style(\"opacity\", 0);
    });
};
"

x <- 1:3 
y <- 1:3

artists <- c("Bethoven", "Mozart", "Bach")

image_links <- 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"
)

# hoverinfo = "none" will hide the plotly.js tooltip, but the 
# plotly_hover event will still fire
plot_ly(hoverinfo = "none") %>%
  add_text(x = x, y = y, customdata = image_links, text = artists) %>%
  htmlwidgets::onRender(js)

Nothing happens on hover and the console is throwing the following error:

htmlwidgets.js:261 Uncaught SyntaxError: Unexpected token ';'
    at tryEval (htmlwidgets.js:252:32)
    at htmlwidgets.js:236:24
    at Array.forEach (<anonymous>)
    at forEach (htmlwidgets.js:55:14)
    at evalAndRun (htmlwidgets.js:230:7)
    at htmlwidgets.js:654:11
    at Array.forEach (<anonymous>)
    at forEach (htmlwidgets.js:55:14)
    at htmlwidgets.js:576:7
    at Array.forEach (<anonymous>)

What am I doing wrong here?

Also, would it be possible to display the tooltip (e.g., as defined in text) while displaying the image on hover?

like image 917
mat Avatar asked Oct 20 '25 04:10

mat


1 Answers

The 2.0 release of plotly.js dropped d3 as a bundled dependency, so you'll need to bring that in separately now:

library(htmlwidgets)
library(magrittr)
library(plotly)

x <- 1:3 
y <- 1:3

artists <- c("Bethoven", "Mozart", "Bach")

image_links <- 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"
)

# hoverinfo = "none" will hide the plotly.js tooltip, but the 
# plotly_hover event will still fire
p <- plot_ly(hoverinfo = "none") %>%
  add_text(x = x, y = y, customdata = image_links, text = artists) %>%
  htmlwidgets::onRender(readLines("hover_tooltip.js"))

p$dependencies <- c(p$dependencies, list(d3))
p

And then you'll need you change Plotly.d3 to d3 in the JavaScript:

// hover_tooltip.js
function(el) {
    var tooltip = d3.select('#' + el.id + ' .svg-container')
      .append("div")
      .attr("class", "my-custom-tooltip");

    el.on('plotly_hover', function(d) {
      var pt = d.points[0];
      // Choose a location (on the data scale) to place the image
      // Here I'm picking the top-left corner of the graph
      var x = pt.xaxis.range[0];
      var y = pt.yaxis.range[1];
      // Transform the data scale to the pixel scale
      var xPixel = pt.xaxis.l2p(x) + pt.xaxis._offset;
      var yPixel = pt.yaxis.l2p(y) + pt.yaxis._offset;
      // Insert the base64 encoded image
      var img = "<img src='" +  pt.customdata + "' width=100>";
      tooltip.html(img)
        .style("position", "absolute")
        .style("left", xPixel + "px")
        .style("top", yPixel + "px");
      // Fade in the image
      tooltip.transition()
        .duration(300)
        .style("opacity", 1);
    });

    el.on('plotly_unhover', function(d) {
      // Fade out the image
      tooltip.transition()
        .duration(500)
        .style("opacity", 0);
    });
}
like image 123
Carson Avatar answered Oct 21 '25 16:10

Carson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!