在 Shiny 应用程序中处理图像映射点击
Handling image map clicks in a Shiny app
我正在构建一个闪亮的应用程序,其中 selectInput
功能将以传统的 HTML select 下拉菜单和可点击的图像地图的形式呈现。我目前的策略是 link 将图像映射多边形返回到添加了 URL 参数的应用程序,解析 URL,然后更新 select。这当然每次都会重置应用程序,这还可以,但不是很好,而且 UI 闪烁不是很优雅。
我的问题是:
- 是否有更好的方法来捕获图像点击并更新输入?
- 如果我继续我的解决方案,是否可以延迟
selectInput
上的渲染,以便在解析 URL 之后才显示它,这样它就不会显示默认 selection 一会儿,然后闪烁到参数化的 selection?
这是一个演示:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("letter_select",
"Pick a letter:",
choices = c('A' = 1, 'B' = 2, 'C' = 3, 'D' = 4))
),
mainPanel(
h3('Or click a letter'),
img(src = 'testpattern.png', usemap = '#image-map'),
HTML('
<map name="image-map">
<area target="_self" title="A" href="?letter=1" coords=0,0,50,0,50,50,0,50" shape="poly">
<area target="_self" title="B" href="?letter=2" coords=50,0,100,0,100,50,50,50" shape="poly">
<area target="_self" title="C" href="?letter=3" coords=0,50,50,50,50,100,0,100" shape="poly">
<area target="_self" title="D" href="?letter=4" coords=50,50,100,50,100,100,50,100" shape="poly">
</map>
')
)
)
)
server <- function(input, output, session) {
observe({
query <- parseQueryString(session$clientData$url_search)
if (!is.null(query[['letter']])) {
updateSelectInput(session, 'letter_select', selected = query[['letter']])
}
})
}
shinyApp(ui = ui, server = server)
图像位于“www/testpattern.png”,如下所示。
在整个页面重新加载时,维护状态和防止闪烁将变得很困难。最好的解决方法可能是捕获 javascript 中的事件并更新应用程序状态。接下来是关于这可能如何工作的非常粗略的概念证明。首先,我们抽象出一个图像映射的思想
imageMap <- function(inputId, imgsrc, opts) {
areas <- lapply(names(opts), function(n)
shiny::tags$area(title=n, coords=opts[[n]],
href="#", shape="poly"))
js <- paste0("$(document).on('click', 'map area', function(evt) {
evt.preventDefault();
var val = evt.target.title;
Shiny.onInputChange('", inputId, "', val);})")
list(
shiny::tags$img(src=imgsrc, usemap=paste0("#", inputId),
shiny::tags$head(tags$script(shiny::HTML(js)))),
shiny::tags$map(name=inputId, areas))
}
这将从我们传入的 URL 和选项列表中渲染图像和地图数据。我们添加一点 javascript 来捕获图像地图上的点击事件。例如
imgsrc <- "https://i.stack.imgur.com/C5aoV.png"
mapopts <- list(A="0,0,50,0,50,50,0,50",
B="50,0,100,0,100,50,50,50",
C="0,50,50,50,50,100,0,100",
D ="50,50,100,50,100,100,50,100")
imageMap("map1", imgsrc, mapopts)
我们可以在 UI
中使用它
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("letter_select",
"Pick a letter:",
choices = c('A', 'B', 'C', 'D'))
),
mainPanel(
h3('Or click a letter'),
imageMap("map1", imgsrc , mapopts)
)
)
)
现在我们可以在服务器上监听事件,并更改 select 输入
server <- function(input, output, session) {
observeEvent(input$map1, {
updateSelectInput(session, "letter_select", selected=input$map1)
})
}
现在你可以 运行
shinyApp(ui = ui, server = server)
您会看到,当您单击图像中的字母时,select 输入的值将更改以匹配。
我正在构建一个闪亮的应用程序,其中 selectInput
功能将以传统的 HTML select 下拉菜单和可点击的图像地图的形式呈现。我目前的策略是 link 将图像映射多边形返回到添加了 URL 参数的应用程序,解析 URL,然后更新 select。这当然每次都会重置应用程序,这还可以,但不是很好,而且 UI 闪烁不是很优雅。
我的问题是:
- 是否有更好的方法来捕获图像点击并更新输入?
- 如果我继续我的解决方案,是否可以延迟
selectInput
上的渲染,以便在解析 URL 之后才显示它,这样它就不会显示默认 selection 一会儿,然后闪烁到参数化的 selection?
这是一个演示:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("letter_select",
"Pick a letter:",
choices = c('A' = 1, 'B' = 2, 'C' = 3, 'D' = 4))
),
mainPanel(
h3('Or click a letter'),
img(src = 'testpattern.png', usemap = '#image-map'),
HTML('
<map name="image-map">
<area target="_self" title="A" href="?letter=1" coords=0,0,50,0,50,50,0,50" shape="poly">
<area target="_self" title="B" href="?letter=2" coords=50,0,100,0,100,50,50,50" shape="poly">
<area target="_self" title="C" href="?letter=3" coords=0,50,50,50,50,100,0,100" shape="poly">
<area target="_self" title="D" href="?letter=4" coords=50,50,100,50,100,100,50,100" shape="poly">
</map>
')
)
)
)
server <- function(input, output, session) {
observe({
query <- parseQueryString(session$clientData$url_search)
if (!is.null(query[['letter']])) {
updateSelectInput(session, 'letter_select', selected = query[['letter']])
}
})
}
shinyApp(ui = ui, server = server)
图像位于“www/testpattern.png”,如下所示。
在整个页面重新加载时,维护状态和防止闪烁将变得很困难。最好的解决方法可能是捕获 javascript 中的事件并更新应用程序状态。接下来是关于这可能如何工作的非常粗略的概念证明。首先,我们抽象出一个图像映射的思想
imageMap <- function(inputId, imgsrc, opts) {
areas <- lapply(names(opts), function(n)
shiny::tags$area(title=n, coords=opts[[n]],
href="#", shape="poly"))
js <- paste0("$(document).on('click', 'map area', function(evt) {
evt.preventDefault();
var val = evt.target.title;
Shiny.onInputChange('", inputId, "', val);})")
list(
shiny::tags$img(src=imgsrc, usemap=paste0("#", inputId),
shiny::tags$head(tags$script(shiny::HTML(js)))),
shiny::tags$map(name=inputId, areas))
}
这将从我们传入的 URL 和选项列表中渲染图像和地图数据。我们添加一点 javascript 来捕获图像地图上的点击事件。例如
imgsrc <- "https://i.stack.imgur.com/C5aoV.png"
mapopts <- list(A="0,0,50,0,50,50,0,50",
B="50,0,100,0,100,50,50,50",
C="0,50,50,50,50,100,0,100",
D ="50,50,100,50,100,100,50,100")
imageMap("map1", imgsrc, mapopts)
我们可以在 UI
中使用它ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("letter_select",
"Pick a letter:",
choices = c('A', 'B', 'C', 'D'))
),
mainPanel(
h3('Or click a letter'),
imageMap("map1", imgsrc , mapopts)
)
)
)
现在我们可以在服务器上监听事件,并更改 select 输入
server <- function(input, output, session) {
observeEvent(input$map1, {
updateSelectInput(session, "letter_select", selected=input$map1)
})
}
现在你可以 运行
shinyApp(ui = ui, server = server)
您会看到,当您单击图像中的字母时,select 输入的值将更改以匹配。