web-dev-qa-db-fra.com

utilisé sur un smartphone, l'intrigue brillante et interactive ne comprend pas les mouvements des doigts

J'ai une application R-Shiny avec un tracé qui implémente des actions interactives: cliquez, survolez (survoler, c'est passer la souris sur le tracé, qui peut être détecté par brillant). Pour donner une idée, je poste ci-dessous un shinyapp simplifié avec la fonctionnalité qui me pose problème, le tracé de dessin interactif. (il est tiré d'une ancienne réponse à moi ici )

En fait, cela fonctionne bien, mais j'ai besoin que les gens l'utilisent depuis leurs smartphones . Le problème: les mouvements des doigts que nous faisons dans le smartphone sont interprétés par le téléphone comme zoom sur la page ou défilement sur la page, et non comme une sélection de souris ou le mouvement de la souris sur l'intrigue (en vol stationnaire).

Y a-t-il une modification du code (java? Css?) Que je peux implémenter sur l'application pour transformer les événements tactiles en événements de souris, ou une option/un geste sur le smartphone pour permettre un mouvement de type souris?

Merci beaucoup. Code:

library(shiny)
ui <- fluidPage(
  h4("Click on plot to start drawing, click again to pause"),
  sliderInput("mywidth", "width of the pencil", min=1, max=30, step=1, value=10),
  actionButton("reset", "reset"),
  plotOutput("plot", width = "500px", height = "500px",
             hover=hoverOpts(id = "hover", delay = 100, delayType = "throttle", clip = TRUE, nullOutside = TRUE),
             click="click"))
server <- function(input, output, session) {
  vals = reactiveValues(x=NULL, y=NULL)
  draw = reactiveVal(FALSE)
  observeEvent(input$click, handlerExpr = {
    temp <- draw(); draw(!temp)
    if(!draw()) {
      vals$x <- c(vals$x, NA)
      vals$y <- c(vals$y, NA)
    }})
  observeEvent(input$reset, handlerExpr = {
    vals$x <- NULL; vals$y <- NULL
  })
  observeEvent(input$hover, {
    if (draw()) {
      vals$x <- c(vals$x, input$hover$x)
      vals$y <- c(vals$y, input$hover$y)
    }})
  output$plot= renderPlot({
    plot(x=vals$x, y=vals$y, xlim=c(0, 28), ylim=c(0, 28), ylab="y", xlab="x", type="l", lwd=input$mywidth)
  })}
shinyApp(ui, server)
22
agenis

Vous pouvez désactiver les mouvements de panoramique/zoom sur le tracé à l'aide de touch-action Propriété CSS:

#plot {
  touch-action: none;
}

Transformer des événements tactiles en événements de souris est un peu plus délicat, mais vous pouvez écouter des événements tactiles comme touchstart, touchmove, touchend et simuler des événements de souris équivalents en JavaScript. Voir https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Using_Touch_Events et https://javascript.info/dispatch-events pour plus d'informations.

Ce n'est pas parfait, mais je l'ai essayé. J'ai désactivé les gestes tactiles sur l'intrigue et ajouté un script qui convertit touchmove en mousemove, et indique au serveur quand commencer à dessiner (sur touchstart) et arrêter de dessiner (sur touchend).

library(shiny)

