【问题标题】:Canvas get touch position on mobile web画布在移动网络上获取触摸位置
【发布时间】:2016-03-01 08:01:57
【问题描述】:

我有一个代码将一条线从 (x,y) 坐标拖动到新的鼠标 (x,y) 坐标。这在桌面浏览器中运行良好,但由于某种原因它在移动浏览器中不起作用。我添加了触摸事件侦听器,但我猜坐标有些不正确。这是我的代码:

   function getMouse(e) {
     var element = canvas, offsetX = 0, offsetY = 0;
     if (element.offsetParent) {
       do {
         offsetX += element.offsetLeft;
         offsetY += element.offsetTop;
       } while ((element = element.offsetParent));
     }

     mx = (e.pageX - offsetX) - LINE_WIDTH;
     my =( e.pageY - offsetY )- LINE_WIDTH;
   }
   function mouseDown(e){
     getMouse(e);
     clear(fctx);
     var l = lines.length;
     for (var i = l-1; i >= 0; i--) {
       draw(fctx,lines[i]);
       var imageData = fctx.getImageData(mx, my, 1, 1);
       if (imageData.data[3] > 0) {
         selectedObject = lines[i];
         isDrag = true;
         canvas.onmousemove = drag;
         clear(fctx);
       }
     }
   }
   function mouseUp(){
     isDrag = false;
   }
   canvas.onmousedown = mouseDown;
   canvas.onmouseup = mouseUp;
   canvas.addEventListener('touchstart', mouseDown, false);
   canvas.addEventListener('touchend', mouseUp, false);

你可以在这里看到工作部分:http://codepen.io/nirajmchauhan/pen/yYdMJR

