推荐的方法来调整抽屉大小?

IT技术 javascript reactjs resize material-ui
2021-04-04 02:45:45

我想通过可拖动手柄调整材质 ui 抽屉的宽度。我目前的方法是在整个应用程序上设置一个 mousevent 侦听器,它检查是否按下了手柄,并根据每次鼠标移动时的鼠标位置更新宽度。

然而,这需要在整个应用程序上有一个恒定的 mouseevent 侦听器,这对于简单的调整大小功能来说似乎有点矫枉过正。

是否有更好/推荐的调整大小的方法?

5个回答

您可以mousedown在其上使用指示器拖动器

这里例如

// styles
dragger: {
  width: '5px',
  cursor: 'ew-resize',
  padding: '4px 0 0',
  borderTop: '1px solid #ddd',
  position: 'absolute',
  top: 0,
  left: 0,
  bottom: 0,
  zIndex: '100',
  backgroundColor: '#f4f7f9'
}

...

state = {
  isResizing: false,
  lastDownX: 0,
  newWidth: {}
};

handleMousedown = e => {
  this.setState({ isResizing: true, lastDownX: e.clientX });
};

handleMousemove = e => {
  // we don't want to do anything if we aren't resizing.
  if (!this.state.isResizing) {
    return;
  }

  let offsetRight =
    document.body.offsetWidth - (e.clientX - document.body.offsetLeft);
  let minWidth = 50;
  let maxWidth = 600;
  if (offsetRight > minWidth && offsetRight < maxWidth) {
    this.setState({ newWidth: { width: offsetRight } });
  }
};

handleMouseup = e => {
  this.setState({ isResizing: false });
};

componentDidMount() {
  document.addEventListener('mousemove', e => this.handleMousemove(e));
  document.addEventListener('mouseup', e => this.handleMouseup(e));
}

...

<Drawer
  variant="permanent"
  open
  anchor={'right'}
  classes={{
    paper: classes.drawerPaper
  }}
  PaperProps={{ style: this.state.newWidth }}
>
  <div
    id="dragger"
    onMouseDown={event => {
      this.handleMousedown(event);
    }}
    className={classes.dragger}
  />
  {drawer}
</Drawer>

这个想法是,当单击Drawer拖动器时,它会随着鼠标移动调整宽度

播放演示

document.removeEventListener(...)如果您认为需要,也可以添加以删除侦听器componentWillUnmount
2021-05-28 02:45:45
当我单击以调整大小时,容器会跳跃并在我的光标拖动位置的左侧约 10 像素处。
2021-06-14 02:45:45
谢谢你这么棒的回复!在接受这个答案之前,您会怎么想只在单击拖动手柄时添加 mousemove 侦听器并在 mouseup 上取消注册?这将阻止 mousemove 事件侦听器在 99% 的时间处于开启状态。
2021-06-17 02:45:45
@nauti,你的意思是 Drawer如果是这样,只要Drawer有手柄mousemove道具就可以做到(我没有检查过)。如果没有,您可以将Drawer. 这取决于你如何实现这一点,我建议你也考虑一下UI/UX因为大多数用户都会使用它。当悬停/单击外部链接时,鼠标将更改为拖动器图标(只有链接有指针图标)。这取决于您的要求。
2021-06-19 02:45:45

我想添加一个使用 React Hooks 更新的答案。

你可以这样做,然后:

CSS:

sidebar-dragger: {
  width: '5px',
  cursor: 'ew-resize',
  padding: '4px 0 0',
  borderTop: '1px solid #ddd',
  position: 'absolute',
  top: 0,
  left: 0,
  bottom: 0,
  zIndex: '100',
  backgroundColor: '#f4f7f9'
}

React(使用带有引用和状态的钩子)

let isResizing = null;

function ResizeableSidebar (props) {
  const sidebarPanel = React.useRef('sidebarPanel');
  const cbHandleMouseMove = React.useCallback(handleMousemove, []);
  const cbHandleMouseUp = React.useCallback(handleMouseup, []);

  function handleMousedown (e) {
    e.stopPropagation();
    e.preventDefault();
    // we will only add listeners when needed, and remove them afterward
    document.addEventListener('mousemove', cbHandleMouseMove);
    document.addEventListener('mouseup', cbHandleMouseUp);
    isResizing = true;
  };

  function handleMousemove (e) {
    if (!isResizing) {
      return;
    }

    let offsetRight =
      document.body.offsetWidth - (e.clientX - document.body.offsetLeft);
    let minWidth = 50;
    if (offsetRight > minWidth) {
      let curSize = offsetRight - 60;
      // using a ref instead of state will be way faster
      sidebarPanel.current.style.width = curSize + 'px';
    }
  };

  function handleMouseup (e) {
    if (!isResizing) {
      return;
    }
    isResizing = false;
    document.removeEventListener('mousemove', cbHandleMouseMove);
    document.removeEventListener('mouseup', cbHandleMouseUp);
  };

  return <div className="sidebar-container">
    <div
      className="sidebar-dragger"
      onMouseDown={handleMousedown}
    />
    <div>
      Your stuff goes here
    </div>
  </div>;
}

