【问题标题】:Detect touchpad vs mouse in Javascript在Javascript中检测触摸板与鼠标
【发布时间】:2012-05-31 11:01:37
【问题描述】:

有什么方法可以检测客户端使用的是触摸板还是带有 Javascript 的鼠标?

或者至少对使用触摸板而不是鼠标的用户数量进行合理估计?

【问题讨论】:

  • @apsillers:触摸板鼠标设备。

标签: javascript mouse touchpad trackpad


【解决方案1】:

这个话题可能已经解决了,但答案是没有办法检测到它。好吧,我需要找到解决方案,这非常重要。所以我为这个问题找到了一个可以接受的解决方案:

var scrolling = false;
var oldTime = 0;
var newTime = 0;
var isTouchPad;
var eventCount = 0;
var eventCountStart;

var mouseHandle = function (evt) {
    var isTouchPadDefined = isTouchPad || typeof isTouchPad !== "undefined";
    console.log(isTouchPadDefined);
    if (!isTouchPadDefined) {
        if (eventCount === 0) {
            eventCountStart = new Date().getTime();
        }

        eventCount++;

        if (new Date().getTime() - eventCountStart > 100) {
                if (eventCount > 10) {
                    isTouchPad = true;
                } else {
                    isTouchPad = false;
                }
            isTouchPadDefined = true;
        }
    }

    if (isTouchPadDefined) {
        // here you can do what you want
        // i just wanted the direction, for swiping, so i have to prevent
        // the multiple event calls to trigger multiple unwanted actions (trackpad)
        if (!evt) evt = event;
        var direction = (evt.detail<0 || evt.wheelDelta>0) ? 1 : -1;

        if (isTouchPad) {
            newTime = new Date().getTime();

            if (!scrolling && newTime-oldTime > 550 ) {
                scrolling = true;
                if (direction < 0) {
                    // swipe down
                } else {
                    // swipe up
                }
                setTimeout(function() {oldTime = new Date().getTime();scrolling = false}, 500);
            }
        } else {
            if (direction < 0) {
                // swipe down
            } else {
                // swipe up
            }
        }
    }
}

并注册事件:

document.addEventListener("mousewheel", mouseHandle, false);
document.addEventListener("DOMMouseScroll", mouseHandle, false);

它可能需要一些优化并且可能不够完美,但它确实有效!至少它可以检测到macbook触控板。但是由于设计的原因,我想说它应该可以在 pad 引入大量事件调用的任何地方工作。

这是它的工作原理:

当用户第一次滚动时,它会检测并检查在 50 毫秒内触发的事件不超过 5 个,这对于普通鼠标来说是很不寻常的,但对于触控板来说则不然。

然后是else部分,这不是为了检测的重要性,而是像用户滑动一样调用一次函数的技巧。如果我不够清楚,请来找我,让这个工作非常棘手,当然不是理想的解决方法。

编辑:我现在尽可能地优化了代码。它第二次检测到鼠标滚动并立即在触控板上滑动。还删除了很多重复和不必要的代码。

编辑 2 我将时间检查的数字和调用的事件数分别从 50 更改为 100 和从 5 更改为 10。这应该会产生更准确的检测。

