触摸支持 React 应用程序上的箭头点击

Touch support for arrow clicks on React app

我正在开发一个 React 网络应用程序,其中有一个 div4x4 grid,其中有 16 个 blocks 呈现在其中。我有右箭头、左箭头、上箭头和下箭头的箭头单击事件。当仅在移动设备上访问时,我需要为这些箭头点击添加移动触摸支持。我从未实施过这些 touch/swipe 移动功能,因此看起来很混乱。任何帮助将不胜感激。

分量:

const Grid = () => {
  const [grid, setGrid] = useState(new Grid());

  const arrowLeftKey = 37;
  const arrowDownKey = 40;

  const handleKeyDown = (event) => {
    if (grid.hasWon()) {
      return;
    }

    if (event.keyCode >= arrowLeftKey && event.keyCode <= arrowDownKey) {
      let direction = event.keyCode - arrowLeftKey;
      let gridClone = Object.assign(
        Object.create(Object.getPrototypeOf(grid)),
        grid
      );
      let newGrid = gridClone.move(direction);
      setGrid(newGrid);
    }
  };

  useArrowKeyEvent('keydown',handleKeyDown); //hook

  const displayBlocks = grid.cells.map((row, rowIndex) => {
    return (
      <div key={rowIndex}>
        {row.map((col, colIndex) => {
          return <Block key={rowIndex + colIndex} />;
        })}
      </div>
    );
  });

  return (
    <div className="grid" id="gridId">            
      {displayBlocks}
    </div>   
);

我通过谷歌搜索得知我需要使用 Touch Events,例如 touchStarttouchMovetouchEnd。查看 touchevents documentation 我将以下代码添加到我的组件中。我将 MouseEvents 更改为“KeyBoardevent”。因为这是 arrow key click/keydown 事件。但这是行不通的。不知道我哪里做错了。

   const onTouch = (evt) => {
    evt.preventDefault();
    if (evt.touches.length > 1 || (evt.type === "touchend" && evt.touches.length > 0))
      return;
  
    var newEvt = document.createEvent("KeyboardEvent");
    var type = null;
    var touch = null;
  
    // eslint-disable-next-line default-case
    switch (evt.type) {
      case "touchstart":
        type = "keydown";
        touch = evt.changedTouches[0];
        break;
      case "touchmove":
        type = "keydown";
        touch = evt.changedTouches[0];
        break;
      case "touchend":
        type = "keydown";
        touch = evt.changedTouches[0];
        break;
    }
  
    newEvt.initEvent(type, true, true, evt.originalTarget.ownerDocument.defaultView, 0,
      touch.screenX, touch.screenY, touch.clientX, touch.clientY,
      evt.keyCode('37'), evt.keyCode('39'), evt.keyCode('38'), evt.keyCode('40'), 0, null);
    evt.originalTarget.dispatchEvent(newEvt);
  }
  
document.addEventListener("touchstart", onTouch, true);
document.addEventListener("touchmove", onTouch, true);
document.addEventListener("touchend", onTouch, true);

当我向右滑动并期望点击右箭头时,我收到以下错误:

TypeError: Cannot read property 'ownerDocument' of undefined

在下面的代码行中:

newEvt.initEvent(type, true, true, evt.originalTarget.ownerDocument.defaultView, 0,
      touch.screenX, touch.screenY, touch.clientX, touch.clientY,
      evt.keyCode('37'), evt.keyCode('39'), evt.keyCode('38'), evt.keyCode('40'), 0, null);

版本 2 编辑::在@sschwei1 建议

后使用react-swipeable

我在组件中添加了以下部分:

  const swipeHandlers = useSwipeable({
    onSwipedLeft: useArrowKeyEvent('keydown',handleKeyDown),<<<<<problem 
    onSwipedRight: eventData => console.log("swiped right"),
    onSwipedUp: eventData => console.log("swiped up"),
    onSwipedDown: eventData => console.log("swiped down")
  });

和 return 语句:

  <div className="grid" {...swipeHandlers}>
    {displayBlocks}   
  </div>

问题: 无法将钩子用作 callback 函数。

React-swipeable 是一个为您处理滑动的库,它使您能够为不同的滑动方向创建处理程序,例如 onSwipedLeftonSwipedUp 以及几乎所有其他情况想想 onTaponSwipingonSwiped 等等。

在这些处理程序中,您可以重复使用箭头键的逻辑。


我想到的第一个解决方案(不是最漂亮的解决方案,但易于使用和理解)为滑动创建包装函数并调用相应的 keyHandler 函数

以下是这些函数的示例:

const handleTouch = (key) => {
  handleKeyDown({keyCode:key});
}

并且在您的触摸处理程序中,您可以使用相应的键调用此函数

const swipeHandlers = useSwipeable({
  onSwipedLeft: () => handleTouch('37'),
  onSwipedUp: () => handleTouch('38'),
  onSwipedRight: () => handleTouch('39'),
  onSwipedDown: () => handleTouch('40')
});

由于您只在 handleKeyDown 函数中使用 keyCode,因此您只需将带有 keyCode 属性 的对象传递给函数,然后 'simulate' 按键