js PointerEvent指针事件简单介绍

指针事件

早期的浏览器,只存在鼠标事件(MouseEvent)。后来,以智能手机和平板电脑为首的触屏设备开始普及,交互方式发生了改变。但为了使现有功能不受影响,在很多情况下,触摸事件和鼠标事件会相继触发(以使非触摸专用的代码仍然可以与用户交互)。例如轻触屏幕会触发touchstart事件,如果不调用event.preventDefault()会继续触发mousedown事件。但在面对多点触控的时候,鼠标事件就显得无能为力了。因此,引入了触摸事件(TouchEvent)。不过这还不够完美,因为很多其他输入设备(如触控笔)有自己的特性。如果此时推出基于触控笔的API,那后面万一又有新特性的输入设备出现时,又怎么办呢?而且同时维护两份处理鼠标事件和触摸事件的代码已经很笨重了。面对这些问题,W3C急需一套能够整合输入事件的API,指针事件应运而生。指针事件(PointerEvent)是HTML5的事件规范之一,它主要目的是用来将鼠标(Mouse),触摸(Touch)和触控笔(Pen)三种事件整合为统一的API。

449809-20171219114446381-308449891.png

指针事件属性

指针事件属性继承自MouseEventEvent。常用属性例如:clientX,clientY等都有。下面简单介绍指针事件独有的属性。

属性 介绍
pointerId 触发事件的指针的唯一提示
width 指针的接触面的CSS像素宽度
height 指针的接触面的CSS像素高度
pressure 归一化后的指针压力值,范围在0-1之间
tangentialPressure 归一化后的切向压力值,范围在-1-1]之间,0表示控制设备中立状态时的值
tiltX 由输入设备(如手写笔)与Y轴的构成平面,和YZ平面之间的夹角,范围在-90-90之间
tiltY 由输入设备(如手写笔)与X轴构成平面,和XZ平面之间的夹角,范围在-90-90之间
twist 输入设备(如手写笔)围绕自身价值范围旋转的角度,范围在0-359之间
pointerType 表示触发事件的设备类型,mouse,pen,touch
isPrimary 表示一个指针是否是当前设备类型的主指针

指针事件类型

MouseEvent TouchEvent PointerEvent
mousedown touchstart pointerdown
mousemove touchmove pointermove
mouseup touchend pointerup
touchcancel pointercancel
mouseenter pointerenter
mouseleave pointerleave
mouseover pointerover
mouseout pointerout
gotpointercapture
lostpointercapture

使用方式

MouseEvent

我们一般对于MouseEvent事件会这样处理:

<div id="box"></div>

const box = document.getElementById('box');
let isMouseDown = false;
// 将mousedown事件绑定到box元素上
box.addEventListener('mousedown', function (e) {
    isMouseDown = true;
});
// 将mousemove事件绑定到document或window上,防止移动过快丢失目标元素
document.addEventListener('mousemove', function (e) {
    if (isMouseDown) {
        // todo
    }
});
// 将mouseup事件绑定到document或window上,防止在目标元素外释放鼠标
document.addEventListener('mouseup', function (e) {
    isMouseDown = false;
});
复制代码

上述处理方式的问题在于,鼠标在文档周围的移动可能会引起副作用,触发其他元素的事件处理程序。

TouchEvent

<div id="box"></div>

const box = document.getElementById('box');
let point = { x: 0, y: 0 };
let point2 = { x: 0, y: 0 };

box.addEventListener('touchstart', function (e) {
    point = { x: e.touches[0].clientX, y: e.touches[0].clientY };
    // 第二个触摸点
    if (pointers.length > 1) {
	point2 = { x: e.touches[1].clientX, y: e.touches[1].clientY };
    }
});

box.addEventListener('touchmove', function (e) {
    const current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
    // 第二个触摸点
    if (e.touches.length === 1) {
        const current2 = { x: e.touches[1].clientX, y: e.touches[1].clientY };
    }
});

box.addEventListener('touchend', function (e) { });

box.addEventListener('touchcancel', function (e) { });
复制代码

相对于鼠标事件需要将mousemove,mouseup绑定到document上,防止丢失目标元素的问题,在触摸事件中则不会发生。因为touchmove,touchend,touchcancel事件的目标和触发touchstart事件的目标元素相同。

image.png

PointerEvent

对于MouseEvent存在的问题,指针事件有对应的解决方法,就是Element.setPointerCapture()。我们可以在pointerdown事件处理程序中调用box.setPointerCapture(e.pointerId),这样接下来所发生的事件(例如pointerenter,pointerleave,pointerout,pointerover,pointerup,pointercancel)都会被重定向到box上。具体使用如下:

<div id="box"></div>

const box = document.getElementById('box');
let isMouseDown = false;
box.addEventListener('pointerdown', function (e) {
    isMouseDown = true;
});
box.addEventListener('pointermove', function (e) {
    box.setPointerCapture(e.pointerId);
    if (isMouseDown) {
        // todo
    }
});
box.addEventListener('pointerup', function (e) {
    isMouseDown = false;
});
box.addEventListener('pointercancel', function (e) {
    isMouseDown = false;
});
复制代码

如果在移动端使用,请给box元素添加touch-action: none;

多点触控

我们使用TouchEvent实现多点触控的时候,event会返回touches属性,该属性会列出所有当前在与触摸表面接触的Touch对象,不管触摸点是否已经改变或其目标元素是在处于touchstart阶段。但指针事件并没有类似的属性,而是需要我们自己处理。具体实现如下:

<div id="box"></div>

const box = document.getElementById('box');
let pointers = [];
let point = { x: 0, y: 0 };
let point2 = { x: 0, y: 0 };
box.addEventListener('pointerdown', function (e) {
    // 维护一个数组,用于记录当前触摸点
    pointers.push(e);
    point = { x: pointers[0].clientX, y: pointers[0].clientY };
    // 第二个触摸点
    if (pointers.length > 1) {
	point2 = { x: pointers[1].clientX, y: pointers[1].clientY };
    }
});
box.addEventListener('pointermove', function (e) {
    handlePointers(e, 'update');
    const current = { x: pointers[0].clientX, y: pointers[0].clientY };
    if (pointers.length > 1) {
	const current2 = { x: pointers[1].clientX, y: pointers[1].clientY };
    }
});
box.addEventListener('pointerup', function (e) {
    handlePointers(e, 'delete');
});
box.addEventListener('pointercancel', function (e) {
    pointers = [];
});

/**
 * 处理指针
 * @param {PointerEvent} e 
 * @param {string} type 
 */
function handlePointers(e, type) {
    for (let i = 0; i < pointers.length; i++) {
        if (pointers[i].pointerId === e.pointerId) {
            if (type === 'update') {
                pointers[i] = e;
            } else if (type === 'delete') {
                pointers.splice(i, 1);
            }
        }
    }
}
复制代码

以上代码简单实现了多点触控(两个触摸点),如果需要两个以上触摸点的小伙伴需要自己实现哦。效果如下:

GIF 2021-07-07 19-01-01.gif

在线demo:jsdemo.codeman.top/html/pointe…

总结

指针事件允许我们通过一份代码,同时处理鼠标,触摸和触控笔事件。极大的方便了开发者。对指针事件感兴趣的小伙伴不要忘了上手实操一下。毕竟,”纸上得来终觉浅,绝知此事要躬行“。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享