From e3c69cbd72c662dbe64e22c84856efa2f3e71fd0 Mon Sep 17 00:00:00 2001 From: Florian Breitwieser Date: Mon, 12 Sep 2016 11:00:55 -0400 Subject: [PATCH] Add zooming to Sankey It has the bug that zomming is persistent once it is set. That means if a Sankey is redrawn, it will always have zooming --- R/sankeyNetwork.R | 6 +++-- inst/examples/shiny/server.R | 3 ++- inst/examples/shiny/ui.R | 3 ++- inst/htmlwidgets/sankeyNetwork.js | 37 +++++++++++++++++++++++++------ man/sankeyNetwork.Rd | 5 ++++- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/R/sankeyNetwork.R b/R/sankeyNetwork.R index 839c5d4f..9c3d8538 100644 --- a/R/sankeyNetwork.R +++ b/R/sankeyNetwork.R @@ -41,6 +41,8 @@ #' browser on the client so don't push it too high. #' @param sinksRight boolean. If \code{TRUE}, the last nodes are moved to the #' right border of the plot. +#' @param zoom logical value to enable (\code{TRUE}) or disable (\code{FALSE}) +#' zooming #' #' @examples #' \dontrun{ @@ -76,7 +78,7 @@ sankeyNetwork <- function(Links, Nodes, Source, Target, Value, NodeID, NodeGroup = NodeID, LinkGroup = NULL, units = "", colourScale = JS("d3.scale.category20()"), fontSize = 7, fontFamily = NULL, nodeWidth = 15, nodePadding = 10, margin = NULL, - height = NULL, width = NULL, iterations = 32, sinksRight = TRUE) + height = NULL, width = NULL, iterations = 32, sinksRight = TRUE, zoom = FALSE) { # Check if data is zero indexed check_zero(Links[, Source], Links[, Target]) @@ -132,7 +134,7 @@ sankeyNetwork <- function(Links, Nodes, Source, Target, Value, options = list(NodeID = NodeID, NodeGroup = NodeGroup, LinkGroup = LinkGroup, colourScale = colourScale, fontSize = fontSize, fontFamily = fontFamily, nodeWidth = nodeWidth, nodePadding = nodePadding, units = units, - margin = margin, iterations = iterations, sinksRight = sinksRight) + margin = margin, iterations = iterations, sinksRight = sinksRight, zoom = zoom) # create widget htmlwidgets::createWidget(name = "sankeyNetwork", x = list(links = LinksDF, diff --git a/inst/examples/shiny/server.R b/inst/examples/shiny/server.R index fc93a2e8..01f4e41d 100644 --- a/inst/examples/shiny/server.R +++ b/inst/examples/shiny/server.R @@ -31,7 +31,8 @@ shinyServer(function(input, output) { Energy <- jsonlite::fromJSON(URL) sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source", Target = "target", Value = "value", NodeID = "name", - fontSize = 12, nodeWidth = 30, sinksRight = input$sinksRight) + fontSize = 12, nodeWidth = 30, sinksRight = input$sinksRight, + zoom = input$zoom) }) output$rt <- renderRadialNetwork({ diff --git a/inst/examples/shiny/ui.R b/inst/examples/shiny/ui.R index 297a1012..570a57a6 100644 --- a/inst/examples/shiny/ui.R +++ b/inst/examples/shiny/ui.R @@ -16,9 +16,10 @@ shinyUI(fluidPage( tabPanel("Force Network with Legend & Radius", forceNetworkOutput("forceRadius")), tabPanel("Sankey Network", checkboxInput("sinksRight", "sinksRight", value = TRUE), + checkboxInput("zoom", "zoom", value = FALSE), sankeyNetworkOutput("sankey")), tabPanel("Reingold-Tilford Tree", radialNetworkOutput("rt")) ) ) ) -)) \ No newline at end of file +)) diff --git a/inst/htmlwidgets/sankeyNetwork.js b/inst/htmlwidgets/sankeyNetwork.js index 6b64ed83..8fba38b8 100644 --- a/inst/htmlwidgets/sankeyNetwork.js +++ b/inst/htmlwidgets/sankeyNetwork.js @@ -66,8 +66,11 @@ HTMLWidgets.widget({ var width = el.getBoundingClientRect().width - margin.right - margin.left; var height = el.getBoundingClientRect().height - margin.top - margin.bottom; - var color = eval(options.colourScale); + // set this up even if zoom = F + var zoom = d3.behavior.zoom(); + var color = eval(options.colourScale); + var color_node = function color_node(d){ if (d.group){ return color(d.group.replace(/ .*/, "")); @@ -106,13 +109,33 @@ HTMLWidgets.widget({ .sinksRight(options.sinksRight) .layout(options.iterations); - // select the svg element and remove existing children - d3.select(el).select("svg").selectAll("*").remove(); - // remove any previously set viewBox attribute - d3.select(el).select("svg").attr("viewBox", null); + // select the svg element and remove existing children or previously set viewBox attribute + var svg = d3.select(el).select("svg"); + svg.selectAll("*").remove(); + svg.attr("viewBox", null); + // append g for our container to transform by margin - var svg = d3.select(el).select("svg").append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")");; + svg = svg + .append("g").attr("class","zoom-layer") + .append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");; + + // add zooming if requested + if (options.zoom) { + zoom.on("zoom", redraw) + function redraw() { + d3.select(el).select(".zoom-layer").attr("transform", + "translate(" + d3.event.translate + ")"+ + " scale(" + d3.event.scale + ")"); + } + + d3.select(el).select("svg") + .attr("pointer-events", "all") + .call(zoom); + + } else { + zoom.on("zoom", null); + + } // draw path var path = sankey.link(); diff --git a/man/sankeyNetwork.Rd b/man/sankeyNetwork.Rd index a13f6c26..a71d3ffa 100644 --- a/man/sankeyNetwork.Rd +++ b/man/sankeyNetwork.Rd @@ -12,7 +12,7 @@ sankeyNetwork(Links, Nodes, Source, Target, Value, NodeID, NodeGroup = NodeID, LinkGroup = NULL, units = "", colourScale = JS("d3.scale.category20()"), fontSize = 7, fontFamily = NULL, nodeWidth = 15, nodePadding = 10, margin = NULL, height = NULL, width = NULL, iterations = 32, - sinksRight = TRUE) + sinksRight = TRUE, zoom = FALSE) } \arguments{ \item{Links}{a data frame object with the links between the nodes. It should @@ -74,6 +74,9 @@ browser on the client so don't push it too high.} \item{sinksRight}{boolean. If \code{TRUE}, the last nodes are moved to the right border of the plot.} + +\item{zoom}{logical value to enable (\code{TRUE}) or disable (\code{FALSE}) +zooming} } \description{ Create a D3 JavaScript Sankey diagram