Title: | An Interactive HTML Image Editing Tool |
---|---|
Description: | An interactive image editing tool that can be added as part of the HTML in Shiny, R markdown or any type of HTML document. Often times, plots, photos are embedded in the web application/file. 'drawer' can take screenshots of these image-like elements, or any part of the HTML document and send to an image editing space called 'canvas' to allow users immediately edit the screenshot(s) within the same document. Users can quickly combine, compare different screenshots, upload their own images and maybe make a scientific figure. |
Authors: | Le Zhang [aut, cre] |
Maintainer: | Le Zhang <[email protected]> |
License: | GPL (>= 3) |
Version: | 0.2.0.99 |
Built: | 2024-10-27 03:03:50 UTC |
Source: | https://github.com/lz100/drawer |
use this function on Shiny UI or R markdown to create the image editing area.
canvas( canvasID, title = "drawer", height = "100vh", width = "100%", logo_src = "drawer/img/drawer.png", log_link = "https://github.com/lz100/drawer", on_start = TRUE, rmarkdown = FALSE )
canvas( canvasID, title = "drawer", height = "100vh", width = "100%", logo_src = "drawer/img/drawer.png", log_link = "https://github.com/lz100/drawer", on_start = TRUE, rmarkdown = FALSE )
canvasID |
string, an unique HTML ID |
title |
string, title of the canvas |
height |
string, css value of initial height of the canvas, like "100vh" for full height current window, "50vh" for half. |
width |
string, css value of initial width of the canvas |
logo_src |
string, link of an image you want to display as logo on the top left |
log_link |
string, a link, when the logo is clicked, where should it jump to |
on_start |
|
rmarkdown |
bool, are you using inside R markdown? If yes, drawer will copy
all image icons that required by the canvas to current directory to |
If you are not working in Shiny or R markdown, you need to add the required full "Bootstrap3" javascript and CSS + latest "jquery" dependencies by yourself.
There are two options for canvas height and width:
dynamic CSS units like "100vh" (viewpoint height), "100vw" (view point width), or "100%" for both. This kind of units adapt to all kinds of user screen settings.
fixed unit, px
(pixels). This does not change across users, but fixes
the on_start
problem (read below).
height
, css style vh
is safer than %
is not safe, unless the parent
has some defined height, "%" will work. Otherwise, if the parent height is "auto"
or not defined, and you choose "100%", canvas will still have 0 height.
Width usually does not have this problem. As long as an element is displayed, it has some width.
on_start
This argument specify if you want to initiate the canvas when the document is loaded.
If TRUE
, then when the document loading is done, start the canvas. The problem
is if you set the height to be "vh" (view height) units and if the canvas is
hidden, like in a different tab and not displayed on start, the view height is
0, because it is hidden on another tab (display property is none
), so it
will cause the canvas cannot be initiated properly.
The solution is to bind the initiation with a clicking event, like on a tab or a button.
For example, make a button on the second tab and bind on_start
to that button:
on_start = "#buttonID"
. Then when users click on that button, canvas initiate.
Remember this is a Jquery CSS selector, which means you need to append "#" in
front your button ID.
If you want to do it automatically, like clicking on a certain tab, some CSS knowledge may required. For example, in Shiny, you can use shiny::tabsetPanel to create a tab panel.
tabsetPanel(id = "tabs", tabPanel("Tab A", value = "A", ...), tabPanel("Tab B", value = "B", ...), ... )
Then, bind to it canvas(on_start = '#tabs li a[data-value="B"]', ...)
.
This means we are selecting the element with ID "tabs", which is the main tabsetPanel
ID, then a list item (li
) which is the tab titles you see on UI, and finally, the
link jump to tab B, (a[data-value="B"]
). See examples for a real case.
Another way to fix it is by given the height
and width
a fixed pixel unit:
canvas( canvasID = "canvas_f", height = "900px", width = "1500px" )
You can drag your own images to the canvas. Support major image formats, like "jpg", "png", "svg", "gif", "webp", "bmp". Moving images like "gif", "webp" will be animated on left side preview, but will not move on canvas.
a HTML component to be added to a Shiny app or document
# basic usage if(interactive()){ library(shiny) ui <- fluidPage( h3("Try to drag pictures locally to canvas"), canvas("canvas_a") ) server <- function(input, output, session) { } shinyApp(ui, server) } # multiple canvas on a page if(interactive()){ library(shiny) ui <- fluidPage( h3("multiple canvas on a page"), p("They are independent"), p("Dragging from one canvas to another is not supported currently"), column(6, canvas("canvas_left")), column(6, canvas("canvas_right")) ) server <- function(input, output, session) { } shinyApp(ui, server) } # with capture buttons buttons if(interactive()){ library(shiny) library(ggplot2) ui <- fluidPage( fluidRow( id = "new_row", column( 6, h3("this is a title"), column(6, tags$label("plot 1"), plotOutput("plot_1")), column(6, tags$label("plot 2"), plotOutput("plot_2")), ), column( 6, h2("To canvas buttons"), h4("pure button with `toCanvasBtn`"), toCanvasBtn( dom = "plot_1", label = "capture plot 1", canvasID = "canvas_b" ), br(), toCanvasBtn( dom = "capture_button", label = "capture this button itself", canvasID = "canvas_b", id = "capture_button" ), br(), toCanvasBtn( dom = "#new_row .col-sm-6:first-of-type", label = "complex selector to select left column", canvasID = "canvas_b", isID = FALSE ), br(), h4("button text input for any part of document with `toCanvasBtn`"), toCanvasTextBtn( label = "try #plot_2 to for plot 2 or other selector", canvasID = "canvas_b", text_value = "#plot_2" ) ) ), canvas("canvas_b") ) server <- function(input, output, session) { output$plot_1 <- renderPlot({ ggplot(mpg, aes(cty, hwy)) + geom_count(col="tomato3", show.legend=F) }) output$plot_2 <- renderPlot({ ggplot(mpg, aes(cty, hwy)) + geom_point() }) } shinyApp(ui, server) } # start canvas as hidden, initiate later in tab panels if(interactive()){ library(shiny) ui <- fluidPage( tabsetPanel( id = "tabs", tabPanel( "Home page", value = "tab_1", h4("Content on home page ...."), p("Canvas is hidden on start, go to other tabs") ), tabPanel( "Canvas C", value = "tab_2", markdown( ' # canvas hidden on start In this example, you will see if the canvas is hidden, not on the first tab in a `tabsetPanel`, or other similar UI where you do not see canvas on start. Then, the canvas cannot be initiate properly using the default height value (100vh). Using the dynamic computed CSS height like "100%", or "100vh" with "hidden" (display = none) elements give the height of `0` on start. So, you **should not see the canvas** on this tab, but a broken structure and no canvas grid. To fix it, either give it a fixed `height` and `width` pixel unit, like - `height = "800px"`, `width = "1500px"` or bind the initiation event to a click of a button, the tab title or any other element you specify with the `on_start` argument. See the example code and watch how we do it in "canvas D-F". ' ), canvas(canvasID = "canvas_c") ), tabPanel( "Canvas D", value = "tab_3", h4("Initiate canvas by a button"), actionButton("start_canvas", "Start Canvas C"), canvas( canvasID = "canvas_d", on_start = "#start_canvas" ) ), tabPanel( "Canvas E", value = "tab_4", h4("Initiate canvas by clicking tab title"), p("Canvas initiate when first time users come to this tab"), canvas( canvasID = "canvas_e", on_start = "#tabs li a[data-value='tab_4']" ) ), tabPanel( "Canvas F", value = "tab_5", h4("Initiate canvas with fixed height and width"), canvas( canvasID = "canvas_f", height = "800px", width = "1500px" ) ) ) ) server <- function(input, output, session) { } shinyApp(ui, server) }
# basic usage if(interactive()){ library(shiny) ui <- fluidPage( h3("Try to drag pictures locally to canvas"), canvas("canvas_a") ) server <- function(input, output, session) { } shinyApp(ui, server) } # multiple canvas on a page if(interactive()){ library(shiny) ui <- fluidPage( h3("multiple canvas on a page"), p("They are independent"), p("Dragging from one canvas to another is not supported currently"), column(6, canvas("canvas_left")), column(6, canvas("canvas_right")) ) server <- function(input, output, session) { } shinyApp(ui, server) } # with capture buttons buttons if(interactive()){ library(shiny) library(ggplot2) ui <- fluidPage( fluidRow( id = "new_row", column( 6, h3("this is a title"), column(6, tags$label("plot 1"), plotOutput("plot_1")), column(6, tags$label("plot 2"), plotOutput("plot_2")), ), column( 6, h2("To canvas buttons"), h4("pure button with `toCanvasBtn`"), toCanvasBtn( dom = "plot_1", label = "capture plot 1", canvasID = "canvas_b" ), br(), toCanvasBtn( dom = "capture_button", label = "capture this button itself", canvasID = "canvas_b", id = "capture_button" ), br(), toCanvasBtn( dom = "#new_row .col-sm-6:first-of-type", label = "complex selector to select left column", canvasID = "canvas_b", isID = FALSE ), br(), h4("button text input for any part of document with `toCanvasBtn`"), toCanvasTextBtn( label = "try #plot_2 to for plot 2 or other selector", canvasID = "canvas_b", text_value = "#plot_2" ) ) ), canvas("canvas_b") ) server <- function(input, output, session) { output$plot_1 <- renderPlot({ ggplot(mpg, aes(cty, hwy)) + geom_count(col="tomato3", show.legend=F) }) output$plot_2 <- renderPlot({ ggplot(mpg, aes(cty, hwy)) + geom_point() }) } shinyApp(ui, server) } # start canvas as hidden, initiate later in tab panels if(interactive()){ library(shiny) ui <- fluidPage( tabsetPanel( id = "tabs", tabPanel( "Home page", value = "tab_1", h4("Content on home page ...."), p("Canvas is hidden on start, go to other tabs") ), tabPanel( "Canvas C", value = "tab_2", markdown( ' # canvas hidden on start In this example, you will see if the canvas is hidden, not on the first tab in a `tabsetPanel`, or other similar UI where you do not see canvas on start. Then, the canvas cannot be initiate properly using the default height value (100vh). Using the dynamic computed CSS height like "100%", or "100vh" with "hidden" (display = none) elements give the height of `0` on start. So, you **should not see the canvas** on this tab, but a broken structure and no canvas grid. To fix it, either give it a fixed `height` and `width` pixel unit, like - `height = "800px"`, `width = "1500px"` or bind the initiation event to a click of a button, the tab title or any other element you specify with the `on_start` argument. See the example code and watch how we do it in "canvas D-F". ' ), canvas(canvasID = "canvas_c") ), tabPanel( "Canvas D", value = "tab_3", h4("Initiate canvas by a button"), actionButton("start_canvas", "Start Canvas C"), canvas( canvasID = "canvas_d", on_start = "#start_canvas" ) ), tabPanel( "Canvas E", value = "tab_4", h4("Initiate canvas by clicking tab title"), p("Canvas initiate when first time users come to this tab"), canvas( canvasID = "canvas_e", on_start = "#tabs li a[data-value='tab_4']" ) ), tabPanel( "Canvas F", value = "tab_5", h4("Initiate canvas with fixed height and width"), canvas( canvasID = "canvas_f", height = "800px", width = "1500px" ) ) ) ) server <- function(input, output, session) { } shinyApp(ui, server) }
A bootstrap button that allows users to take a screenshot of specified HTML element (usually an image) and send it to the drawer canvas for editing. In addition, you can download it as "png" or "jpg" by opening up the dropdown menu.
toCanvasBtn( dom, canvasID, isID = TRUE, id = "", label = "To Canvas", color_class = "primary" )
toCanvasBtn( dom, canvasID, isID = TRUE, id = "", label = "To Canvas", color_class = "primary" )
dom |
a HTML DOM selector, mostly common is to select the element by ID:
e.g. a plot with ID "plot1", to select, use Other complex selector is supported. First turn |
canvasID |
string, the ID of canvas. Unlike |
isID |
bool, |
id |
string, ID for this button, optional |
label |
label of this button, optional |
color_class |
bootstrap button color class suffix, usually one of 'default', 'primary', 'info', 'success', 'warning', 'danger' |
This component will not work unless a drawer canvas has been loaded on current document.
a button
# see example of "canvas", `?canvas`
# see example of "canvas", `?canvas`
Unlike toCanvasBtn only screenshot a defined element, this function can take screenshot of any element you specify in the text box and sent to canvas by using Jquery selector format.
toCanvasTextBtn( canvasID, label = "", text_value = "", placeholder = "type a selector", tooltip = "Screenshot any element to drawer canvas", placement = "bottom", btn_label = "To Canvas", color_class = "primary", style = "" )
toCanvasTextBtn( canvasID, label = "", text_value = "", placeholder = "type a selector", tooltip = "Screenshot any element to drawer canvas", placement = "bottom", btn_label = "To Canvas", color_class = "primary", style = "" )
canvasID |
string, the ID of canvas. |
label |
string, label of the whole group, on the top |
text_value |
string, nitial value of the text input |
placeholder |
string, placeholder text of the text input |
tooltip |
a tooltip of the group |
placement |
where should the tooltip go? |
btn_label |
text on the button |
color_class |
bootstrap button color class suffix, usually one of 'default', 'primary', 'info', 'success', 'warning', 'danger' |
style |
additional CSS style of the group, like "width: 50%" |
This component will not work unless a drawer canvas has been loaded on current document.
The selector uses Jquery selector.
If you are not familiar with it, just remember
the mostly commonly used is the element ID, which is the inputID
, ID
arguments
in most Shiny components. Jquery selector is almost the same for ID, but requires
you to add "#" in front, "#element-ID".
If you have no idea about shiny or HTML selector, right click on the element, and click inspect, you should see the document HTML code in the inspector and the element you want should be highlighted. Find the attribute of "id", that's what you need. Again, append "#" in front of that value. Some elements do not have an "id", in this case, you need some advanced selectors. Learn about them by Google "CSS selector".
a shiny input group
# see the example of "canvas", `?canvas`
# see the example of "canvas", `?canvas`