在开发移动 WebApp 时,手势是常用的交互方式,但是 Web 中并未支持支持手势事件,你可能需要使用第三方库(例如:Hammer.js)来支持手势操作,Gem 内置 <gem-gesture> 来完成简单的手势支持。
很久以前,Web 只支持 MouseEvent,随着移动时代的带来,Web 添加了 TouchEvent,现在,除了鼠标以及触摸屏外,可能还有其他的指针设备,比如手写笔。为此,Web 引入了 PointerEvent,它为各种指针设备提供了一组通用事件,为了方便迁移,PointerEvent 类似 MouseEvent 提供了 pointerdown, pointerup, pointermove 等事件。
下面的代码为 <my-element> 添加了 pan 事件:
@customElement('my-element')
class MyElement extends GemElement {
@emitter pan: Emitter;
#start = false;
constructor() {
this.addEventListener('pointerdown', () => {
this.#start = true;
});
this.addEventListener('pointermove', ({ movementX, movementY }) => {
this.pan({ x: movementX, y: movementY });
});
this.addEventListener('pointerup', () => {
this.#start = false;
});
this.addEventListener('pointercancel', () => {
this.#start = false;
});
}
}在使用 MouseEvent, TouchEvent 时,当指针移动过快时,指针可能会离开目标元素。引入 PointerEvent 的同时添加了 Element.setPointerCapture,它能将一系列指针事件绑定到元素,无论指针位于什么位置,它改善了 Web 的手势交互。
@customElement('my-element')
class MyElement extends GemElement {
@emitter pan: Emitter;
#start = false;
constructor() {
this.addEventListener('pointerdown', ({ pointerId }) => {
this.#start = true;
this.setPointerCapture(pointerId);
});
this.addEventListener('pointermove', ({ movementX, movementY }) => {
this.pan({ x: movementX, y: movementY });
});
this.addEventListener('pointerup', () => {
this.#start = false;
});
this.addEventListener('pointercancel', () => {
this.#start = false;
});
}
}在进行手势操作时,可能触发浏览器原生的事件处理器,比如,鼠标点击图片试图平移时会触发拖拽事件,这是会触发 pointercancel 事件导致平移手势中断。CSS 提供的 touch-action 解决了这个问题,touch-action: none 将禁用浏览器的任何触摸动作,以便正常进行平移手势。
html`<my-element @pan=${console.log} style="touch-action: none"></my-element>`;<gem-gesture> 支持简单的手势事件:pan, pinch, rotate, swipe, press:
@customElement('my-app')
class MyApp extends GemElement {
#state = createState({
x: 0,
y: 0
})
#onPan = ({x, y} => this.#state({x: this.#state.x + x, y: this.#state.y + y}))
render = () => {
return html`
<style>
gem-gesture {
width: 100px;
height: 100px;
background: gray;
translate: ${x}px ${y}px;
}
</style>
<gem-gesture @pan=${this.#onPan}></gem-gesture>
`
}
}