likes
comments
collection
share

鼠标事件之外,认识一下指针事件家族?

作者站长头像
站长
· 阅读数 8

鼠标事件之外,认识一下指针事件家族?

一、前言

相信大多数开发者,都知道或用过鼠标事件,例如 mousedown 和 mouseup,而指针事件家族,例如 pointerdown 和 pointerup,用到的人则相对比较少了。

我是因为遇到:需要把鼠标事件重新定向到特定元素,从而实现捕获鼠标绑定到特定元素的需求,用到了 setCapture(),而 setCapture 已经被弃用且是非标准的,然后选用了 setPointerCapture。不得了,一下子捅了 PointerEvent 指针事件家族的马蜂窝。

以实现“拖动边界线改变布局”为例,用了 setPointerCapture 就得用 releasePointerCapture,而在实现需求过程中,还会接着使用 pointerdown、pointermove以及pointerup。

本文,将以实现“滑块”和“拖动边界线改变布局”的需求为例,以 setPointerCapture 的使用为引,介绍:

1、MouseEvent 和 PointerEvent 家族的介绍

2、简述 setCapture 和 setPointerCapture

3、PointerEvent 的应用场景

二、MouseEvent 和 PointerEvent 家族的介绍

2.1 MouseEvent 家族

MouseEvent 接口指用户与指针设备(如鼠标)交互时发生的事件。使用此接口的常见事件包括:click、dblclick、mouseup、mousedown。

MouseEvent 派生自 UIEvent,UIEvent 派生自 Event。虽然 MouseEvent.initMouseEvent() 方法保持向后兼容性,但是应该使用 MouseEvent() 构造函数创建一个 MouseEvent 对象。

一些具体的事件都派生自 MouseEvent:WheelEvent 和DragEvent。

鼠标事件之外,认识一下指针事件家族?

它的一些相关事件:

  • setCapture 用于把全部的鼠标事件重新定向到指定元素。
  • releaseCapture 用来将鼠标从先前通过 setCapture() 绑定的元素身上释放出来。
  • mousedown 事件在定点设备(如鼠标或触摸板)按钮在元素内按下时,会在该元素上触发。
  • mouseenter 事件在定点设备(通常指鼠标)首次移动到元素的激活区域内时,在该元素上触发。
  • mouseleave 事件在定点设备(通常是鼠标)的指针移出某个元素时被触发。
  • mousemove 事件在定点设备(通常指鼠标)的光标在元素内移动时,会在该元素上触发。
  • mouseout 事件在定点设备(通常是鼠标)移动至元素或其子元素之外时,会在该元素上触发。
  • mouseover 当一个定点设备(通常指鼠标)在一个元素本身或者其子元素上移动时,mouseover 事件在该元素上触发。
  • mouseup 事件在定点设备(如鼠标或触摸板)按钮在元素内释放时,在该元素上触发。

2.2 PointerEvent 家族

PointerEvent 接口代表了由 指针 引发的 DOM 事件的状态,包括接触点的位置,引发事件的设备类型,接触表面受到的压力等。

指针 是输入设备的硬件层抽象(比如鼠标,触摸笔,或触摸屏上的一个触摸点)。指针 能指向一个具体表面(如屏幕)上的一个(或一组)坐标。

指针的 击中检测 指浏览器用来检测 指针事件的目标元素的过程。大多数情况下,这个目标元素是由 指针的位置和元素在文章中的位置和分层共同决定的。

该接口属性继承自 MouseEvent 和 Event,所以它们有很多相似的属性和方法,只是 PointerEvent 的作用对象更具体,具体到鼠标指针。

鼠标事件之外,认识一下指针事件家族?

它的一些相关事件:

  • setPointerCapture 用于将特定元素指定为未来指针事件的捕获目标。
  • releasePointerCapture 用来将鼠标指针从先前通过 setPointerCapture() 绑定的元素身上释放出来,还给鼠标指针自由。
  • pointerdown 鼠标指针按下时触发该事件
  • pointerenter 鼠标指针首次进入到元素的激活区域内时触发该事件
  • pointerleave 鼠标指针离开时触发该事件
  • pointermove 鼠标指针移动时触发该事件
  • pointerout 鼠标指针移动到元素之外时触发该事件
  • pointerover 鼠标指针悬浮覆盖时触发该事件
  • pointerup 鼠标指针抬起时触发该事件

三、简述 setCapture 和 setPointerCapture

3.1 特征比较

setCapturesetPointerCapture
是否标准规范中的特性
是否已废弃
浏览器兼容性
配套对应事件releaseCapturereleasePointerCapture

3.2 setCapture 和 setPointerCapture

在处理一个 mousedown 事件过程中调用 setCapture() 方法来把全部的鼠标事件重新定向到这个元素,直到鼠标按钮被释放或者 document.releaseCapture() 被调用。

// 语法

// retargetToElement 如果被设置为 true, 所有事件被直接定向到这个元素; 如果是 false, 事件也可以在这个元素的子元素上触发。
element.setCapture(retargetToElement);

setPointerCapture() 方法用于将特定元素指定为未来指针事件的捕获目标。指针的后续事件将以捕获元素为目标,直到捕获被释放(通过Element.releasePointerCapture())。

