在Safari的div内查看时,画布对触摸无响应

时间:2018-06-30 17:46:06

标签: javascript html ios css html5-canvas

我正在尝试获得一个不错的旋钮/转盘,可以旋转该旋钮/转盘以通过websockets将值发送回服务器。
基本前提很好,我从网上获得了代码。
我正在尝试修改代码,以便获得更漂亮的旋钮。我成功地将画布放置在显示静态图像的div内,而画布根据鼠标/触摸事件旋转了半透明图像。
我对代码进行的添加在台式机上运行良好(我正在运行Firefox 45.0.2),但在iPad(Safari,iOS 9.3.5)上根本无法运行,而在iPhone(iOS 10.2)上则完全无法运行。 1)
在iPhone上,旋钮的旋转方向与预期的相反,通常只有水平移动才能使旋钮旋转。
我没有使用(也不希望使用)诸如jquery之类的任何库。
下面的代码将按原样工作。但是,删除正文部分的注释标记会导致我指出的问题。
(哦,为了阻止所有评论,黑色背景和奇数文字的颜色就在那里,以便您可以看到不带静态背景的半透明元素)
我完全没有jscript的经验,只能设法遵循当前代码的作用。 (我不想使用其他库的原因之一)
我怀疑问题在于如何解释触摸事件坐标,但我无法进行任何测试。
任何帮助或建议,将不胜感激。


HTML代码:


<!DOCTYPE html>
<html>
  <head>
    <title>Stepper example</title>
    <meta name="viewport" content="width=device-width, initial-scale=0.7">
    <style>
      body { 
        text-align: center; background-color: black;
        color: red
      }

      .container{
        position: relative;
        background: url(step_background.png);
        width: 480px;
        height: 480px;
        margin: auto;
        z-index:1;
      }

      .knob{
        position: relative;
        top: 59px;
        background: url(knob_bg.png);
        width: 362px;
        height:362px;
        margin:auto;
        z-index:2;
      }

      #stepper{
        position: relative;
      }

    </style>

    <script>
      var MIN_TOUCH_RADIUS = 20;
      var MAX_TOUCH_RADIUS = 200;
      var CANVAS_WIDTH = 362, CANVAS_HEIGHT = 362;
      var PIVOT_X = 181, PIVOT_Y = 181;
      var plate_angle = 0;
      var plate_img = new Image();
      var click_state = 0;
      var last_angle_pos = 0;
      var mouse_xyra = {x:0, y:0, r:0.0, a:0.0};
      var ws;
      plate_img.src = "knob_fg.png";

      function init() {
        var stepper = document.getElementById("stepper");
        var ctx = stepper.getContext("2d");
        stepper.width = CANVAS_WIDTH;
        stepper.height = CANVAS_HEIGHT;
        stepper.addEventListener("touchstart", mouse_down);
        stepper.addEventListener("touchend", mouse_up);
        stepper.addEventListener("touchmove", mouse_move);
        stepper.addEventListener("mousedown", mouse_down);
        stepper.addEventListener("mouseup", mouse_up);
        stepper.addEventListener("mousemove", mouse_move);
        ctx.translate(PIVOT_X, PIVOT_Y);
        rotate_plate(plate_angle);
      }

      function connect_onclick() {
        if(ws == null) {
          ws = new WebSocket('ws://'+ window.location.hostname + ':81/', ['arduino']);
          document.getElementById("ws_state").innerHTML = "CONNECTING";
          ws.onopen = ws_onopen;
          ws.onclose = ws_onclose;
          ws.onmessage = ws_onmessage;
          ws.onerror = function(){ alert("websocket error " + this.url) };
        }
        else
          ws.close();
      }

      function ws_onopen() {
        document.getElementById("ws_state").innerHTML = "<font color='blue'>CONNECTED</font>";
        document.getElementById("bt_connect").innerHTML = "Disconnect";
        rotate_plate(plate_angle);
      }

      function ws_onclose() {
        document.getElementById("ws_state").innerHTML = "<font color='gray'>CLOSED</font>";
        document.getElementById("bt_connect").innerHTML = "Connect";
        ws.onopen = null;
        ws.onclose = null;
        ws.onmessage = null;
        ws = null;
        rotate_plate(plate_angle);
      }

      function ws_onmessage(e_msg) {
        e_msg = e_msg || window.event; // MessageEvent
        plate_angle = Number(e_msg.data);
        rotate_plate(plate_angle);
        //alert("msg : " + e_msg.data);
      }

      function rotate_plate(angle) {
        var stepper = document.getElementById("stepper");
        var ctx = stepper.getContext("2d");
        ctx.clearRect(-PIVOT_X, -PIVOT_Y, CANVAS_WIDTH, CANVAS_HEIGHT);
        ctx.rotate(-angle / 180 * Math.PI);
        ctx.drawImage(plate_img, -PIVOT_X, -PIVOT_Y);
        ctx.rotate(angle / 180 * Math.PI);
        /*
            Currently, the angle displayed and sent as a message appears to be set such that movement in a clockwise direction
            reports a negative number. Needs to be looked at, probably by changing "angle.toFixed" to "-angle.toFixed"
        */
        if(ws && (ws.readyState == 1))
          ws.send(plate_angle.toFixed(4) + "\r\n");
        ws_angle = document.getElementById("ws_angle");
        ws_angle.innerHTML = angle.toFixed(1);
      }

      function check_update_xyra(event, mouse_xyra) {
        var x, y, r, a;
        var min_r, max_r, width;
        if(event.touches) {
          var touches = event.touches;
          x = (touches[0].pageX - touches[0].target.offsetLeft) - PIVOT_X;
          y = PIVOT_Y - (touches[0].pageY - touches[0].target.offsetTop);
        }
        else {
          x = event.offsetX - PIVOT_X;
          y = PIVOT_Y - event.offsetY;
        }
        /* cartesian to polar coordinate conversion */
        r = Math.sqrt(x * x + y * y);
        a = Math.atan2(y, x);
        mouse_xyra.x = x;
        mouse_xyra.y = y;
        mouse_xyra.r = r;
        mouse_xyra.a = a;
        if((r >= MIN_TOUCH_RADIUS) && (r <= MAX_TOUCH_RADIUS))
          return true;
        else
          return false;
      }

      function mouse_down(event) {
        if(event.target == stepper)
          event.preventDefault();
        if(event.touches && (event.touches.length > 1))
          click_state = event.touches.length;
        if(click_state > 1)
          return;
        if(check_update_xyra(event, mouse_xyra)) {
          click_state = 1;
          last_angle_pos = mouse_xyra.a / Math.PI * 180.0;
        }
      }

      function mouse_up() {
        click_state = 0;
      }

      function mouse_move(event) {
        var angle_pos, angle_offset;
        if(event.touches && (event.touches.length > 1))
          click_state = event.touches.length;
        if(!click_state || (click_state > 1))
          return;
        if(!check_update_xyra(event, mouse_xyra)) {
          click_state = 0;
          return;
        }
        event.preventDefault();
        angle_pos = mouse_xyra.a / Math.PI * 180.0;
        if(angle_pos < 0.0)
          angle_pos = angle_pos + 360.0;
        angle_offset = angle_pos - last_angle_pos;
        last_angle_pos = angle_pos;
        if(angle_offset > 180.0)
          angle_offset = -360.0 + angle_offset;
        else
          if(angle_offset < -180.0)
            angle_offset = 360 + angle_offset;
        plate_angle += angle_offset;
        rotate_plate(plate_angle);
      }

      window.onload = init;

    </script>
  </head>

  <body>
    <h2>
      Smart Expansion / Stepper Motor<br><br>
      Angle <font id="ws_angle" color="blue">0</font><br><br>
