@any-touch/core

any-touch核心程序, 主要用来兼容Mouse/Touch事件.

Usage no npm install needed!

<script type="module">
  import anyTouchCore from 'https://cdn.skypack.dev/@any-touch/core';
</script>

README

any-touch NPM Version NPM Downloads size-image codecov CircleCI

6类手势

  • 支持 PC 端 / 移动端 / 微信小程序.
  • 默认加载6 个手势, 也可按需加载手势, 核心1kb, 完整5kb.
  • 无依赖, 不限于 Vue / React / Angular等...

演示

查看二维码

简单演示

衍生产物: any-scroll(虚拟滚动)

目录

:zap: 快速开始

📐 按需加载

🌈 进阶使用

:bulb: API

:lollipop: 事件对象(event)

🍳 常见问题

安装

npm i -S any-touch

CDN

<script src="https://unpkg.com/any-touch/dist/any-touch.umd.min.js"></script>
<script>
    console.log(AnyTouch.version); // 2.x.x
</script>

快速开始

import AnyTouch from 'any-touch';

const el = document.getElementById('box');
const at = new AnyTouch(el);

// e包含位移/速度/方向等信息
at.on('pan', (e) => console.log(e));

:rocket: 返回目录

vue中的简写

默认所有手势都会触发原生DOM事件, 所以在vue中可在模版元素上直接使用"@tap"等语法监听手势事件.

<template>
    <div 
        @tap="onTap" 
        @swipe="onSwipe" 
        @press="onPress" 
        @pan="onPan" 
        @pinch="onPinch" 
        @rotate="onRotate">
        <p>Hello any-touch</p>
    </div>
</template>

<script>
    import AnyTouch from 'any-touch';
    export default {
        mounted() {
            // 没错, 就这2行
            const at = new AnyTouch(this.$el);
            
            this.$on('hook:destroyed', () => {
                at.destroy();
            });
        },
    };
</script>

注意: vue中 "@tap"这种语法只能用在元素标签上, 而不能用在自定义组件标签上:

<!-- 有效 -->
<div @tap="onTap"></div>

<!-- 不生效, 监听不到tap -->
<my-component @tap="onTap"></my-component>

:rocket: 返回目录

支持微信小程序

由于小程序中没有 dom 元素的概念, 所以我们需要通过catchEvent方法手动接收touch事件的事件对象来进行识别

<view
  @touchstart="at.catchEvent"
  @touchmove="at.catchEvent"
  @touchend="at.catchEvent"
  @touchcancel="at.catchEvent">
</view>
const at = new AnyTouch()
{
    onload(){
        at.on('press', onPress);
    }
}

:rocket: 返回目录

按需加载

默认 any-touch 支持所有手势, 为了更小的体积, 提供了按需加载.

完整引入

// 只加载pan识别器(拖拽)
import AT from 'any-touch';
const at = AT(el);
at.on('tap', (e) => {});
at.on('pan', (e) => {});
// 同时监听多个事件
at.on(['swipe', 'press', 'rotate', 'pinch'], (e) => {});

按需引入

安装"any-touch"时, 对应的"@any-touch/xxx"会自动安装, 直接引入即可.

// 只加载pan识别器(拖拽)
import Core from '@any-touch/core';
import pan from '@any-touch/pan
// Core不识别任何手势.
const at = new Core(el);
// 加载pan
at.use(pan);

at.on('pan', e=>{});

@any-touch/core

手势库的核心组件, 主要用来实现 PC/移动端的兼容(查看更多).

@any-touch/xx 手势识别器

手势识别器均已做成独立的包, 从而实现按需加载.

名称 说明
@any-touch/tap 点击
@any-touch/pan 拖拽
@any-touch/swipe
@any-touch/press 按压
@any-touch/pinch 缩放
@any-touch/rotate 旋转

手势识别器支持事件对照表

识别器 事件名 说明
tap tap 单击
press press / pressup 按压 / 松开
pan pan / panstart / panmove / panend /panup/pandown/panright/panleft 拖拽 / 拖拽开始 / 拖拽进行中 / 拖拽结束/ 上下左右滑动
swipe swipe / swipeup / swipedown / swiperight / swipeleft 快划 / 不同方向快划
pinch pinch / pinchstart / pinchmove / pinchend / pinchin /pinchout 缩放 / 缩放开始 / 缩放进行中 / 缩放结束 / 放大 / 缩小
rotate rotate / rotatestart / rotatemove / rotateend 旋转 / 旋转开始 / 旋转进行中 / 旋转结束
// 拖拽中只出发一次
at.on('panstart', (e) => {
    console.log('拖拽开始');
});

