图像未使用 Shiny renderImage 在 Canvas 内渲染

Image not rendering inside Canvas using Shiny renderImage

我正在尝试在 Shiny 应用程序中以交互方式使用 Fabric.js。 Shiny 应该渲染图像,然后用户将通过 Fabric 修改它。然后,Shiny 将该图像读回服务器并进一步处理它。但是,我现在无法通过第一步!

在下面的代码中,plot/png 显示在 canvas 下方,但不在其内部。我在 javascript 中引用图像 ID 的方式有误吗?

可能是因为 renderImage 将图像放在 div?

代码如下:

library(shiny)

js <- "
$(document).ready(function () {
var canvas = new fabric.Canvas('drawarea'); 

var imgElement = document.getElementById('myimage');
var imgInstance = new fabric.Image(imgElement, {
  left: 100,
  top: 100,
  angle: 0,
  opacity: 0.75,
  width:300,
  height:300
});
canvas.add(imgInstance);   

});
"

ui <- fluidPage(
  tags$head(tags$script(HTML(js))),
  tags$head(tags$script(src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js", type="text/javascript")),
  tags$head(
    tags$style(HTML("
   canvas {
    border: 1px solid #999;
   }
    myimage{display:none;}

    "))
  ),
  
  titlePanel("Fabric Demo"),
  sidebarLayout(
    sidebarPanel(
      h4("Side")
    ),
    mainPanel(
      
      HTML('
           <canvas width="800" height="800" id="drawarea" style="border: 1px solid red;float: right">     </canvas>
           '),
      plotOutput("div.for.image")
    )
  )
)


server <- function(input, output, session) {
 
   output$div.for.image <- renderImage({
     
    outfile <- tempfile(fileext='.png')
    
    # Generate a png
    png(outfile, width=400, height=400)
    hist(rnorm(20))
    dev.off()
    
    # Return a list
    list(src = outfile,
         alt = "This is alternate text",
         id = "myimage")
  }, deleteFile = TRUE)
  
    }

shinyApp(ui=ui,server=server)

您必须在 Shiny 插入图像后触发 JavaScript 代码。现在,您将在文档准备就绪后立即重新启动代码。

我将使用 shinyjs 来完成此任务并使用 runjsdelay 函数,以等待包含图像的列表返回并在 [=26= 中可见].

如果你想隐藏 Shiny 渲染的图像,你需要在 css 中添加 #(如 #myimage

完整代码:

library(shiny)
library(shinyjs)

js <- "
$(document).ready(function () {
var canvas = new fabric.Canvas('drawarea'); 

var imgElement = document.getElementById('myimage');
var imgInstance = new fabric.Image(imgElement, {
  left: 100,
  top: 100,
  angle: 0,
  opacity: 0.75,
  width:300,
  height:300
});
canvas.add(imgInstance);   

});
"

# ui #########
ui <- fluidPage(
  useShinyjs(),
  tags$head(tags$script(src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js", type="text/javascript")),
  tags$head(
    tags$style(HTML("canvas {border: 1px solid #999;}
    #myimage{display:none;}"))
  ),
  
  titlePanel("Fabric Demo"),
  sidebarLayout(
    sidebarPanel(
      h4("Side")
    ),
    mainPanel(
      HTML('<canvas width="800" height="800" id="drawarea" style="border: 1px solid red;float: right"></canvas>'),
      plotOutput("div.for.image")
    )
  )
)

## server #######
server <- function(input, output, session) {
  
  output$div.for.image <- renderImage({
    
    outfile <- tempfile(fileext='.png')
    
    # Generate a png
    png(outfile, width=400, height=400)
    hist(rnorm(20))
    dev.off()
    
    delay(200, runjs(HTML(js)))
    
    # Return a list
    list(src = outfile,
         alt = "This is alternate text",
         id = "myimage")
  }, deleteFile = TRUE)
}

shinyApp(ui=ui,server=server)

您可以使用 shiny:value JavaScript 事件,该事件在呈现输出时触发。

library(shiny)

js <- "
$(document).on('shiny:value', function(evt) {
  if(evt.name === 'divForImage') {
    setTimeout(function(){
      var canvas = new fabric.Canvas('drawarea'); 
      var imgElement = document.getElementById('myimage');
      var imgInstance = new fabric.Image(imgElement, {
        left: 100,
        top: 100,
        angle: 0,
        opacity: 0.75,
        width: 300,
        height: 300
      });
      canvas.add(imgInstance);
    }, 0);
  }
});
"

# ui #########
ui <- fluidPage(
  tags$head(
    tags$script(
      src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js"
    ),
    tags$script(HTML(js)),
    tags$style(HTML(
      "canvas {border: 1px solid #999;}
       #myimage{display:none;}"))
  ),
  
  titlePanel("Fabric Demo"),
  sidebarLayout(
    sidebarPanel(
      h4("Side")
    ),
    mainPanel(
      tags$canvas(
        width="800", height="800", id="drawarea", style="border: 1px solid red; float: right;"
      ),
      imageOutput("divForImage")
    )
  )
)

## server #######
server <- function(input, output, session) {
  
  output[["divForImage"]] <- renderImage({
    
    outfile <- tempfile(fileext='.png')
    
    # Generate a png
    png(outfile, width=400, height=400)
    hist(rnorm(20))
    dev.off()
    
    # Return a list
    list(src = outfile,
         alt = "This is alternate text",
         id = "myimage")
  }, deleteFile = TRUE)
}

shinyApp(ui=ui, server=server)

注意:不要在 ID 中使用句点,这会导致一些问题。