[Front-end novice can also do big projects]: Build your own online Visio project from scratch with me [ReactJS + UmiJS + DvaJS] (1)

This series of tutorials is to teach you how to create your own online drawing software based on the open source js drawing library . Of course, you can also look at the development tutorial of this drawing library. If you think it is good, please give a like, let us be more motivated to do well!

This series of tutorials focuses on how to develop your own drawing software, so react basics and frameworks are not covered here. You can recommend the React official website to learn, or " React Family Bucket Free Video ".

The source code address of this series of tutorials: Github

First, build the react framework environment

Here, we choose Ali's UmiJS + DvaJS + Ant.Desgin light application framework.

1. Install UmiJS

// 推荐使用yarn
npm install yarn -g

yarn global add umi

2. Install UmiJS scaffolding

mkdir topology-react
 yarn create umi
 
 // 创建项目文件后,安装依赖包
 yarn

Here, we choose typescript, dva, etc. (dll can not be used, it is outdated).

3. Change css to less

A. typings.d.ts join less

declare module '*.less';

B. Change global.css to global.less and introduce antd theme

@import '~antd/es/style/themes/default.less';

html,
body,
#root {
  height: 100%;
}

.colorWeak {
  filter: invert(80%);
}

.ant-layout {
  min-height: 100vh;
}

canvas {
  display: block;
}

