草庐IT

javascript - Canvas 在移动网络上获得触摸位置

coder 2024-07-25 原文

我有一个代码,它从 (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

最佳答案

从触摸事件生成鼠标事件

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

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

处理两者

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

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

以编程方式创建事件。

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

最基本的。

创建鼠标点击事件并将其分派(dispatch)到文档

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

您还可以将事件分派(dispatch)给单个元素。
  document.getElementById("someButton").dispatchEvent(event);

监听事件与监听实际鼠标相同。
  document.getElementById("someButton").addEventListener(function(event){
        // your code
  ));
MouseEvent 中的第二个参数功能是您可以添加有关事件的额外信息的地方。比如说 clientXclientY鼠标的位置,或whichbuttons正在按下哪个按钮。

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

触摸事件。

触摸事件类似于鼠标。有touchstart , touchmove , 和 touchend .它们的不同之处在于它们提供一系列位置,每个接触点一个项目。不确定最大值是多少,但对于这个答案,我们只对一个感兴趣。见 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);

现在您的鼠标监听器将收到 mousedown , mousemove , mouseup当用户仅在一个位置触摸设备时发生的事件。

缺少点击

到目前为止一切都很好,但缺少一个鼠标事件,这也是必需的。 “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. 

所以希望对你的代码有所帮助。

关于javascript - Canvas 在移动网络上获得触摸位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33951943/

有关javascript - Canvas 在移动网络上获得触摸位置的更多相关文章

  1. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  2. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

  3. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  4. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

  5. ruby - 正则表达式在哪个位置失败? - 2

    我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

  6. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  7. ruby - 下载位置 Selenium-webdriver Cucumber Chrome - 2

    我将Cucumber与Ruby结合使用。通过Selenium-Webdriver在Chrome中运行测试时,我想将下载位置更改为测试文件夹而不是用户下载文件夹。我当前的chrome驱动程序是这样设置的:Capybara.default_driver=:seleniumCapybara.register_driver:seleniumdo|app|Capybara::Selenium::Driver.new(app,:browser=>:chrome,desired_capabilities:{'chromeOptions'=>{'args'=>%w{window-size=1920,1

  8. ruby - Heroku production.log 文件位置 - 2

    我想在heroku.com上查看我的应用程序日志的内容,所以我关注了thisexcellentadvice并拥有我所有的日志内容。但是我现在很想知道我的日志文件实际在哪里,因为“log/production.log”似乎是空的:C:\>herokuconsoleRubyconsoleforajpbrevx.heroku.com>>files=Dir.glob("*")=>["public","tmp","spec","Rakefile","doc","config.ru","app","config","lib","README","Gemfile.lock","vendor","sc

  9. ruby - 在 Ruby 中查找多个正则表达式匹配的模式和位置 - 2

    这应该是一个简单的问题,但我找不到任何相关信息。给定一个Ruby中的正则表达式,对于每个匹配项,我需要检索匹配的模式$1、$2,但我还需要匹配位置。我知道=~运算符为我提供了第一个匹配项的位置,而string.scan(/regex/)为我提供了所有匹配模式。如果可能,我需要在同一步骤中获得两个结果。 最佳答案 MatchDatastring.scan(regex)do$1#Patternatfirstposition$2#Patternatsecondposition$~.offset(1)#Startingandendingpo

  10. ruby-on-rails - 尝试打开 .gitignore 以在文本编辑器中对其进行编辑,但在 OS X Mountain Lion 上找不到文件位置 - 2

    我使用“newapp_name”创建了一个新的Rails应用程序,我正在尝试编辑.gitignore文件,但在我的应用程序文件夹中找不到它。我在哪里可以找到它?我安装了Git。 最佳答案 .gitignore位于项目的root中,而不是app子目录中。首先打开终端并进入您的目录。您需要使用ls-a来显示stash文件。然后使用打开.gitignore 关于ruby-on-rails-尝试打开.gitignore以在文本编辑器中对其进行编辑,但在OSXMountainLion上找不到文件位

随机推荐