<!--
      <div class="container">
        <div class="knob">
-->
          <canvas id="stepper"></canvas>
<!--
        </div>
      </div>
-->

      <br><br>
      WebSocket <font id="ws_state" color="gray">CLOSED</font>
    </h2>

    <p><button id="bt_connect" type="button" onclick="connect_onclick();">Connect</button></p>

  </body>
</html>

我可能需要添加其他注释,以提供指向背景图片的链接

knob_bg.png knob_fg.png

1 个答案:

答案 0 :(得分:0)

因此,在设法找出如何通过Windows上的firefox在ios设备上调试html之后,我设法找出了导致我的代码失败的原因。 问题出在检查函数check_update_xyra(event,mouse_xyra)
具体来说,行:
x = (touches[0].pageX - touches[0].target.offsetLeft) - PIVOT_X; y = PIVOT_Y - (touches[0].pageY - touches[0].target.offsetTop);
target.offsetxxx返回的值为0。这使弧度值(r)超出范围,从而导致函数返回false,或者在iPhone的情况下,导致触摸事件的行为异常。
偏移量返回为0的原因是因为我没有考虑到它们仅提供与目标父对象的偏移量,而不是整个文档的事实。
我设法通过添加一些代码为所有父元素添加偏移量来解决此问题,然后使用该总和来计算新的x和y坐标。 我的代码更改如下。 但是,如果有人有一种更优雅的计算偏移量的方法,我将不胜感激。
干杯...


function check_update_xyra(event, mouse_xyra) {
  var x, y, r, a;
  var tgtoffleft = 0;
  var tgtofftop = 0;
  var min_r, max_r, width;
  if(event.touches) {
    var touches = event.touches;
    // Bit of code to calculate the actual Left and Top offsets by adding offsets 
    // of each parent back through the hierarchy
    var tgt = event.touches[0].target;
    while (tgt) {
      tgtoffleft = tgtoffleft + tgt.offsetLeft;
      tgtofftop = tgtofftop + tgt.offsetTop;
      tgt = tgt.offsetParent;
    }
    // x = (touches[0].pageX - touches[0].target.offsetLeft) - PIVOT_X;
    // y = PIVOT_Y - (touches[0].pageY - touches[0].target.offsetTop);
    x = (touches[0].pageX - tgtoffleft) - PIVOT_X;
    y = PIVOT_Y - (touches[0].pageY - tgtofftop);
  }
  else {
    x = event.offsetX - PIVOT_X;
    y = PIVOT_Y - event.offsetY;
  }
  /* cartesian to polar coordinate conversion */
  r = Math.sqrt(x * x + y * y);
  a = Math.atan2(y, x);
  mouse_xyra.x = x;
  mouse_xyra.y = y;
  mouse_xyra.r = r;
  mouse_xyra.a = a;
  if((r >= MIN_TOUCH_RADIUS) && (r <= MAX_TOUCH_RADIUS))
    return true;
  else
    return false;
}