body {
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

ul,
ol {
  list-style: none;
}

@media (max-width: @screen-xs) {
  .ant-table {
    width: 100%;
    overflow-x: auto;
    &-thead > tr,
    &-tbody > tr {
      > th,
      > td {
        white-space: pre;
        > span {
          display: block;
        }
      }
    }
  }
}

**C. Change other css to less ** Change the css under layouts and pages to less, and modify the references in tsx.

4. Modify the default single page template file

According to the convention of UmiJS, we can add a template file named document.ejs under src/pages instead of the default template. For the content of the new template, refer to the source code.

Specific reference: HTML template documentation for UmiJS

5. Run

npm start you can see the default UmiJS interface. Code: tag: umi

Among them, index.tsx under layouts is the global template file; pages is the routing module.

2. Modify the page layout to up and down navigation, left, middle and right layout

Copy static resource files

Add additional image file extensions in typings.d.ts

declare module '*.ico';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';

Create a new public folder and put static resources into it. Assets are not used here, mainly because some independent static resources are placed in public.

New top navigation menu bar

We create a new Headers class in layouts as a navigation menu and add it to BasicLayout. code show as below:

import React from 'react';
import styles from './index.less';
import Headers from './headers';

const BasicLayout: React.FC = props => {
  return (
    <div className={styles.page}>
      <Headers />
      <div className={styles.body}>{props.children}</div>
    </div>
  );
};

export default BasicLayout;

The navigation bar menu calls onMenuClick and the dialog box for details.

Workspace left, middle and right three-column layout

We modify index.tsx under pages to make it three columns left, middle and right, as follows:

import React from 'react';
import styles from './index.less';
import { Tools } from '@/utils/tools';


class Index extends React.Component<{}> {
  state = {
    tools: Tools,
    iconfont: { fontSize: '.24rem' }
  };


  render() {
    return (
      <div className={styles.page}>
        <div className={styles.tools}/>
        <div id="workspace" className={styles.full} />
        <div className={styles.props}>{}</div>
      </div>
    );
  }
}

export default Index;

le5le-topology - 3-column layout for left, middle and right

Implement the left toolbar

1. Import Ali font icon iconfont Import the iconfont we need in src/pages/document.ejs

  <link href="//at.alicdn.com/t/font_1113798_0532l8oa6jqp.css" rel="stylesheet" />
  <link href="//at.alicdn.com/t/font_1331132_5lvbai88wkb.css" rel="stylesheet" />

Among them, the upper part is the icon that needs to be used in the left toolbar; the lower part is the right attribute bar as the node icon library that can be selected by the user. You can replace it with your own address (just pay attention to modifying the data in Tools).

2. Customize the list of icons on the left toolbar We create a new utils directory under src, which is separate from the UmiJS convention rules directory , as our custom function module. Add a tools.tsx file and define the array of our left toolbar icon list here.

Among them, tools.tsx functions as follows:file

Then, import it in src/pages/index.tsx, and cycle through and display the left toolbar icons: (Here, there is no separate definition of a left toolbar class, you can just follow your own habits, there is no mandatory regulation, nor need extreme)

import React from 'react';
import styles from './index.less';
import { Tools } from '@/utils/tools';


class Index extends React.Component<{}> {
  state = {
    tools: Tools,
    iconfont: { fontSize: '.24rem' }
  };


  render() {
    return (
      <div className={styles.page}>
        <div className={styles.tools}>
          {
            this.state.tools.map((item, index) => {
              return (
                <div key={index}>
                  <div className={styles.title}>{item.group}</div>
                  <div className={styles.buttons}>
                    {
                      item.children.map((btn: any, i: number) => {
                        return (
                          <a key={i} title={btn.name}>
                            <i className={'iconfont ' + btn.icon} style={this.state.iconfont} />
                          </a>
                        )
                      })
                    }
                  </div>
                </div>
              )
            })
          }
        </div>
        <div id="workspace" className={styles.full} />
        <div className={styles.props}>{}</div>
      </div>
    );
  }
}

export default Index;

Import Canvas (emphasis, emphasis, emphasis)

Here are the key functions, which need to be used according to the official development documentation .

1. Install the canvas core library We add the following to the package.json folder:

"topology-activity-diagram": "^0.0.4",
"topology-class-diagram": "^0.0.1",
"topology-core": "^0.0.10",
"topology-flow-diagram": "^0.0.1",
"topology-sequence-diagram": "^0.0.4"

Among them, topology-core is the core library, and the other four are extended graphics libraries; we can implement our own graphics library according to the API development document, and can choose to share it for everyone to use together. This is topology scalability.

Then, execute yarn to download and install the dependency library.

2. Register the extended graphics library The core library only contains the simplest and most basic graphics. Other rich graphics libraries need to install dependency packages and register them in topology-core. Here we define a registration function of canvasRegister, as follows:

// 先导入库
import { Topology } from 'topology-core';
import { Options } from 'topology-core/options';
import { registerNode } from 'topology-core/middles';
import {
  flowData,
  flowDataAnchors,
  flowDataIconRect,
  flowDataTextRect,
  flowSubprocess,
  flowSubprocessIconRect,
  flowSubprocessTextRect,
  flowDb,
  flowDbIconRect,
  flowDbTextRect,
  flowDocument,
  flowDocumentAnchors,
  flowDocumentIconRect,
  flowDocumentTextRect,
  flowInternalStorage,
  flowInternalStorageIconRect,
  flowInternalStorageTextRect,
  flowExternStorage,
  flowExternStorageAnchors,
  flowExternStorageIconRect,
  flowExternStorageTextRect,
  flowQueue,
  flowQueueIconRect,
  flowQueueTextRect,
  flowManually,
  flowManuallyAnchors,
  flowManuallyIconRect,
  flowManuallyTextRect,
  flowDisplay,
  flowDisplayAnchors,
  flowDisplayIconRect,
  flowDisplayTextRect,
  flowParallel,
  flowParallelAnchors,
  flowComment,
  flowCommentAnchors
} from 'topology-flow-diagram';

import {
  activityFinal,
  activityFinalIconRect,
  activityFinalTextRect,
  swimlaneV,
  swimlaneVIconRect,
  swimlaneVTextRect,
  swimlaneH,
  swimlaneHIconRect,
  swimlaneHTextRect,
  fork,
  forkHAnchors,
  forkIconRect,
  forkTextRect,
  forkVAnchors
} from 'topology-activity-diagram';
import {
  simpleClass,
  simpleClassIconRect,
  simpleClassTextRect,
  interfaceClass,
  interfaceClassIconRect,
  interfaceClassTextRect
} from 'topology-class-diagram';
import {
  lifeline,
  lifelineAnchors,
  lifelineIconRect,
  lifelineTextRect,
  sequenceFocus,
  sequenceFocusAnchors,
  sequenceFocusIconRect,
  sequenceFocusTextRect
} from 'topology-sequence-diagram';

// 使用
canvasRegister() {
    registerNode('flowData', flowData, flowDataAnchors, flowDataIconRect, flowDataTextRect);
    registerNode('flowSubprocess', flowSubprocess, null, flowSubprocessIconRect, flowSubprocessTextRect);
    registerNode('flowDb', flowDb, null, flowDbIconRect, flowDbTextRect);
    registerNode('flowDocument', flowDocument, flowDocumentAnchors, flowDocumentIconRect, flowDocumentTextRect);
    registerNode(
      'flowInternalStorage',
      flowInternalStorage,
      null,
      flowInternalStorageIconRect,
      flowInternalStorageTextRect
    );
    registerNode(
      'flowExternStorage',
      flowExternStorage,
      flowExternStorageAnchors,
      flowExternStorageIconRect,
      flowExternStorageTextRect
    );
    registerNode('flowQueue', flowQueue, null, flowQueueIconRect, flowQueueTextRect);
    registerNode('flowManually', flowManually, flowManuallyAnchors, flowManuallyIconRect, flowManuallyTextRect);
    registerNode('flowDisplay', flowDisplay, flowDisplayAnchors, flowDisplayIconRect, flowDisplayTextRect);
    registerNode('flowParallel', flowParallel, flowParallelAnchors, null, null);
    registerNode('flowComment', flowComment, flowCommentAnchors, null, null);

    // activity
    registerNode('activityFinal', activityFinal, null, activityFinalIconRect, activityFinalTextRect);
    registerNode('swimlaneV', swimlaneV, null, swimlaneVIconRect, swimlaneVTextRect);
    registerNode('swimlaneH', swimlaneH, null, swimlaneHIconRect, swimlaneHTextRect);
    registerNode('forkH', fork, forkHAnchors, forkIconRect, forkTextRect);
    registerNode('forkV', fork, forkVAnchors, forkIconRect, forkTextRect);

    // class
    registerNode('simpleClass', simpleClass, null, simpleClassIconRect, simpleClassTextRect);
    registerNode('interfaceClass', interfaceClass, null, interfaceClassIconRect, interfaceClassTextRect);

    // sequence
    registerNode('lifeline', lifeline, lifelineAnchors, lifelineIconRect, lifelineTextRect);
    registerNode('sequenceFocus', sequenceFocus, sequenceFocusAnchors, sequenceFocusIconRect, sequenceFocusTextRect);
  }

3. Declare and define the canvas object We define two member variables for the Index class under src/pages/index.tsx: canvas and canvasOptions

class Index extends React.Component<{}> {
  canvas: Topology;
  canvasOptions: Options = {};

  state = {
    tools: Tools,
    iconfont: { fontSize: '.24rem' }
  };
	...
}

Note that this is not defined in state, because state is used for data display and interaction on the internal UI, and our canvas is data that belongs to an internal non-ui interaction.

Then, we instantiate the canvas in componentDidMount after the dom is loaded (make sure the canvas's parent element exists):

componentDidMount() {
    this.canvasRegister();
    this.canvasOptions.on = this.onMessage;
    this.canvas = new Topology('topology-canvas', this.canvasOptions);
}

Among them, canvasOptions.on is the message callback function of the canvas, so far, it is temporarily unavailable.

4. Add drag event on the left toolbar to enable drag and drop graphics

4.1 Add drag attribute and event to icon button

<a key={i} title={btn.name} draggable={true} onDragStart={(ev) => { this.onDrag(ev, btn) }}>
  <i className={'iconfont ' + btn.icon} style={this.state.iconfont} />
</a>

4.2 Define the onDrag function

onDrag(event: React.DragEvent<HTMLAnchorElement>, node: any) {
  event.dataTransfer.setData('Text', JSON.stringify(node.data));
}

At this point, the basic operation of the canvas is completed.

Define the right property bar

1. Create a simple property bar class Again, we create a src/pages/components folder to put our components; then create a canvasProps.tsx file.

Define the props property interface:

export interface CanvasPropsProps {
  form: FormComponentProps['form'];
  data: {
    node?: Node,
    line?: Line,
    multi?: boolean
  };
  onValuesChange: (props: any, changedValues: any, allValues: any) => void;
}

Among them, if node is not empty, it means node node attribute; if line is not empty, it means line connection attribute; multi means multiple selection.

The other content is the form input of react, see the source code for details. (Here, we are using the form of ant.design )

2. Define the change event We still define the change event of the form by means of ant.design:

src/pages/components/canvasProps.tsx

export default Form.create<CanvasPropsProps>({
  onValuesChange({ onValuesChange, ...restProps }, changedValues, allValues) {
    if (onValuesChange) {
      onValuesChange(restProps, changedValues, allValues);
    }
  }
})(CanvasProps);

src/pages/index.tsx

<div className={styles.props}>
  <CanvasProps data={this.state.selected} onValuesChange={this.handlePropsChange} />
</div>
handlePropsChange = (props: any, changedValues: any, allValues: any) => {
    if (changedValues.node) {
      // 遍历查找修改的属性,赋值给原始Node

      // this.state.selected.node = Object.assign(this.state.selected.node, changedValues.node);
      for (const key in changedValues.node) {
        if (Array.isArray(changedValues.node[key])) {
        } else if (typeof changedValues.node[key] === 'object') {
          for (const k in changedValues.node[key]) {
            this.state.selected.node[key][k] = changedValues.node[key][k];
          }
        } else {
          this.state.selected.node[key] = changedValues.node[key];
        }
      }
      // 通知属性更新,刷新
      this.canvas.updateProps(this.state.selected.node);
    }
  }

A simple property modification example is complete. For more attributes, you are welcome to add and submit GitHub's pr : 0. Read the development documentation to understand the relevant attributes.

  1. fork the repository to your own name
  2. Modify locally and submit to your own git repository
  3. Find the "Pull request" button in your fork repository and submitfile

other

Top toolbar and right-click menu functions to be continued.

Open source projects are not easy, everyone is welcome to participate, or fund the server:

file

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324208589&siteId=291194637