React之卡片拖拽移动

管理页面我们通常能看到一连串的卡片,以很明显的方式对用户进行提示,也可以是图表嵌在卡片的形式直观展示。那就会存在一个需求,用户可以自定义自己想要的卡片布局,其实也就是自定义宽高这样。

image.png

比如说上图,我想自定义成为下面这样,
image.png

这样一个需求我们如何去实现呢?
查阅了一些文章之后,我选择了React-Grid-Layout这样一个组件,基本放大缩小以及拖拽这些都能实现,参照官方文档就ok,我这里主要讲一下,使用之后会出现的一些问题。
因为是基于hzero的项目,所有的路由是像一个tab页一样并列在顶部,并且路由之间的跳转不会触发组件的unmount方法,这时候就遇到了一个问题。当我们在其他页面进行浏览器的放大和缩小后,再回到使用React-Grid-Layout的页面,就会发现,所有的卡片挤在一起,样式错乱了,只在使用React-Grid-Layout的页面缩放就不会有问题,这样试了几次之后,很奇怪,为什么出现这种情况呢,那就只能去看看组件源码了,看了会,我发现了问题。

// @flow
import React from "react";
import PropTypes from "prop-types";
import ReactDOM from "react-dom";
import type { ComponentType as ReactComponentType } from "react";

type WPProps = {
  className?: string,
  measureBeforeMount: boolean,
  style?: Object
};

type WPState = {
  width: number
};

/*
 * A simple HOC that provides facility for listening to container resizes.
 */
// typescript  类型检查
export default function WidthProvider<
  Props,
  ComposedProps: { ...Props, ...WPProps }
>(
  ComposedComponent: ReactComponentType
): ReactComponentType {
  return class WidthProvider extends React.Component {
    static defaultProps = {
      measureBeforeMount: false
    };

    static propTypes = {
      // If true, will not render children until mounted. Useful for getting the exact width before
      // rendering, to prevent any unsightly resizing.
      measureBeforeMount: PropTypes.bool
    };

    state = {
      width: 1280
    };

    mounted: boolean = false;

    componentDidMount() {
      this.mounted = true;

      window.addEventListener("resize", this.onWindowResize);
      // Call to properly set the breakpoint and resize the elements.
      // Note that if you're doing a full-width element, this can get a little wonky if a scrollbar
      // appears because of the grid. In that case, fire your own resize event, or set `overflow: scroll` on your body.
      this.onWindowResize();
    }

    componentWillUnmount() {
      this.mounted = false;
      window.removeEventListener("resize", this.onWindowResize);
    }

    onWindowResize = () => {
      if (!this.mounted) return;
      // eslint-disable-next-line
      const node = ReactDOM.findDOMNode(this); // Flow casts this to Text | Element
      if (node instanceof HTMLElement)
        this.setState({ width: node.offsetWidth });
    };

    render() {
      const { measureBeforeMount, ...rest } = this.props;
      if (measureBeforeMount && !this.mounted) {
        return (
          
); } return ; } }; }

可以看到这里监听了页面的resize,然后进行onWindowResize方法,我们在其他页面进行resize之后,也会触发这个方法,这也是我前面要强调一下路由切换不会unmount的原因,这里的resize没法得到正确的node.offsetWidth,所以样式错乱了,那怎么办呢?

首先我想着,在这个页面写一个时钟,不停的js 去触发resize事件,让它在切换页面的时候重新计算,这样肯定是ok的,但是这样肯定会有性能问题,因为不停的延时调用,这样我就得想个办法,不那么频繁的刷新了,怎么做呢?

路由切换,页面也切换了,那组件肯定会触发receiveProps钩子,那我在这里判断一下当前页面的路由,如果是当前路由,那就刷新一下(dispatchEvent('resize')),这样就做到只是进入这个页面就刷新,比时钟要好一些。但是这样还是很low,因为进入其他页面没有resize操作,只要返回这个页面就会刷新,接下来我就要优化一下,只有当其他页面resize了之后,回到当前页才dispatchEvent('resize')

// 当切换到其他页面并进行了页面缩放之后重新resize当前页面
resize = debounce(() => {
  window.dispatchEvent(new Event('resize'));
  this.setState({
    loading: false,
    ifResize: false,
  });
}, 1000);

// eslint-disable-next-line
UNSAFE_componentWillReceiveProps() {
  if (window.location.href.indexOf('hipsappsa/pandect') > -1 && this.state.ifResize) {
    this.setState({
      loading: true,
    });
    this.resize();
  }
}

componentDidMount() {
  const callback = this.onreSize;
  const resize = debounce(() => {
    callback();
  }, 1000);
  window.addEventListener('resize', resize);
}

onResize() {
  const callback = this.onreSize;
  debounce(() => {
    callback();
  }, 1000);
}

/**
 * 响应式瀑布
 */
@Bind
onreSize() {
  if (window.location.href.indexOf('hipsappsa/pandect') === -1) {
    this.setState({
      ifResize: true,
    });
  } else {
    this.setState({
      ifResize: false,
    });
  }
}

页面挂载的时候就进行resize监听,当发生resize事件之后进行一下路由判断,如果是其他页面,就改变state里的一个值,返回到当前页之后进行一下state的值判断,为true才刷新。

为了页面切换的时候更合理一些(不展示错乱的页面),加个loading延时一秒过度一下,还要记得unmount时去除掉resize监听。

这里的图表用的是bizcharts,它会监听页面resize的变化,但是当我们改变宽高的时候,发现没有自适应,一开始我的解决方法是把这个渲染的canvas设置为width:100%,这样能使它拉伸开,但是很丑,因为是相当于是放大,这样效果太不好了,所以也要解决这个问题。

React-Grid-Layout组件有一个propsonResize事件返回当前改变宽高这样的事件回调,我们在这个回调里手动触发一下页面resize事件,以实现bizcharts的自适应。

 // 当手动修改resize之后触发一次页面resize使得bizcharts重新渲染
  handResize = debounce(() => {
    window.dispatchEvent(new Event('resize'));
  }, 100);

文章链接:https://www.npmjs.com/package/react-grid-layout

你可能感兴趣的:(React之卡片拖拽移动)