:rocket: 返回目录

进阶使用

阻止默认事件

参数"preventDefault"是一个函数, 可以通过他的返回值的"true/false"来决定是否"阻止默认事件".

比如实现: 阻止多点触发的事件的"默认事件", 比如"pinch/rotate".

const at = new AnyTouch(el, {
    preventDefault(e) {
        return 1 == e.touches.length;
    },
});

参数"e"是原生事件对象, 移动端是TouchEvent, PC端是MouseEvent.

:rocket: 返回目录

typescript

针对自定义的手势, 比如上面的"双击", 在ts中我们需要进行"类型扩充声明", 声明文件如下:

// global.d.ts
import tap from '@any-touch/tap';
declare module '@any-touch/core' {
    // 扩充at.get('doubletap')返回值的类型
    // 如不扩充, get返回的插件实例类型不完整.
    export interface PluginContextMap {
        doubletap: ReturnType<typeof tap>;
    }

    // 扩充at.on('doubletap',e=>{})中的e的类型
    export interface EventMap {
        doubletap: AnyTouchEvent;
    }
}

当然不写声明文件也可以, 偷懒的方法是:

// ⭐让"e"和tap事件的e的类型一致,
// 毕竟都是tap识别器衍生的事件
at.on('doubletap' as 'tap', e=>{});

// ⭐返回tap识别器的实例,
// 其实就是同一个识别器的实例 
at.get('doubletap' as 'tap');

注意: 上面2种写法都ok, 在这里写声明文件和断言其实没区别, 都可以正确的推导出其他部分的类型. :rocket: 返回目录

"at:xxx"事件

宽泛的事件钩子.

名称 说明
at:start 开始触碰/触点增加触发
at:move 触点移动触发
at:end 触点离开触发
at:after 任意事件触发后,其都会被触发

"at:after"的事件对象和其他事件的不同, 多一个name字段, 这个name表示哪个事件引起的"at:after".

at.on('at:after',e=>{
    // ⭐tap/pan/swipe/press/pinch/rotate/at:start/at:move/at:end
    console.log(e.name) 
});

:rocket: 返回目录

常见问题

手势识别器的 name 字段必填

自定义手势一定记得给起一个名字哦, 而且不要和默认存在的手势同名(已有 tap/swipe/pan/rotate/pinch/press).

at.use(tap, { pointLength: 2, name: 'twoFingersTap' });
at.on('twoFingersTap', onTwoFingersTap);

:rocket: 返回目录

不要用 alert 调试

:heavy_exclamation_mark::heavy_exclamation_mark::heavy_exclamation_mark: 在安卓手机的真机上, 如果touchstarttouchmove阶段触发了alert, 会出现后续的touchmove/touchend不触发的 bug. 所以请大家务必避免在手势的事件回调中使用alert. 测试代码

如果仅仅是了在移动端调试, 请使用腾讯的vconsole

:rocket: 返回目录

macos 上的 chrome 浏览器触发 touchend 会比较慢

由于上述原因, swipe 事件发生的会"慢半拍",所以请大家最终测试以手机效果为准.

:rocket: 返回目录

移动端尽量使用 tap 代理 click

在移动端 touchstart 比 click 先触发, 所以 touchstart 阶段的 preventDefault 会阻止 click 触发, 恰恰 any-touch 默认在 touchstart 中使用了preventDefault, 用来阻止了浏览器默认事件的触发,比如 click 和页面滚动.

如果移动端非要使用 click 做如下设置

const at = new AnyTouch(el, { preventDefault: false });

:rocket: 返回目录

使用pnpm提示找不到@any-touch/xxx

这是因为pnpm不像yarn和npm, 其不会把any-touch依赖的包安装到node_modules目录的根, 所以会提示找不到. 参考pnpm说明.

如果使用pnpm, 那么只能通过手动安装包解决.

pnpm i @any-touch/core @any-touch/tap @any-touch/press @any-touch/pan @any-touch/swipe @any-touch/pinch @any-touch/rotate

:rocket: 返回目录