// 语法

// pointerId 是指 PointerEvent 对象的 pointerId
element.setPointerCapture(pointerId);

注意: 一旦设置了 pointer capture,pointerover、pointerout、pointerenter 和 pointerleave 事件将不会被触发,直到越过设置了 capture 的元素的边界。 这是因为其他元素将不能再作为 pointer 事件的目标了。这会影响到这些事件在其他元素上的触发。

四、PointerEvent 的应用场景

4.1 PointerEvent 应用场景简述

把全部的鼠标事件重新定向到特定元素,常见有 setCapture 和 setPointerCapture,但是 setCapture 是非标准的,那意味着在一些浏览器可能会不被支持, 而且它又被废弃了,所以 setPointerCapture 通常成为了首选。而常见与之配套使用的 PointerEvent 中的事件有 onpointerdown、onpointermove、onpointerup 以及 releasePointerCapture。

setPointerCapture 的应用场景,或者说把全部的鼠标事件重新定向到特定元素的需求场景,挺常见的,只是通常被封装在了组件里面。例如: 1、滑块组件,拖动滑盖,可视化显示拖动结果,例如一个数值或百分比。

鼠标事件之外,认识一下指针事件家族?

2、滑块校验组件,拖动滑块复位,常用于人机验证场景。

鼠标事件之外,认识一下指针事件家族?

3、拖动边界线改变布局,左右拖动改变左右的元素的宽,这种需求非常常见,也非常常用,而且在各种终端都是。

鼠标事件之外,认识一下指针事件家族?

4、其他:其实和鼠标以及鼠标指针有关的动作效果、动画效果、操作行为,PointerEvent 都有很广的应用场景,尤其在游戏领域中。

4.2 PointerEvent 案例:实现滑块

下面是一个 PointerEvent 实现滑块的示例 Demo,Demo 中,使用了 PointerEvent 中的:

①onpointerdown

②onpointermove

③onpointerup

④setPointerCapture

⑤releasePointerCapture

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>滑块拖动 Demo</title>
    <style>
        .slider-box {
            background: #1e87f0;
        }
        #slider {
            width: 140px;
            height: 50px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: red;
            cursor: pointer;
            user-select:none;
            border-radius: 20px;
        }
    </style>
</head>
<body>
<h1> 滑块拖动 setPointerCapture 示例 Demo</h1>
<h1> 在小小的滑槽里,滑呀滑呀滑</h1>
<div class="slider-box">
    <div id="slider">滑呀滑</div>
</div>
<script>
  function beginSliding(e) {
    slider.onpointermove = slide;
    slider.setPointerCapture(e.pointerId);
  }

  function stopSliding(e) {
    slider.onpointermove = null;
    slider.releasePointerCapture(e.pointerId);
  }

  function slide(e) {
    slider.style.transform = `translate(${e.clientX - 70}px)`;
  }

  const slider = document.getElementById('slider');

  slider.onpointerdown = beginSliding;
  slider.onpointerup = stopSliding;

</script>
</body>
</html>

Demo 效果图:

鼠标事件之外,认识一下指针事件家族?

4.3 PointerEvent 案例:拖动边界线改变布局

这是一个通过拖动边界线改变宽度,从而改变布局的 Demo,Demo 中,使用了 PointerEvent 中的:

①onpointerdown

②onpointermove

③onpointerup

④setPointerCapture

⑤releasePointerCapture

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>左右拖动div,改变左右元素的宽度</title>
    <style>
        #box {
            width: 100vw;
            height: 100vh;
            overflow: hidden;
        }

        #left {
            width: calc(20% - 5px);
            height: 100%;
            background: #87eb8c;
            float: left;
        }

        #resize {
            width: 5px;
            height: 100%;
            cursor: col-resize;
            float: left;
        }

        #right {
            float: right;
            width: 80%;
            height: 100%;
            background: #ffc547;
        }
    </style>
</head>

<body>
<div id="box">
    <div id="left">left</div>
    <div id="resize"></div>
    <div id="right">right</div>
</div>
<script>
    window.onload = function () {
        let left = document.getElementById("left");
        let resize = document.getElementById("resize");
        let right = document.getElementById("right");
        let box = document.getElementById("box");
        resize.onpointerdown = function (e) {
            const startX = e.clientX;
            resize.left = resize.offsetLeft;
            resize.onpointermove = function (em) {
                const endX = em.clientX;
                let moveL = resize.left + (endX - startX);
                let maxT = box.clientWidth - resize.offsetWidth;
                const step = 100
                if (moveL < step) moveL = step;
                if (moveL > maxT - step) moveL = maxT - step;

                resize.style.left = moveL;
                left.style.width = moveL + "px";
                right.style.width = (box.clientWidth - moveL - 5) + "px";
            }
            resize.onpointerup = function (et) {
                resize.onpointermove = null;
                resize.onpointerup = null;
                resize.releasePointerCapture(et.pointerId);
            }
            resize.setPointerCapture(e.pointerId);
            return false;
        }
    }
</script>
</body>

</html>

这是该 Demo 的效果图:

鼠标事件之外,认识一下指针事件家族?