на смартфоне блестящий интерактивный сюжет не понимает движений пальцев

У меня есть приложение R-Shiny с сюжетом, реализующим интерактивные действия: щелчок, наведение (при наведении указатель мыши проходит над сюжетом, что может быть обнаружено с помощью блестящего). Чтобы дать представление, я размещаю ниже упрощенное блестящее приложение с проблематичной для меня функциональностью, интерактивным сюжетом для рисования. (это взято из моего старого ответа здесь)

На самом деле он работает нормально, но мне нужно, чтобы люди использовали его со своих смартфонов. Проблема: движения пальца, которые мы делаем на смартфоне, интерпретируются телефоном как масштабирование на странице или прокрутка на странице, а не как выделение или перемещение мыши по сюжет (парение).

Есть ли модификация кода (java? CSS?), Которую я могу реализовать в приложении, чтобы превратить события касания в события мыши, или параметр / жест на смартфоне, чтобы включить движение, подобное мышке?

Большое спасибо; код:

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)

person agenis    schedule 28.06.2019    source источник
comment
Существует список желаний для мобильных взаимодействий на GitHub в отношении plotly.js (не R api)   -  person ismirsehregal    schedule 28.06.2019


Ответы (2)


Вы можете отключить жесты панорамирования / масштабирования на графике с помощью _1 _ свойство CSS:

#plot {
  touch-action: none;
}

Превратить события касания в события мыши немного сложнее, но вы можете прослушивать такие события касания, как touchstart, touchmove, touchend, и имитировать эквивалентные события мыши в JavaScript. См. https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Using_Touch_Events и https://javascript.info/dispatch-events для получения дополнительной информации.

Это не идеально, но я попробовал. Я отключил сенсорные жесты на графике и добавил сценарий, который преобразует touchmove в mousemove и сообщает серверу, когда начинать рисование (на touchstart) и останавливать рисование (на 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)
person greg L    schedule 26.07.2019
comment
опубликовал ссылку с вашим кодом для проверки: agenis.shinyapps.io/handwriting-v3 - person agenis; 26.07.2019
comment
Привет, спасибо, это великолепно. Я добавил код на сервере, чтобы добавить NA в data.frame после touchchend, чтобы разорвать линии (поднимите карандаш). Я также немного уменьшил размер сюжета, чтобы он поместился на большинстве экранов без необходимости прокрутки. Блокирование движений сюжета (панорамирование / масштабирование) не всегда эффективно на iphone / Safari, я обнаружил, что двойной щелчок работает. Нет ли способа заблокировать любое масштабирование и прокрутку на всей веб-странице (без блокировки кнопок ...)? как на этот ответ? stackoverflow.com/q/17596938/3871924 - person agenis; 26.07.2019
comment
Ах блин, конечно Safari и iOS не поддерживают touch-action: none :( caniuse.com/#search=touch -action. Я не могу это проверить, но вы можете попробовать добавить preventDefault() к каждому обработчику событий касания, чтобы полностью отключить прокрутку / масштабирование. Я отредактировал ответ. - person greg L; 27.07.2019
comment
благодаря. Теперь кажется, что страница больше не движется, но побочный эффект - кнопка действий отключена как в Android, так и в iOS ... Что ж, вы достаточно помогли в этом, я попробую себя улучшить сейчас, большое спасибо! Это последняя версия agenis.shinyapps.io/handwriting-v4 - person agenis; 27.07.2019
comment
Хорошо, тогда еще одна попытка. Мы могли фиксировать сенсорные события только на сюжете, а не на всем документе. Смотрите редактирование - я проверил, что он, по крайней мере, работает в Android Chrome. Если он по-прежнему не работает на iOS, извините: / - person greg L; 27.07.2019
comment
бинго! это работает, стоит всех усилий! благодаря. agenis.shinyapps.io/handwriting-v5 - person agenis; 27.07.2019

Хотя я не могу полностью решить этот вопрос, возможно, грязный обходной путь также может иметь для вас некоторую ценность. Или кто-то другой может опираться на этот ответ.

Я мог воспроизвести ошибку, заключающуюся в том, что щелчки по графику не фиксируются на мобильном устройстве. Но я заметил, что могу добавлять дополнительные события кликов с помощью javascript / shinyjs.

Один из способов:

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

У него есть несколько недостатков:

  • фигуры рисуются только линиями, он не улавливает все зависания на графике
  • позиция довольно неточная, так как вы должны учитывать границы и поля (здесь очень грязно, но потенциально)

Через несколько часов у меня немного не хватило времени, наверняка его можно улучшить, но, может быть, это все равно заинтересует вас.

Протестируйте здесь: (ссылка может измениться в течение следующих недель)

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

Воспроизводимый код: (проверено на смартфоне: 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)
person Tonio Liebrand    schedule 20.07.2019
comment
привет большое спасибо за ваш ответ; действительно, мне действительно нужно решить эту проблему для публичной демонстрации. Если я хорошо понимаю, вы заменили наведение на просто щелчок? Я не думал об этом обходном пути, хорошая идея, ну, это упрощение, потому что рисунок становится сложнее рисовать и менее гладким. Но если у меня нет другого решения, я пойду с этим. Я адаптировал ваш код к своему первоначальному приложению, добавив кнопку для разрыва линии (поднимите карандаш) и обновление, чтобы при первом щелчке мыши отображалась точка справа: agenis.shinyapps.io/handwriting-v2 - person agenis; 21.07.2019
comment
Ну, особо не заменял. Исходная функциональность, похоже, не работает на мобильных устройствах, поэтому я удалил ее. Если вы заставите его работать, это будет лучшим решением, но я не эксперт по мобильным устройствам. По этой причине я только что добавил еще одно событие щелчка. Я не уверен, как запечатлеть наведение на мобильном телефоне, я думаю, это будет отключено, но при нажатии мыши я получаю контекстное меню, но ему не удалось предотвратить поведение по умолчанию. - person Tonio Liebrand; 21.07.2019
comment
У меня был другой ответ, который решил исходную проблему, поэтому я наградил его наградой. В любом случае спасибо @BigDataScientist за ваш вклад и ваше драгоценное время! - person agenis; 26.07.2019
comment
полностью заслужено. Мне очень интересно после работы разобраться в его ответе. У меня нет большого опыта работы с мобильными устройствами, и я очень рад узнать, как он это решил :). - person Tonio Liebrand; 26.07.2019