惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
Last Week in AI
Last Week in AI
月光博客
月光博客
D
DataBreaches.Net
WordPress大学
WordPress大学
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
Recent Announcements
Recent Announcements
宝玉的分享
宝玉的分享
MyScale Blog
MyScale Blog
C
Check Point Blog
F
Fortinet All Blogs
B
Blog
小众软件
小众软件
Vercel News
Vercel News
罗磊的独立博客
有赞技术团队
有赞技术团队

博客园 - SKILL·NULL

如何实现超长数字:先缩小字号,再省略号 如何通过传参来实现SVG排序图标的三种状态 如何为GIT设置全局勾子,为每次提交追加信息 一文了解大模型、小模型与各类神经网络的关系 如何在Mac上调整外星人鼠标AW720M的灯光颜色 Karabiner-Elements最常用配置 IndexedDB封装 echarts获取坐标上的点距离顶部底部高度 Let`s Encrypt 生成免费自动续签 HTTPS 证书 H5滚动截取长图 ReactNative常见问题及处理 根据.nvmrc自动切换项目所需node版本 Command PhaseScriptExecution failed with a nonzero exit code echarts双Y轴,实现均分为包含刻度0的指定段数,同时对齐刻度 env(safe-area-inset-bottom) 兼容写法 缩放实现0.5px 禁止 IOS 橡皮筋效果 JS 拦截浏览器返回 海康威视DS-IPC-E42H-IWPT监控画面竖线处理 Echarts 5 动态按需引入图表 React 18 自定义 Hook 获取 useState 最新值 处理报错 ResizeObserver loop completed with undelivered notifications.
获取指定 dom 的 touchmove 方向及到达边缘时是否禁止橡皮筋效果
SKILL·NULL · 2026-08-27 · via 博客园 - SKILL·NULL
export type GET_MOVE_DIRECTION_INFO = {
    moveX: number;
    moveY: number;
};

export type GET_MOVE_DIRECTION = {
    dom?: string | Element;
    notEdge?: boolean | null;
    edgeStop?: boolean | null;
    /** 为 true 时 callback 第二个参数返回额外信息 */
    otherInfo?: boolean | null;
    callback: (direction: string, info?: GET_MOVE_DIRECTION_INFO) => void;
};

/**
 * @Function 获取指定 dom 的 touchmove 方向
 * @props {
 *   dom: 指定 dom(选择器或元素)
 *   notEdge: 不达到边缘就触发回调,默认 false
 *   edgeStop: 到达边缘时是否禁止橡皮筋效果,默认 false
 *   otherInfo: 是否返回额外信息,默认 false
 *   callback: 回调函数,返回方向;otherInfo 为 true 时额外返回 { moveX, moveY }
 * }
 */
export class GetMoveDirection {
    startX: number;
    startY: number;
    dom: Element | Document | null;
    props: GET_MOVE_DIRECTION;
    private listenerOptions: AddEventListenerOptions;

    constructor(props: GET_MOVE_DIRECTION) {
        this.startX = 0;
        this.startY = 0;
        if (!props?.dom) {
            this.dom = document;
        } else if (typeof props.dom === "string") {
            this.dom = document?.querySelector(props.dom);
        } else {
            this.dom = props.dom;
        }
        this.removeAllListent = this.removeAllListent.bind(this);
        this.listenTouchstart = this.listenTouchstart.bind(this);
        this.listenTouchmove = this.listenTouchmove.bind(this);
        this.props = props;
        // notEdge:捕获阶段,避免子元素 stopPropagation 拦掉 callback
        // edgeStop:passive:false,才能在边缘 preventDefault 禁用橡皮筋效果
        this.listenerOptions = {
            capture: !!props?.notEdge,
            passive: !props?.edgeStop,
        };
        this.dom?.addEventListener(
            "touchstart",
            this.listenTouchstart,
            this.listenerOptions
        );
        this.dom?.addEventListener(
            "touchmove",
            this.listenTouchmove,
            this.listenerOptions
        );
    }

    private getScrollDom(): HTMLElement | null {
        if (!this.dom) return null;
        return this.dom instanceof Document
            ? document.documentElement
            : (this.dom as HTMLElement);
    }

    private isAtLeftEdge(dom: HTMLElement) {
        return dom.scrollLeft === 0;
    }

    private isAtRightEdge(dom: HTMLElement) {
        return (
            Math.ceil(dom.scrollLeft + dom.offsetWidth) >= dom.scrollWidth &&
            Math.ceil(dom.scrollLeft + dom.clientWidth) >= dom.scrollWidth
        );
    }

    private isAtTopEdge(dom: HTMLElement) {
        return dom.scrollTop === 0;
    }

    private isAtBottomEdge(dom: HTMLElement) {
        return (
            Math.ceil(dom.scrollTop + dom.offsetHeight) >= dom.scrollHeight &&
            Math.ceil(dom.scrollTop + dom.clientHeight) >= dom.scrollHeight
        );
    }

    private applyEdgeStop(ev: TouchEvent, atEdge: boolean, axis: "x" | "y") {
        if (!this.props?.edgeStop || !atEdge) return;
        ev.cancelable && ev.preventDefault();
        if (axis === "y") {
            ev.stopPropagation();
        }
    }

    private emitDirection(direction: string, info: GET_MOVE_DIRECTION_INFO) {
        if (this.props?.otherInfo) {
            this.props.callback(direction, info);
        } else {
            this.props.callback(direction);
        }
    }

    listenTouchstart(ev: any) {
        this.startX = ev?.changedTouches?.[0]?.pageX;
        this.startY = ev?.changedTouches?.[0]?.pageY;
    }

    listenTouchmove(ev: any) {
        const moveEndX = ev?.changedTouches?.[0]?.pageX;
        const moveEndY = ev?.changedTouches?.[0]?.pageY;
        const moveX = moveEndX - this.startX;
        const moveY = moveEndY - this.startY;
        const _dom = this.getScrollDom();
        if (!_dom) return;

        const absX = Math.abs(moveX);
        const absY = Math.abs(moveY);
        const info: GET_MOVE_DIRECTION_INFO = { moveX, moveY };

        // 向右拖拽
        if (absX > absY && moveX > 0) {
            const atEdge = this.isAtLeftEdge(_dom);
            if (this.props?.notEdge || atEdge) {
                this.emitDirection("right", info);
            }
            this.applyEdgeStop(ev, atEdge, "x");
        }

        // 向左拖拽
        if (absX > absY && moveX < 0) {
            const atEdge = this.isAtRightEdge(_dom);
            if (this.props?.notEdge || atEdge) {
                this.emitDirection("left", info);
            }
            this.applyEdgeStop(ev, atEdge, "x");
        }

        // 向下拖拽
        if (absY > absX && moveY > 0) {
            const atEdge = this.isAtTopEdge(_dom);
            if (this.props?.notEdge || atEdge) {
                this.emitDirection("down", info);
            }
            this.applyEdgeStop(ev, atEdge, "y");
        }

        // 向上拖拽
        if (absY > absX && moveY < 0) {
            const atEdge = this.isAtBottomEdge(_dom);
            if (this.props?.notEdge || atEdge) {
                this.emitDirection("up", info);
            }
            this.applyEdgeStop(ev, atEdge, "y");
        }
    }

    removeAllListent() {
        this.dom?.removeEventListener(
            "touchstart",
            this.listenTouchstart,
            this.listenerOptions
        );
        this.dom?.removeEventListener(
            "touchmove",
            this.listenTouchmove,
            this.listenerOptions
        );
    }
}