rshinyshinyjs

How to draw and erase and change the brush size and color?


Colors should appear and change when dragging the slider button but nothing happens, there is just a blank canvas in the right hand side of the screen. I changed some things before it was not even a canvas on the right hand side of the screen but now when there is a canvas nothing happens when I use slider option like color should appear. When I select a color from the dropdown and use slider then the color should also change:

# install.packages(c("shiny", "shinyjs"))

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),
  titlePanel("Drawing App"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("brushSize", "Brush Size", min = 1, max = 20, value = 5),
      selectInput("brushColor", "Brush Color",
                  choices = c("Black", "Red", "Blue", "Green", "Eraser"),
                  selected = "Black")
    ),
    mainPanel(
      div(id = "canvas", style = "border:1px solid #000; height: 400px;"),
      tags$script('
        var isDrawing = false;
        var context;

        $(document).ready(function(){
          var canvas = document.getElementById("canvas");
          context = canvas.getContext("2d");

          $("#canvas").mousedown(function(e){
            isDrawing = true;
            context.beginPath();
            context.moveTo(e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop);
          });

          $("#canvas").mousemove(function(e){
            if(isDrawing){
              var x = e.pageX - canvas.offsetLeft;
              var y = e.pageY - canvas.offsetTop;
              var color = $("#brushColor").val();
              var size = $("#brushSize").val();
              context.lineWidth = size;
              if(color === "Eraser"){
                context.strokeStyle = "#FFF"; // White color for eraser
              } else {
                context.strokeStyle = color.toLowerCase();
              }
              context.lineTo(x, y);
              context.stroke();
            }
          });

          $("#canvas").mouseup(function(){
            isDrawing = false;
          });

          $("#canvas").mouseleave(function(){
            isDrawing = false;
          });

          Shiny.setInputValue("canvasData", canvas.toDataURL());
        });

        Shiny.addCustomMessageHandler("clearCanvas", function(message) {
          context.clearRect(0, 0, canvas.width, canvas.height);
        });
      ')
    )
  )
)

server <- function(input, output, session) {
  observe({
    brush_color <- switch(input$brushColor,
                          "Black" = "black",
                          "Red" = "red",
                          "Blue" = "blue",
                          "Green" = "green",
                          "Eraser" = "white")

    shinyjs::runjs(paste0('context.strokeStyle = "', brush_color, '";'))
  })

  observeEvent(input$canvasData, {
    session$sendCustomMessage("clearCanvas", NULL)
    shinyjs::runjs(paste0('var img = new Image(); img.src = "', input$canvasData, '"; context.drawImage(img, 0, 0);'))
  })
}

shinyApp(ui, server)

Solution

  • Note that:

    Then it will work.

    enter image description here

    library(shiny)
    library(shinyjs)
    
    ui <- fluidPage(
        useShinyjs(),
        titlePanel("Drawing App"),
        sidebarLayout(
            sidebarPanel(
                sliderInput("brushSize", "Brush Size", min = 1, max = 20, value = 5),
                selectInput("brushColor", "Brush Color",
                            choices = c("Black", "Red", "Blue", "Green", "Eraser"),
                            selected = "Black")
            ),
            mainPanel(
                tags$canvas(
                    height="400px", id="canvas", style="border: 1px solid #000;"
                ),
                tags$script(HTML('
            var isDrawing = false;
            var context;
    
            $(document).on("shiny:connected", function(){
              var canvas = document.getElementById("canvas");
              canvas.setAttribute("width", canvas.parentNode.offsetWidth);
              canvas.setAttribute("height", canvas.parentNode.offsetHeight);
              context = canvas.getContext("2d");
              var bounds = canvas.getBoundingClientRect();
    
              $("#canvas").mousedown(function(e){
                isDrawing = true;
                context.beginPath();
                context.moveTo(e.pageX - bounds.left, e.pageY - bounds.top);
              });
    
              $("#canvas").mousemove(function(e){
                if(isDrawing){
                  var x = e.pageX - bounds.left;
                  var y = e.pageY - bounds.top;
                  var color = $("#brushColor").val();
                  var size = $("#brushSize").val();
                  context.lineWidth = size;
                  if(color === "Eraser"){
                    context.strokeStyle = "#FFF"; // White color for eraser
                  } else {
                    context.strokeStyle = color.toLowerCase();
                  }
                  context.lineTo(x, y);
                  context.stroke();
                }
              });
    
              $("#canvas").mouseup(function(){
                isDrawing = false;
              });
    
              $("#canvas").mouseleave(function(){
                isDrawing = false;
              });
    
              Shiny.setInputValue("canvasData", canvas.toDataURL());
            });
    
            Shiny.addCustomMessageHandler("clearCanvas", function(message) {
              context.clearRect(0, 0, canvas.width, canvas.height);
            });
          '))
            )
        )
    )
    
    server <- function(input, output, session) {
        observe({
            brush_color <- switch(input$brushColor,
                                  "Black" = "black",
                                  "Red" = "red",
                                  "Blue" = "blue",
                                  "Green" = "green",
                                  "Eraser" = "white")
            
            shinyjs::runjs(paste0('context.strokeStyle = "', brush_color, '";'))
        })
        
        observeEvent(input$canvasData, {
            session$sendCustomMessage("clearCanvas", NULL)
            shinyjs::runjs(paste0('var img = new Image(); img.src = "', input$canvasData, '"; context.drawImage(img, 0, 0);'))
        })
    }
    
    shinyApp(ui, server)