【讨论】:

  • Here 是这个代码的一个小提琴,以便更容易地测试最终结果。
  • 谢谢。我现在在我的生产站点上使用此代码,它运行良好。我修改了你的小提琴以获得即时结果:jsfiddle.net/ucLe3hLa/1
  • “50 毫秒内不超过 5 个事件”的假设不适用于我的鼠标。你是怎么想到的?
  • 老实说,如果我稍微触摸一下触控板,它就会认为我使用的是鼠标。 =(
  • 只有当你在触摸板上快速移动时才有效 - 如果我使用这个精确的触摸板慢慢滚动,它会说它是鼠标:(
【解决方案2】:

比较 e.wheelDeltaY 和 e.deltaY(或 Firefox 中的 e.deltaMode)来检测触摸板鼠标设备

function handler(e) {
    var isTouchPad = e.wheelDeltaY ? e.wheelDeltaY === -3 * e.deltaY : e.deltaMode === 0
    // your code
    document.body.textContent = isTouchPad ? "isTouchPad" : "isMouse"
}
document.addEventListener("mousewheel", handler, false);
document.addEventListener("DOMMouseScroll", handler, false);

【讨论】:

  • 这个工作原理的概念性解释,以及这里的一些类似的替代策略stackoverflow.com/a/62415754/1209832
  • 刚刚运行您的代码并用我的触摸板进行了测试,男孩确实在isTouchPadisMouse 之间快速切换...但是当使用我的鼠标时它根本没有切换所以肯定有这是一些事实。
  • 不能使用魔术鼠标(有 isTouchPad)
【解决方案3】:

Lauri 的上述答案似乎有效,但我花了一段时间才明白 为什么 它有效。所以在这里我将提供一个更易于阅读的版本,以及一个概念性的解释。首先,以人类可读的方式写出相同的代码:

function detectTrackPad(e) {
  var isTrackpad = false;
  if (e.wheelDeltaY) {
    if (e.wheelDeltaY === (e.deltaY * -3)) {
      isTrackpad = true;
    }
  }
  else if (e.deltaMode === 0) {
    isTrackpad = true;
  }
  console.log(isTrackpad ? "Trackpad detected" : "Mousewheel detected");
}

document.addEventListener("mousewheel", detectTrackPad, false);
document.addEventListener("DOMMouseScroll", detectTrackPad, false);

之所以有效,是因为 wheelDeltaY 测量的是实际硬件鼠标滚轮经过的物理距离,而 deltaY 测量的是屏幕上产生的滚动量。传统鼠标的“滚动分辨率”通常比触控板低得多。也就是说,使用触控板,您可以做出微小的动作并在屏幕上进行微小的滚动。传统的鼠标在更粗的、低分辨率的点击中滚动。要完成鼠标滚轮的完整旋转,可能需要单击 10 次。没有半点击或四分之一点击之类的东西。

对于传统鼠标,单次滚轮单击报告为 120 个 wheelDeltaY“单位”,并导致 大约 ~100px 的滚动。物理 wheelDeltaY 单位是一个完全任意的数字,它不是测量英寸或度数或类似的东西。号码 120 被选中 simply because it has a lot of useful factors。屏幕上的滚动量由 deltaY 表示,它是varies significantly by browser。 (旁注,deltaY 通常以“线”而不是像素来衡量,尽管它很复杂,请参阅上一个链接。

与触控板的交互有两种不同的方式。首先,您可以获得比 120 小得多的 wheelDeltaY 值,因为可以检测到非常细微的手指手势。其次,wheelDeltaY 正好是 deltaY 值的 3 倍(至少在我设法测试的每个浏览器中)。因此,例如,如果您做出等于 12 个单击单位的物理手指手势,通常会导致 4 个像素的滚动。 Lauri 的代码使用第二个属性 (Y1 = Y2 * 3) 来检测触控板的存在,但您也可以通过检查 abs(wheelDeltaY) 是否等于 120 来成功

我没有对此进行测试,但我认为它也可以:

function detectTrackPad(e) {
  var isTrackpad = false;
  if (e.wheelDeltaY) {
    if (Math.abs(e.wheelDeltaY) !== 120) {
      isTrackpad = true;
    }
  }
  else if (e.deltaMode === 0) {
    isTrackpad = true;
  }
  console.log(isTrackpad ? "Trackpad detected" : "Mousewheel detected");
}

document.addEventListener("mousewheel", detectTrackPad, false);
document.addEventListener("DOMMouseScroll", detectTrackPad, false);

【讨论】:

  • 优秀的解决方案,按预期工作。请注意 - 如果您在 jquery 的 .on('mousewhell wheel') ... 回调中使用它,那么不要忘记传递原始事件,而不是该事件的包装器:detectTrackPad(e.originalEvent)
  • 如果您可以从这个(Y 方向)获得方向和幅度而不需要滚动窗口(例如,如果 y-overflow: hidden)会很好
  • Safari 为单击一次滚轮提供了 wheelDelta 12 而不是 120,并为快速滚动执行了一些奇怪的加速度计算。其他浏览器也可能显示 240 等,用于事件中的多个滚动。
【解决方案4】:

在一般情况下,没有办法做你想做的事。 ActiveX 可能允许您查看和检查 USB 设备,但在最好的情况下,即使有可能,这也将您限制为 IE 用户。除此之外,没有办法知道。

您或许能够辨别触摸板用户移动光标的方式(或频率)与鼠标用户移动光标的方式。以这种方式区分物理输入设备是一个非常困难的前景,并且可能完全不可能,因此我将其包含在此处只是为了完整。

【讨论】:

    【解决方案5】:

    您可以检测 JS 事件。

    除了鼠标事件之外,触摸设备还会触发触摸事件,例如touchstart

    非触控设备只会触发鼠标事件。

    【讨论】:

    • 对于最近查看此问题的任何人,我一直在四处寻找,我认为并非所有浏览器都是如此,如果有的话。很想看到显示触控板触发触摸事件的来源。
    • @minorcase 嗯......现在我重新阅读了这个问题,我不确定 OP 是指的是手持设备,还是任何触摸输入外围设备。我同意,触摸板(例如,连接到计算机)不会被 视为 为触摸设备。我的回答专门针对整个设备(即移动浏览器与桌面浏览器)。
    • 啊,明白了。我只是假设它们的意思是像触控板,但我也只是重新阅读了它,措辞有点模棱两可。我只是假设他们的意思是触控板,但如果他们的意思是触摸设备,那么你的答案是非常正确的!感谢您的澄清。
    【解决方案6】:

    触摸板触发的滚轮事件会产生更小的 event.deltaY,1 或 2,但鼠标滚轮触发会产生 100,200 左右。

    【讨论】:

    • 这是最好的答案。谢谢!
    【解决方案7】:

    从测试插入鼠标到带有触摸板的 mac 以及带有触摸板的 windows 机器,我可以总结一下我是如何工作的。

    1. 检测导航器用户代理是否包含“Mobile”或“Mac OS” 这些都是真的,它可能是一个基于触摸的系统,但努力 消除它。将 boolean hasTouchPad 设置为 true

    2. 如果上述情况属实,则检测“鼠标”事件并运行类似 high 的测试 数字,或非整数的频率或卡尔曼滤波。

    3. 将这些保留在队列中,如果该队列的总和超过阈值, 禁用 hasTouchpad 变量并断开事件。

    let isMouseCounts: Array<number> = []
    
    if (Client.hasTouchpad) {
        document.addEventListener('wheel', detectMouseType);
    }
    
    function detectMouseType(e:WheelEvent) {
        if (!Client.hasTouchpad) return
    
        let isMouse = e.deltaX === 0 && !Number.isInteger(e.deltaY)
    
        isMouseCounts.push(isMouse ? 1 : 0)
        if (isMouseCounts.length > 5) isMouseCounts.shift()
    
        let sum = isMouseCounts.reduce(function(a, b) { return a + b; });
    
        if (sum > 3 && e.type === "wheel") {
            console.log("Touchpad disabled")
            document.removeEventListener('wheel', detectMouseType);
            Client.hasTouchpad = false;
        }
    }
    

    【讨论】:

    • 这是打字稿,对吧?你能提供一个JS版本吗?这就是问题所要求的,如果不启动整个 webpack 构建,我无法测试这个解决方案。
    【解决方案8】:

    您可以检查安装到本地软件包中的设备驱动程序软件是否正常运行。就像在 windows synaptics、elan 硬件中一样,对于 UNIX(Linux),您可以只检查在基本安装过程中安装的软件包。许多包在不同版本的 Linux 和类似 Linux 的系统中以不同的格式出现(不完全是 linux),但它们使用相同的包名称。刚知道拉它的代码。仍在努力。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-16
      • 1970-01-01
      • 2013-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-03
      相关资源
      最近更新 更多