TypeScript(TS) 自定义绑定快捷键

 有很多软件中都可以让用户自定义快捷键

如微信中的快捷键:

思路:

1. 将快捷键分为两部分:

    a. 主要的键   'shift', 'ctrl', 'alt', 'command';

    b. 非主要的键  字母键、数字键等;

2. 键盘按下事件:比较按键和绑定的快捷键是否相同

 代码实现

/**
 * 快捷键信息对象
 */
interface Shortcuts {
    // 快捷键
    readonly shortcut:string;
    // 名称
    readonly name: string;
    // 执行的方法
    readonly callback:Function,
    // 字母键
    readonly key: string,
    // 主组合键
    readonly modifiers: string[],
}

const KEY_SHORTCUT = ['shift', 'ctrl', 'alt', 'command'];


/**
 * 快捷键
 */
export default class ShortcutBinder {

    // 存放快捷键信息的集合
    private readonly shortcuts:Shortcuts[];
    // 新快捷键  --->   默认快捷键
    private readonly defaultShortcutsMap: Map<string, string> = new Map<string, string>();
    private readonly defaultShortcuts:Shortcuts[];

    private static shortcutBinder:ShortcutBinder;

    // 单例
    public static getInstance(){
        if (!ShortcutBinder.shortcutBinder){
            ShortcutBinder.shortcutBinder = new ShortcutBinder()
        }
        return ShortcutBinder.shortcutBinder;
    }


    private constructor() {
        this.shortcuts = [];
        this.defaultShortcuts = [];

        // 初始化
        this.init();
    }

    private init() {
        this.addKeydownEvent();
    }

    /**
     * 添加快捷键
     */
    private addKeydownEvent() {
        // 全局 键盘按下事件
        document.addEventListener('keydown', (event) => {
            console.log("按键:", event.keyCode, event.code, event.key)
            const modifers = getModifierKeysByKeyboardEvent(eventInfo.event);
            const matchedShortcut = this.shortcuts.find(shortcut =>
                    // 判断字母按键是否相同
                    checkKeyMatch(shortcut.key, event.key.toLowerCase()) &&
                    // 判断非字母按键是否相同
                    checkModifiersMatch(shortcut.modifiers, modifers)
            );

            if (matchedShortcut !== undefined) {
                // 执行函数
                matchedShortcut.callback(event);
            }
        });
    }

    /**
     * 绑定默认快捷键和对应执行的方法
     * @param {string} shortcut
     * @param {Function} callback
     */
    bind(shortcut:string, name:string, callback:Function) {
        this.addShortcut(shortcut, name, callback);
        // 存放默认快捷键
        this.defaultShortcutsMap.set(shortcut, shortcut);
        // 克隆默认快捷键
        this.defaultShortcuts.push(this.shortcuts[this.shortcuts.length-1])
    }

    /**
     * 修改绑定的快捷键
     * @param newShortcut 新组合键
     * @param oldShortcut 旧组合键
     */
    editBind(newShortcut:string, oldShortcut:string){
        if (newShortcut === oldShortcut){
            // 新键和旧键一致 不做处理
            return;
        }
        let flag = false;
        // 获取快捷键
        let shortcutObj:Shortcuts|undefined = undefined;
        let len = this.shortcuts.length;
        // 当前位置
        let c = 0;
        for (let i = 0; i < len; i++) {
            if (oldShortcut === this.shortcuts[i].shortcut){
                shortcutObj = this.shortcuts[i];
                c = i;
            }
            if (newShortcut === this.shortcuts[i].shortcut){
                // 新快捷键与原有的冲突
                flag = true;
            }
            if (shortcutObj !== undefined && flag){
                break
            }
        }
        if (flag){
            // TODO 弹窗提示 快捷键冲突
            return;
        }
        if (shortcutObj === undefined){
            // TODO 弹窗提示没有该组合键
            return;
        }
        // 添加新的快捷键
        this.addShortcut(newShortcut, shortcutObj.name, shortcutObj.callback);
        // 删除原来的快捷键
        this.shortcuts.splice(c, 1);

        // 如果 旧快捷键 是默认键添加到映射
        let shortcutSlt = this.defaultShortcutsMap.get(oldShortcut);
        if (shortcutSlt !== undefined){
            // 删除原始的
            this.defaultShortcutsMap.delete(oldShortcut);
            // 添加新映射关系
            this.defaultShortcutsMap.set(newShortcut, shortcutSlt);
        }

    }

