美文网首页
useKeyPress

useKeyPress

作者: skoll | 来源:发表于2020-07-28 20:24 被阅读0次
    image.png

    //为什么逻辑感觉都走完了,还会打印出来一遍40呢,又返回之前的地方

    image.png

    还有就时同一个hook,根据日志返现其实被调用了两次,由于使用这个hook,会导致主函数也会render两次。这样做会不会浪费啊

    hook最麻烦的就是不确定自己写的函数到底执行了多少次,虽然看起来效果是对的,但是打印log的时候,一些自己感觉不会再执行到的地方也会打印出东西来

    关键,组合键的实现不支持任意键,只是支持和快捷键的组合

    import { MutableRefObject } from 'react';
    
    // function useRef<T>(initialValue: T): MutableRefObject<T>;
    // interface MutableRefObject<T> {
    //   current: T;
    // }
    
    export type BasicTarget<T = HTMLElement> =
      | (() => T | null)
      | T
      | null
      | MutableRefObject<T | undefined>;
    
    type TargetElement =HTMLElement|Document|Window
    
    export function getTargetElement(
        target?:BasicTarget<TargetElement>,
        defaultElemnt?:TargetElement
    ):TargetElement|undefined|null{
        if(!target){
            return defaultElemnt
        }
    
        let targetElement:TargetElement|undefined|null
    
        if(typeof target ==='function'){
            targetElement=target()
        }else if('current' in target){
            targetElement=target.current
        }else{
            targetElement=target
        }
    
        return targetElement
    }
    
    export function isType(obj:any){
        return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase()
    }
    
    
    
    import {BasicTarget,getTargetElement,isType} from '../src/utils/dom'
    import {useEffect,useCallback,useRef}from 'react'
    
    
    export type keyPredicate=(event:KeyboardEvent)=>boolean
    export type KeyType=KeyboardEvent['keyCode']|KeyboardEvent['key']
    export type KeyFilter=KeyType|Array<KeyType>|((event:KeyboardEvent)=>boolean)
    export type EventHandler=(event:KeyboardEvent)=>void
    export type KeyEvent='keydown'|'keyup'
    export type Target=BasicTarget<HTMLElement|Document|Window>
    
    const defaultEvents:Array<KeyEvent>=["keydown"]
    
    export type EventOption={
        events?:Array<KeyEvent>
        target?:Target
    }
    
    const keyCodeMap:any={
        esc:27,
        tab:9,
        enter:13,
        space:32,
        up:38,
        left:37,
        right:39,
        down:40,
        delete:[8,46]
    }
    
    const keyMap:any={
        esc:"Escape",
        tab:"Tab",
        enter:'Enter',
        sapce:" ",
    
        up:["Up","ArrowUp"],
        left:["Left","ArrwowLeft"],
        right:["Right","ArrowRight"],
        down:["Down","ArrowDown"],
        delete:["backspace","Delete"]
    }
    
    const modifierKey:any={
        ctrl:(event:KeyboardEvent)=>event.ctrlKey,
        shift:(event:KeyboardEvent)=>event.shiftKey,
        alt:(event:KeyboardEvent)=>event.altKey,
        meta:(event:KeyboardEvent)=>event.metaKey,
        // 他只支持快捷键的串联组合,并不支持任意键的组合
    }
    
    const noop=()=>{}
    // 返回空对象
    
    // 输入检测函数
    function getKeyFormater(keyFilter:any):keyPredicate{
        const type=isType(keyFilter)
        // 需要检测的按键类型
        if(type=='function'){
            return keyFilter
        }
    
        if(type==='string'||type==='number'){
            return (event:KeyboardEvent)=>genFilterKey(event,keyFilter)
        }
    
        if(type==='array'){
            return (event:KeyboardEvent)=>keyFilter.some((item:any)=>genFilterKey(event,item))
        }
        return keyFilter?()=>true:()=>false
    }
    
    // 检测当前按的键是否和定义的一致
    function genFilterKey(event:KeyboardEvent,keyFilter:any):boolean{
        let genLen=0
        console.log("当前需要检测的键位是------------------>",keyFilter)
        const type=isType(keyFilter)
    
        if(type==='number'){
            return event.keyCode===keyFilter
            // 如果需要判断的是数字码,那就直接和按键的码对比
        }
    
        const genArr=keyFilter.split('.')
        // 如果是字符串的话,要判断是否有组合键
    
        for(let key in genArr){
            const genModifier=modifierKey[genArr[key]]
            // 组合键
            const kM=keyMap[genArr[key]]
            // key别名
            const kCM=keyCodeMap[genArr[key]]
    
            // console.log(event.key.toUpperCase(),"输入后面有快捷键",genModifier)
            // console.log(genArr[key].toUpperCase(),"对比")
            // console.log(event.key.toUpperCase()==genArr[key].toUpperCase(),"是否相等")
             if(genModifier&&genModifier(event)||
                // 判断是否有快捷键
                (kCM&&isType(kCM)==='array'?kCM.includes(event.keyCode):kCM==event.keyCode)||
                // 判断自定义别名
                (kM&&isType(kM)==='array'?kM.includes(event.key):kM==event.key)||
                event.key.toUpperCase()==genArr[key].toUpperCase()
                // 判断是否有普通按键
             ){ 
                 genLen++
             }
        }
        return genLen==genArr.length
    }
    
    export default function useKeyPress(
        keyFilter:KeyFilter,
        eventHandler:EventHandler=noop,
        option:EventOption={}
    ){
        console.log('useKeypress')
        const {events=defaultEvents,target}=option
        const callbackRef=useRef(eventHandler)
        callbackRef.current=eventHandler
    
        const callbackHandler=useCallback(
            (event)=>{
                const genGuard:keyPredicate=getKeyFormater(keyFilter)
                console.log(genGuard)
                if(genGuard(event)){
                    console.log(true)
                    return callbackRef.current(event)
                }else{
                    console.log("这个没有检测到哦")
                }
            },
            [keyFilter]
        )
    
        useEffect(()=>{
            console.log(target)
            const el=getTargetElement(target,window)!;
            console.log(el)
            // 这里加感叹号是什么意思?
            // 非空断言运算符:ts编译器无法自动推断时断言表达式不为null或者undefined
    
            for(let key in events){
                el.addEventListener(events[key],callbackHandler)
            }
            return ()=>{
                for(let key in events){
                    el.removeEventListener(events[key],callbackHandler)
                }
            }
        },[events,callbackHandler,typeof target==='function'?undefined:target])
    }
    
      const [inputValue,setInputValue]=useState('default')
        // 这里输入的类型要怎么限制呢
    
        const [count,setCount]=useState(0)
    
        // useTitle(inputValue)
        // 设置标题:现在每次重新刷新都会调用useTitle,好像连初始的时候都会调用两次,为什么一次渲染会调到两次useTitle
       
    
        function handleChange(e: { target: { value: React.SetStateAction<string>; }; }):void{
            setInputValue(e.target.value)
        }
        // 主动修改
    
        function handleClick():void{
            setCount(c=>c+1)
        }
    
    
        // useKeyPress
        // useKeyPress("ArrowUp",()=>{
        //     console.log('ArrowUp')
        // })
    
        // useKeyPress([40,38],()=>{
        //     console.log('keyCode:40 or 38')
        // })
    
        // 使用别名
        // useKeyPress("left",()=>{
        //     console.log('left')
        // })
    
        // useKeyPress("up",()=>{
        //     console.log('left')
        // })
    
        // useKeyPress("delete",()=>{
        //     console.log('left')
        // })
    
        // 组合键
        // 组合键只支持修饰键+键位别名/键盘事件中的key进行组合
        // useKeyPress(['shift.c'], () => {
            
        // });
    
        // useKeyPress(['ctrl.alt.shift'], () => {
            
        // });
    
        //  useKeyPress(['ctrl.up'], () => {
            
        // });
    
        // 传入一个返回布尔值的回调函数,处理一些预处理的操作
        // const [key, setKey] = useState<string>();
        // const filterKey = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
        // useKeyPress(
        //     (event) => !filterKey.includes(event.key),
        //     (event) => {
        //     if (event.type === 'keyup') {
        //         setKey(event.key);
        //     }
        //     },
        //     {
        //     events: ['keydown', 'keyup'],
        //     },
        // );
    
        // 支持传入dom
        // useKeyPress(
        //     'enter',
        //     // 按下enter触发检测
        //     (event: any) => {
        //       const { value } = event.target;
        //       console.log('ok',value)
        //     },
        //     // 符合检测条件触发的函数
        //     {
        //         target: () => document.getElementById('input'),
        //     },
        //     // 需要绑定的目标
        //   );
    
        // const inputRef = useRef()as React.MutableRefObject<HTMLInputElement>
    //注意定义ref的typeScrit形式
        // useKeyPress(
        //     ()=>true,
        //     // 不论按什么键都触发
        //     (event: any) => {
        //               const { value } = event.target;
        //               console.log('ok',value)
        //             },
        //     {
        //         target:inputRef,
        //     }
        // )
    

    还是按照原来的方法,支持任意键的操作

    // 监听组合键
    
    import { useEffect,useRef,useState } from "react"
    function isType(obj){
        return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/,'$1').toLowerCase()
    }
    
    export default function useKeyBindings(keyCodes,isOrder){
        // keyCodes:数组,需要检测按下的键值的数组
        // isOrder:是否完全顺序一致,比如顺序必须匹配,不能乱序,和给的数组必须完全匹配顺序
        console.log('useKeyBindings')
        const [match,setMatch]=useState(false)
        const keyCodeRef=useRef([])
        // keyCodeRef.current=[]
        // 这样初始化会导致值重置的问题
    
        console.log(keyCodeRef.current,'每次都会重置??')
    
        useEffect(()=>{
            document.addEventListener('keydown',handleKeyDown)
            document.addEventListener('keyup',handleKeyUp)
    
            function handleKeyDown(e){
                const type=isType(keyCodes)
                console.log(type)
                if(type==='number'||type==='string'){
                    // 输入数字的时候
                    handleKeyDownClick(e,keyCodes)
                }
    
                if(type==='array'){
                    keyCodes.some((item)=>handleKeyDownClick(e,item))
                }
            }
            function handleKeyUp(e){
               console.log(e)
               setMatch(false)
               //感觉打印还是有点问题啊。    
               keyCodeRef.current=[]
               //这里还是不置为空,先是把那个按键取出来
            //    if(keyCodeRef.current.length==1){
            //        keyCodeRef.current=[]
            //    }else if(e.keyCode){
    
            //    }
    
            }
            function handleKeyDownClick(e,keyCodes){
                const type=isType(keyCodes)
                console.log('handleClick')
    
                if(type==='number'){
                    return e.keyCode==keyCodes
                }
    
                const keyCodesArr=keyCodes.split('.')
    
                if(keyCodeRef.current.length<keyCodesArr.length){
                    if(!keyCodeRef.current.includes(e.key)){
                        keyCodeRef.current.push(e.key)              
                        // 满足条件做一次检测
                        if(keyCodeRef.current.length===keyCodesArr.length){
                            console.log("check")
                            if(isOrder){
                                // 全匹配
                                for(let i=0;i<keyCodesArr.length;i++){
                                    console.log(keyCodeRef.current)
                                    console.log(keyCodesArr)
                                    if(keyCodeRef.current[i]!==keyCodesArr[i]){
                                        console.log('yes',i)
                                        setMatch(false)
                                        // return [match]
                                        // break;
                                    }
                                    if(i==keyCodesArr.length-1){
                                        setMatch(true)
                                        // return [match]
                                    }
                                }
                            }else{
                                for(let i=0;i<keyCodesArr.length;i++){
                                    if(!keyCodesArr.includes(keyCodeRef.current[i])){
                                        setMatch(false)
                                        // return [match]
                                        // break;
                                    }
                                    if(i==keyCodesArr.length-1){
                                        setMatch(true)
                                        // return [match]
                                    }
                                }
                            }
                        }
                    }
                    
                }
            }
            return ()=>{
                document.removeEventListener('keydown',handleKeyDown)
                document.removeEventListener('keyup',handleKeyUp)
            }
        },[keyCodes,isOrder])
    
        return [match]
    }  
    
    //1.我这个函数修改一个值,但是反过来我这个函数依赖这个值进行再次渲染,这样会不会造成死循环
    //键值对应:由于使用的是event.key这个参数用来对比,所以
    // ctrl:Control
    
    

    相关文章

      网友评论

          本文标题:useKeyPress

          本文链接:https://www.haomeiwen.com/subject/arbmlktx.html