ui <- fluidPage(
  h4("Click on plot to start drawing, click again to pause"),
  sliderInput("mywidth", "width of the pencil", min=1, max=30, step=1, value=10),
  actionButton("reset", "reset"),
  plotOutput("plot", width = "400px", height = "400px",
             hover=hoverOpts(id = "hover", delay = 100, delayType = "throttle", clip = TRUE, nullOutside = TRUE),
             click="click"),
  tags$head(
    tags$script("
      $(document).ready(function() {
        var plot = document.getElementById('plot')

        plot.addEventListener('touchmove', function (e) {
          var touch = e.changedTouches[0];
          var mouseEvent = new MouseEvent('mousemove', {
            view: window,
            bubbles: true,
            cancelable: true,
            screenX: touch.screenX,
            screenY: touch.screenY,
            clientX: touch.clientX,
            clientY: touch.clientY
          })
          touch.target.dispatchEvent(mouseEvent);
          e.preventDefault()
        }, { passive: false });

        plot.addEventListener('touchstart', function(e) {
          Shiny.onInputChange('draw', true)
          e.preventDefault()
        }, { passive: false });

        plot.addEventListener('touchend', function(e) {
          Shiny.onInputChange('draw', false)
          e.preventDefault()
        }, { passive: false });
      })
    "),
    tags$style("#plot { touch-action: none; }")
    )
)

server <- function(input, output, session) {
  vals = reactiveValues(x=NULL, y=NULL)
  draw = reactiveVal(FALSE)

  observeEvent(input$click, {
    draw(!draw())
    vals$x <- append(vals$x, NA)
    vals$y <- append(vals$y, NA)
  })

  observeEvent(input$draw, {
    draw(input$draw)
    vals$x <- append(vals$x, NA)
    vals$y <- append(vals$y, NA)
  })

  observeEvent(input$reset, handlerExpr = {
    vals$x <- NULL; vals$y <- NULL
  })

  observeEvent(input$hover, {
    if (draw()) {
      vals$x <- c(vals$x, input$hover$x)
      vals$y <- c(vals$y, input$hover$y)
    }
  })

  output$plot= renderPlot({
    plot(x=vals$x, y=vals$y, xlim=c(0, 28), ylim=c(0, 28), ylab="y", xlab="x", type="l", lwd=input$mywidth)
  })
}

shinyApp(ui, server)
4
greg L

Bien que je ne puisse pas résoudre complètement la question, peut-être qu'une solution de contournement sale pourrait également avoir une certaine valeur pour vous. Ou quelqu'un d'autre peut s'appuyer sur cette réponse.

Je pourrais reproduire l'erreur que les clics sur l'intrigue ne sont pas capturés sur mobile. Mais j'ai remarqué que je peux ajouter des événements de clic supplémentaires avec javascript/shinyjs.

Une façon serait:

  onevent(event = "click", id = "plot", function(e){
    global$clickx = c(global$clickx, e$pageX - 88)
    global$clicky = c(global$clicky, 540 - e$pageY)
  })

Elle présente quelques inconvénients:

  • les formes sont dessinées avec des lignes uniquement, il ne capture pas tout le survol de l'intrigue
  • la position est assez imprécise car il faut tenir compte des frontières et des marges (très sale mais potentiel ici)

J'ai manqué un peu de temps après quelques heures, on peut certainement l'améliorer, mais peut-être que ça vous intéresse quand même.

Testez ici: (le lien pourrait changer dans les prochaines semaines)

http://ec2-3-121-215-255.eu-central-1.compute.amazonaws.com/shiny/rstudio/sample-apps/mobile/

Code reproductible: (testé sur smartphone: Mi A2)

library(shiny)
library(shinyjs)
ui <- fluidPage(
  useShinyjs(),
  h4("Click on plot to start drawing, click again to pause"),

  plotOutput(outputId = "plot", width = "500px", height = "500px")
)


server <- function(input, output, session) {

  onevent(event = "click", id = "plot", function(e){
    global$clickx = c(global$clickx, e$pageX - 88)
    global$clicky = c(global$clicky, 540 - e$pageY)
  })

  global <- reactiveValues(clickx = NULL, clicky = NULL)

  output$plot= renderPlot({
    plot(x = NULL, y = NULL, xlim=c(0, 440), ylim=c(0, 440), ylab="y", xlab="x", type="l")
    len <- length(global$clickx)
    lines(x = global$clickx, y = global$clicky, type = "p")      
    if(len > 1){
      for(nr in 2:len){
        lines(x = global$clickx[(nr - 1):nr], y = global$clicky[(nr - 1):nr], type = "l")
      }
    }
  })
}
shinyApp(ui, server)
5
Tonio Liebrand