    /**
     * 重置绑定的快捷键
     * @param shortcut
     */
    resettingBind(shortcut?:string){
        if (shortcut !== undefined){
            // 重置一个组合键
            let oldShortcut = this.defaultShortcutsMap.get(shortcut);
            if (oldShortcut !== undefined){
                this.editBind(oldShortcut, shortcut);
            }
            return;
        }
        // 重置所有组合键
        this.shortcuts.length = 0;
        this.defaultShortcuts.forEach(ds => {
            this.shortcuts.push(ds);
        })

    }

    /**
     * 获取快捷键集合
     */
    getShortcuts(){
        return this.shortcuts;
    }


    /**
     * 添加绑定的快捷键
     * @param {string} shortcut 快捷键
     * @param {Function} callback 执行的方法
     */
    private addShortcut(shortcut:string, name:string, callback:Function) {
        this.shortcuts.push({
            // 快捷键
            shortcut,
            // 名称
            name,
            // 执行的方法
            callback,
            // 字母按键 注意: 字母按键 不区分大小写但event.key区分大小写
            key: this.getKeyByShortcut(shortcut),
            // 主按键  'shift', 'ctrl', 'alt', 'command'
            modifiers: this.getModifiersByShortcut(shortcut),
        });
    }


    /**
     * 获取字母按键
     * @param {string} shortcut 快捷键的组合
     * @returns {string}
     */
    private getKeyByShortcut(shortcut:string) {
        if (!shortcut.trim()) return '';
        let key = (shortcut.split('+').filter((key) => !KEY_SHORTCUT.includes(key.trim().toLowerCase()))[0] || '');
        return key.toLowerCase();
    }

    /**
     * 获取主按键 ['shift', 'ctrl', 'alt', 'command']
     * @param {string} shortcut 快捷键的组合
     * @returns {Array}
     */
    private getModifiersByShortcut(shortcut:string) {
        const keys = shortcut.split('+').map((key) => key.trim().toLowerCase());
        let modifiers:string[] = [];
        keys.forEach((key) => {
            if (KEY_SHORTCUT.includes(key)) {
                modifiers.push(key);
            }
        });

        return modifiers;
    }
}



/**
 * 统一主按键
 * @param {KeyboardEvent} event - keyboard event
 * @returns {Array}
 */
const getModifierKeysByKeyboardEvent = (event:KeyboardEvent) => {
    const modifiers = [];

    if (event.shiftKey) {
        modifiers.push('shift');
    }

    if (event.altKey) {
        modifiers.push('alt');
    }

    if (event.ctrlKey) {
        modifiers.push('ctrl');
    }

    if (event.metaKey) {
        modifiers.push('command');
    }
    return modifiers;
};


/**
 * 判断按下的非字母键是否相同
 * @param {Array} modifers1
 * @param {Array} modifers2
 * @returns {boolean}
 */
function checkModifiersMatch(modifers1:string[], modifers2:string[]) {
    return modifers1.sort().join(',') === modifers2.sort().join(',');
}

/**
 * 判断按下的 字母键 是否相同
 * @param {string} shortcutKey
 * @param {string} eventKey - event.key
 * @returns {boolean}
 */
function checkKeyMatch(shortcutKey:string, eventKey:string) {

    return shortcutKey === eventKey;
}
 使用方式:
let shortcutBinder = ShortcutBinder.getInstance();
 // enter 菜单快捷键
shortcutBinder.bind('enter', "快速标记菜单",() => {
  console.log('enter');
});
shortcutBinder.bind('ctrl+s', '保存', () => {
  console.log('ctrl+s');
});
注意:

由于 event.key 区分受 shift 的影响,如:字母键区分大小写;

可以考虑 event.keyCode, event.code 来代替非主键('shift', 'ctrl', 'alt', 'command')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值