【问题讨论】:

    标签: javascript canvas mobile


    【解决方案1】:

    从触摸事件生成鼠标事件

    好的,在这里看到这个问题有一段时间了,没有人提出我会给出的答案。

    与鼠标事件不同,触摸事件涉及与 UI 的许多接触点。为了适应这一点,触摸事件提供了一系列接触点。由于鼠标不能同时在两个地方,因此这两种交互方法应该真正分开处理,以获得最佳的用户体验。 OP,因为您没有询问有关检测设备是触摸还是鼠标驱动的问题,所以我已将其留给其他人询问。

    同时处理

    鼠标和触摸事件可以共存。在没有任何一个或另一个的设备上添加鼠标或触摸事件的侦听器不是问题。缺少的输入接口根本不会生成任何事件。这使得为​​您的页面实施透明的解决方案变得容易。

    这取决于您喜欢哪个接口,并在其硬件不可用时模拟该接口。在这种情况下,我将从创建的任何触摸事件中模拟鼠标。

    以编程方式创建事件。

    代码使用 MouseEvent 对象来创建和调度事件。它使用简单,事件与真实的鼠标事件没有区别。有关 MouseEvents 的详细说明,请转到 MDN MouseEvent

    最基本的。

    创建鼠标点击事件并将其发送到文档

      var event = new MouseEvent( "click", {'view': window, 'bubbles': true,'cancelable': true});
      document.dispatchEvent(event);
    

    您还可以将事件分派给单个元素。

      document.getElementById("someButton").dispatchEvent(event);
    

    监听事件与监听鼠标是一样的。

      document.getElementById("someButton").addEventListener(function(event){
            // your code
      ));
    

    MouseEvent 函数中的第二个参数是您可以添加有关事件的额外信息的位置。例如说clientXclientY 鼠标的位置,或者whichbuttons 正在按下哪个按钮。

    如果你看过mouseEvent,你就会知道有很多属性。因此,您在鼠标事件中发送的确切内容将取决于您的事件侦听器正在使用什么。

    触摸事件。

    触摸事件类似于鼠标。有touchstarttouchmovetouchend。它们的不同之处在于它们提供一系列位置,每个接触点一个项目。不确定最大值是多少,但对于这个答案,我们只对一个感兴趣。详情请参阅MDN touchEvent

    我们需要做的是,对于只涉及一个接触点的触摸事件,我们要在同一位置生成相应的鼠标事件。如果触摸事件返回多个接触点,我们无法知道它们的预期焦点在哪一个,所以我们将直接忽略它们。

    function touchEventHandler(event){
        if (event.touches.length > 1){  // Ignor multi touch events
            return;
        }
    }
    

    所以现在我们知道了触摸单个联系人我们可以根据触摸事件中的信息来创建鼠标事件。

    最基本的

    touch = event.changedTouches[0]; // get the position information
    if(type === "touchmove"){        
        mouseEventType = "mousemove";   // get the name of the mouse event
                                        // this touch will emulate   
    }else
    if(type === "touchstart"){  
        mouseEventType = "mousedown";     // mouse event to create
    }else
    if(type === "touchend"){ 
        mouseEventType = "mouseup";     // ignore mouse up if click only
    }
    
    var mouseEvent = new MouseEvent( // create event
        mouseEventType,   // type of event
        {
            'view': event.target.ownerDocument.defaultView,
            'bubbles': true,
            'cancelable': true,
            'screenX':touch.screenX,  // get the touch coords 
            'screenY':touch.screenY,  // and add them to the 
            'clientX':touch.clientX,  // mouse event
            'clientY':touch.clientY,
    });
    // send it to the same target as the touch event contact point.
    touch.target.dispatchEvent(mouseEvent);
    

    现在,当用户仅在一个位置触摸设备时,您的鼠标侦听器将收到 mousedownmousemovemouseup 事件。

    错失点击

    到目前为止一切都很好,但是缺少一个鼠标事件,这也是必需的。 “onClick”我不确定是否存在等效的触摸事件,作为练习,我看到有足够的信息来决定一组触摸事件是否可以被视为一次点击。

    这将取决于开始和结束触摸事件的距离,超过几个像素和它的拖动。这也取决于多长时间。 (虽然和鼠标不一样) 我发现人们倾向于点击进行点击,而鼠标可以按住,代替构象上的释放,或者拖开来取消,这不是人们使用触摸界面的方式。

    所以我记录了 touchStart 事件发生的时间。 event.timeStamp 以及它开始的地方。然后在touchEnd 事件中,我找到了它移动的距离和之后的时间。如果它们都在我设置的限制范围内,我还会生成一个鼠标单击事件以及鼠标向上事件。

    这就是将触摸事件转换为鼠标事件的基本方法。

    一些代码

    下面是一个名为 mouseTouch 的小型 API,它可以完成我刚才解释的功能。它涵盖了简单绘图应用程序所需的最基本的鼠标交互。

    //                                _______               _     
    //                               |__   __|             | |    
    //    _ __ ___   ___  _   _ ___  ___| | ___  _   _  ___| |__  
    //   | '_ ` _ \ / _ \| | | / __|/ _ \ |/ _ \| | | |/ __| '_ \ 
    //   | | | | | | (_) | |_| \__ \  __/ | (_) | |_| | (__| | | |
    //   |_| |_| |_|\___/ \__,_|___/\___|_|\___/ \__,_|\___|_| |_|
    //                                                            
    //    
    // Demonstration of a simple mouse emulation API using touch events.
    
    // Using touch to simulate a mouse.
    // Keeping it clean with touchMouse the only pubic reference.
    // See Usage instructions at bottom.
    var touchMouse = (function(){
        "use strict";
        var timeStart, touchStart, mouseTouch, listeningElement, hypot;
    
    
        mouseTouch = {};  // the public object 
        // public properties.
        mouseTouch.clickRadius = 3; // if touch start and end within 3 pixels then may be a click
        mouseTouch.clickTime = 200; // if touch start and end in under this time in ms then may be a click
        mouseTouch.generateClick = true; // if true simulates onClick event
                                         // if false only generate mousedown, mousemove, and mouseup
        mouseTouch.clickOnly = false; // if true on generate click events
        mouseTouch.status = "Started."; // just for debugging
    
    
    
        // ES6 new math function
        // not sure the extent of support for Math.hypot so hav simple poly fill
        if(typeof Math.hypot === 'function'){
            hypot = Math.hypot;
        }else{
            hypot = function(x,y){  // Untested 
                return Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
            };
        }
        // Use the new API and MouseEvent object
        function triggerMouseEvemt(type,fromTouch,fromEvent){
          var mouseEvent = new MouseEvent(
              type, 
              {
                  'view': fromEvent.target.ownerDocument.defaultView,
                  'bubbles': true,
                  'cancelable': true,
                    'screenX':fromTouch.screenX,
                    'screenY':fromTouch.screenY,
                    'clientX':fromTouch.clientX,
                    'clientY':fromTouch.clientY,
                    'offsetX':fromTouch.clientX, // this is for old Chrome 
                    'offsetY':fromTouch.clientY,
                    'ctrlKey':fromEvent.ctrlKey,
                    'altKey':fromEvent.altKey,
                    'shiftKey':fromEvent.shiftKey,
                    'metaKey':fromEvent.metaKey,
                    'button':0,
                    'buttons':1,
              });
            // to do.
            // dispatch returns cancelled you will have to 
            // add code here if needed
            fromTouch.target.dispatchEvent(mouseEvent);
        }
    
        // touch listener. Listens to Touch start, move and end.
        // dispatches mouse events as needed. Also sends a click event
        // if click falls within supplied thresholds and conditions
        function emulateMouse(event) {
    
            var type, time, touch, isClick, mouseEventType, x, y, dx, dy, dist;
            event.preventDefault();  // stop any default happenings interfering
            type = event.type ;  // the type.
    
            // ignore multi touch input
            if (event.touches.length > 1){
                if(touchStart !== undefined){ // don't leave the mouse down
                    triggerMouseEvent("mouseup",event.changedTouches[0],event);
                }
                touchStart = undefined;
                return;
            }
            mouseEventType = "";
            isClick = false;  // default no click
            // check for each event type I have the most numorus move event first, Good practice to always think about the efficancy for conditional coding.
            if(type === "touchmove" && !mouseTouch.clickOnly){        // touchMove
                touch = event.changedTouches[0];
                mouseEventType = "mousemove";      // not much to do just move the mouse
            }else
            if(type === "touchstart"){  
                touch = touchStart = event.changedTouches[0]; // save the touch start for dist check
                timeStart = event.timeStamp; // save the start time
                mouseEventType = !mouseTouch.clickOnly?"mousedown":"";     // mouse event to create
            }else
            if(type === "touchend"){  // end check time and distance
                touch =  event.changedTouches[0];
                mouseEventType = !mouseTouch.clickOnly?"mouseup":"";     // ignore mouse up if click only
                // if click generator active
                if(touchStart !== undefined && mouseTouch.generateClick){
                    time = event.timeStamp - timeStart;  // how long since touch start
                    // if time is right
                    if(time < mouseTouch.clickTime){
                        // get the distance from the start touch
                        dx = touchStart.clientX-touch.clientX;
                        dy = touchStart.clientY-touch.clientY;
                        dist = hypot(dx,dy);
                        if(dist < mouseTouch.clickRadius){
                            isClick = true;
                        }
                    }
                }
            }
            // send mouse basic events if any
            if(mouseEventType !== ""){
                // send the event
                triggerMouseEvent(mouseEventType,touch,event);
            }
            // if a click also generates a mouse click event
            if(isClick){
                // generate mouse click
                triggerMouseEvent("click",touch,event);
            }
        }
    
        // remove events
        function removeTouchEvents(){
            listeningElement.removeEventListener("touchstart", emulateMouse);
            listeningElement.removeEventListener("touchend", emulateMouse);
            listeningElement.removeEventListener("touchmove", emulateMouse);
            listeningElement = undefined;  
    
        }
    
        // start  adds listeners and makes it all happen.
        // element is optional and will default to document.
        // or will Listen to element.
        function startTouchEvents(element){
            if(listeningElement !== undefined){ // untested
                // throws to stop cut and past useage of this example code.
                // Overwriting the listeningElement can result in a memory leak.
                // You can remove this condition block and it will work
                // BUT IT IS NOT RECOGMENDED
    
                throw new ReferanceError("touchMouse says!!!! API limits functionality to one element.");
            }
            if(element === undefined){
                element = document;
            }
            listeningElement = element;
            listeningElement.addEventListener("touchstart", emulateMouse);
            listeningElement.addEventListener("touchend", emulateMouse);
            listeningElement.addEventListener("touchmove", emulateMouse);
        }
    
        // add the start event to public object.
        mouseTouch.start = startTouchEvents;
        // stops event listeners and remove them from the DOM 
        mouseTouch.stop = removeTouchEvents;
    
        return mouseTouch;
    
    })(); 
    
    
    
    
    
    
    
    
    
    
    
    // How to use
    
    touchMouse.start(); // done using defaults will emulate mouse on the entier page 
    
    // For one element and only clicks
    // HTML
    <input value="touch click me" id="touchButton" type="button"></input>
    // Script
    var el = document.getElementById("touchButton");
    if(el !== null){
        touchMouse.clickOnly = true;
        touchMouse.start(el);
    }
    
    // For drawing on a canvas
    <canvas id="touchCanvas"></canvas> 
    // script
    var el = document.getElementById("touchButton");
    if(el !== null){
        touchMouse.generateClick = false; // no mouse clicks please
        touchMouse.start(el);
    }
    
    // For switching elements call stop then call start on the new element
    // warning touchMouse retained a reference to the element you
    // pass it with start. Dereferencing touchMouse will not delete it.
    // Once you have called start you must call stop in order to delete it.
    
    // API
    //---------------------------------------------------------------
    // To dereference call the stop method if you have called start . Then dereference touchMouse
    // Example
    touchMouse.stop();
    touchMouse = undefined;
    
    
    // Methods.
    //---------------------------------------------------------------
    // touchMouse.start(element); // element optional. Element is the element to attach listeners to.
                                  // Calling start a second time without calling stop will
                                  // throw a reference error. This is to stop memory leaks.
                                  // YOU Have been warned...
    
    // touchMouse.stop();          // removes listeners and dereferences any DOM objects held
    //---------------------------------------------------------------
    // Properties
    // mouseTouch.clickRadius = 3; // Number. Default 3. If touch start and end within 3 pixels then may be a click
    // mouseTouch.clickTime = 200; // Number. Default 200. If touch start and end in under this time in ms then may be a click
    // mouseTouch.generateClick;   // Boolean. Default true. If true simulates onClick event
    //                                    // if false only generate mousedown, mousemove, and mouseup
    // mouseTouch.clickOnly;      // Boolean.  Default false. If true only generate click events Default false
    // mouseTouch.status;         // String. Just for debugging kinda pointless really. 
    

    希望对您的代码有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-15
      • 1970-01-01
      • 1970-01-01
      • 2015-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多