只需在句柄元素上使用合成事件即可这样,您可以避免拥有通用事件侦听器的混乱/性能成本。类似于以下内容:

render() {
     return (
       <div onMouseDown={this.yourResizeFunc}>
       </div>
  );
}
@nauti 难道你不能只在“整个应用程序”上做,然后检查以确保鼠标在你想要调整大小的 div 内?要么使用 div 的 onMouseDown 和应用程序的 onMouseUp?
2021-05-29 02:45:45
@JohnMcGowan 在单击调整大小拖动按钮时添加鼠标悬停侦听器并在 mouseup 上取消注册怎么样?
2021-06-06 02:45:45
所以你的意思是鼠标按下的三个事件?问题是,如果我对要调整大小的元素执行此操作,则在移出应调整大小的 div 时,鼠标悬停会停止触发。出于这个原因,这三个通风口需要在父级上注册,不幸的是整个应用程序。
2021-06-13 02:45:45
@JeremyF 是的,这就是我试图在票证中给出的解决方案,但我正在寻找一个更好/更高性能的解决方案,它不需要用户在整个应用程序上始终使用鼠标移动侦听器,只是为了简单地调整大小分区
2021-06-14 02:45:45
此组件是否始终安装?如果是这样,我认为没有办法让事件侦听器不断为 mouseDown 事件注册。如果有卸载的情况,您可以componentDidMountcomponentWillUnmount. 但是,如果可调整大小的组件始终可见,则您将需要某种侦听器。此外,如果它实际上始终处于挂载状态,那么它并不会完全降低应用程序中其他页面的性能。当应用程序增长并且组件并不总是安装时,您始终可以进行优化
2021-06-14 02:45:45

如果这符合您的需要,您只能使用 css 来做到这一点。这是最简单的解决方案。看妈妈,没有javascript。

.resizable {
  height: 150px;
  width: 150px;
  border: 1px solid #333;
  resize: horizontal;
  overflow: auto;
}
<div class="resizable"></div>

MDN 上的参考

不幸的是,因为我的抽屉是从右边开始的,所以你需要一个方向 rtl,它会搞乱内容的布局 + 调整大小的手柄只能设置非常有限的样式。
2021-05-25 02:45:45
这与材质ui drawer无关
2021-05-26 02:45:45

它可能是一个useResize带有 API钩子,用于启用调整大小和提供当前宽度。

import { useCallback, useEffect, useState } from 'react'

type UseResizeProps = {
  minWidth: number
}

type UseResizeReturn = {
  width: number
  enableResize: () => void
}

const useResize = ({
  minWidth,
}: UseResizeProps): UseResizeReturn => {
  const [isResizing, setIsResizing] = useState(false)
  const [width, setWidth] = useState(minWidth)

  const enableResize = useCallback(() => {
    setIsResizing(true)
  }, [setIsResizing])

  const disableResize = useCallback(() => {
    setIsResizing(false)
  }, [setIsResizing])

  const resize = useCallback(
    (e: MouseEvent) => {
      if (isResizing) {
        const newWidth = e.clientX // You may want to add some offset here from props
        if (newWidth >= minWidth) {
          setWidth(newWidth)
        }
      }
    },
    [minWidth, isResizing, setWidth],
  )

  useEffect(() => {
    document.addEventListener('mousemove', resize)
    document.addEventListener('mouseup', disableResize)

    return () => {
      document.removeEventListener('mousemove', resize)
      document.removeEventListener('mouseup', disableResize)
    }
  }, [disableResize, resize])

  return { width, enableResize }
}

export default useResize

然后你可以像这样从你的布局组件中分离调整大小的逻辑:

const Layout = () => {
  const { width, enableResize } = useResize(200);

  return (
    <Drawer
      variant="permanent"
      open
      PaperProps={{ style: { width } }}
    >
      {drawer}
      <div
        style={{ 
          position: absolute,
          width: '2px',
          top: '0',
          right: '-1px',
          bottom: '0',
          cursor: 'col-resize'
        }}
        onMouseDown={enableResize}
      />
    </Drawer>
)
很好的解决方案:)
2021-06-11 02:45:45