React 最佳实践:可拖拽侧边栏

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战css

前言

页面布局也是在实际开发中常常用到的技术。react

  • 在大的方面,能够实现整个页面的布局,好比左侧导航、header、footer...
  • 在小的方面,能够是内容布局,好比文章。
  • 考虑布局时,须要兼顾桌面浏览器和移动端的状况,也就是常说的 responsive。

在 react 中实现,和经典实现其实没什么大的区别。浏览器

实现布局有这几种方式markdown

  • 从 0 开始使用 CSS 实现,这是必须掌握的技能
  • 使用 CSS Grid 系统(网状页面),能够使用不一样尺寸的屏幕
  • 使用组件库,例如 antd
    • Grid:24 栅格系统
    • Layout:页面级总体布局

CSS 实现基础布局

上中下布局

layout-1.png

.app-layout1 {
  width: 500px;
  height: 400px;
  position: relative;
  text-align: center;
}

.app-layout1 .header {
  line-height: 60px;
}
.app-layout1 .content {
  position: absolute;
  bottom: 60px;
  top: 60px;
  left: 0;
  right: 0;
}
.app-layout1 .footer {
  line-height: 60px;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
}
复制代码

Sider + 上中下布局

layout-2.png

使用 left: 150px,留出 Sider 的位置antd

.app-layout2 {
  width: 500px;
  height: 400px;
  position: relative;
  text-align: center;
}
.app-layout2 .header {
  position: absolute;
  left: 150px;
  top: 0;
  right: 0;
}
.app-layout2 .content {
  position: absolute;
  bottom: 60px;
  top: 60px;
  left: 150px;
  right: 0;
}
.app-layout2 .footer {
  bottom: 0;
  left: 150px;
  right: 0;
  position: absolute;
}
.app-layout2 .sider {
  width: 150px;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
}
复制代码

进阶:侧边栏宽度可拖拽

样式布局

style.css

.layout {
  position: relative;
  width: 100%;
  height: 400px;
}
.sider {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  background-color: #ddd;
}
.header {
  background-color: #aaa;
  height: 60px;
}
复制代码

index.js

经过状态 siderWidth 控制 layoutpaddingLeftapp

import { useState } from 'react';
import './style.css';

export default function ResizeLayout() {
  const [siderWidth, setSiderWidth] = useState(150);
  const pxWidth = `${siderWidth}px`;

  return (
    <div className="layout" style={{ paddingLeft: pxWidth }}> <div className="sider" style={{ width: pxWidth }}> sider </div> <div className="header">header</div> <div className="content">content</div> </div>
  );
}
复制代码

拖放逻辑

视觉上,咱们拖拽的是侧边栏 Sider 的右边框,其实并非。咱们会在右边框位置放置一个“隐身”的 bar -- sider-resizeride

.sider-resizer {
  position: absolute;
  width: 6px;
  top: 0;
  bottom: 0;
  cursor: col-resize;
}
复制代码

当鼠标在 sider-resizer 上,触发 onMouseDown 时,记录下鼠标的初始位置,同时标记 dragging 状态为 true布局

const handleMouseDown = (event) => {
  setStartPageX(event.pageX);
  setDragging(true);
};
复制代码

伴随着 dragging 状态的改变,咱们会为页面铺上一层遮罩 -- resize-mask,以便后续 事件的监听:post

{
  dragging && (
    <div className="resize-mask" onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} />
  );
}
复制代码
.resize-mask {
  background: rgba(0, 0, 0, 0);
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  cursor: col-resize;
}
复制代码

onMouseMove 的过程当中,实时更新 siderWidth 以及对 startPageX 的更新,就会产生拖拽的效果。ui

最终在 onMouseUp 时,结束拖拽状态。

const handleMouseMove = (event) => {
  const currentSiderWidth = siderWidth + event.pageX - startPageX;
  setSiderWidth(currentSiderWidth);
  setStartPageX(event.pageX);
};
const handleMouseUp = () => {
  setDragging(false);
};
复制代码

localStorage 存储宽度位置

在拖拽结束时,保存 siderWidthlocalStorage;初始化 siderWidth 时,检查 localStorage 是否有值。

const [siderWidth, setSiderWidth] = useState(
  parseInt(localStorage.getItem('siderWidth')) || 150,
);
const handleMouseUp = () => {
  setDragging(false);
  localStorage.setItem('siderWidth', siderWidth);
};
复制代码

完整代码

style.css

.layout {
  position: relative;
  width: 100%;
  height: 400px;
}
.sider {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  background-color: #ddd;
}
.header {
  background-color: #aaa;
  height: 60px;
}
.sider-resizer {
  position: absolute;
  width: 6px;
  top: 0;
  bottom: 0;
  cursor: col-resize;
}
.resize-mask {
  background: rgba(0, 0, 0, 0);
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  cursor: col-resize;
}
复制代码

index.js

import { useState } from 'react';
import './style.css';

export default function ResizeLayout() {
  const [siderWidth, setSiderWidth] = useState(
    parseInt(localStorage.getItem('siderWidth')) || 150,
  );
  const [dragging, setDragging] = useState(false);
  const [startPageX, setStartPageX] = useState(0);
  const pxWidth = `${siderWidth}px`;
  const handleMouseDown = (event) => {
    setStartPageX(event.pageX);
    setDragging(true);
  };
  const handleMouseMove = (event) => {
    const currentSiderWidth = siderWidth + event.pageX - startPageX;
    setSiderWidth(currentSiderWidth);
    setStartPageX(event.pageX);
  };
  const handleMouseUp = () => {
    setDragging(false);
    localStorage.setItem('siderWidth', siderWidth);
  };
  return (
    <div className="layout" style={{ paddingLeft: pxWidth }}> <div className="sider" style={{ width: pxWidth }}> sider </div> <div className="header">header</div> <div className="content">content</div> <div className="sider-resizer" style={{ left: pxWidth }} onMouseDown={handleMouseDown} > {dragging && ( <div className="resize-mask" onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} /> )} </div> </div>
  );
}
复制代码

React 最佳实践

相关文章
相关标签/搜索