javascript - 在 ios safari 上的 div 中时 Canvas 不响应触摸

标签 javascript html ios css html5-canvas

我正在尝试获得一个不错的旋钮/转盘,它可以旋转以通过 websockets 将值发送回服务器。
基本前提很好,我从网上得到了代码。
我正在尝试修改代码,以便获得更漂亮的旋钮。通过将 Canvas 放置在几个显示静态图像的 div 中,而 Canvas 旋转半透明图像以响应鼠标/触摸事件,我取得了成功。
我对代码所做的添加在桌面上运行良好(我运行的是 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

最佳答案

因此,在设法找出如何通过 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;
}

关于javascript - 在 ios safari 上的 div 中时 Canvas 不响应触摸,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51117225/

相关文章:

php - 在 PHP 中提取 JQuery POST 参数

javascript - 页面加载时的事件类未触发

html - 链接到当前页面中的元素

ios - 动画 ScrollView 高度 - 内容在动画之前突然跳起来

javascript - 如何在一个按钮中放置两个图标

javascript - 按计数和值对选择列表进行排序

Javascript 确认对话框需要点击 3 次 "cancel"才能关闭

html - CSS 2 列布局 : Column height

ios - AppDelegate 如何调用 ViewController.h

objective-c - 如何比较 coredata 字符串和 NSString