web-dev-qa-db-ja.com

スマートフォンで使用すると、光沢のあるインタラクティブなプロットは指の動きを理解しません

私は、インタラクティブなアクションを実装するプロットを備えたR-Shinyアプリケーションを持っています。アイデアを与えるために、私は問題のある機能である簡略化されたshinyappの下に、インタラクティブな描画プロットを投稿します。 (それは私の古い答えから取ったものです here

実際には問題なく動作していますが、スマートフォンから使用する必要があります。問題:スマートフォンで行う指の動きは、電話ではページ上のズームまたはページ上のスクロールとして解釈されます。 、およびプロット上でのマウス選択またはマウス移動(ホバリング)ではありません。

アプリに実装してタッチイベントをマウスイベントに変換できるコード(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)
22
agenis

touch-action CSSプロパティ:

#plot {
  touch-action: none;
}

タッチイベントをマウスイベントに変換するのは少し難しいですが、touchstarttouchmovetouchendなどのタッチイベントをリッスンして、JavaScriptで同等のマウスイベントをシミュレートできます。 https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Using_Touch_Events および https://javascript.info/dispatch-events を参照してください詳細については。

完璧ではありませんが、試してみました。プロット上のタッチジェスチャーを無効にし、touchmovemousemoveに変換し、描画を開始するタイミング(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)
4
greg L

私は質問を完全に解決することはできませんが、汚い回避策もあなたにいくつかの価値があるかもしれません。または、他の誰かがその答えに基づいて構築できます。

プロットのクリックがモバイルでキャプチャされないというエラーを再現できました。しかし、javascript/shinyjsを使用してクリックイベントを追加できることに気付きました。

1つの方法は次のとおりです。

  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)
5
Tonio Liebrand