React Svg 大量点 、线、移动、添加等处理一点思考

公司进行目标移动标点开发,适合 React 的框架较少。以 G6 为基础的 Canvas 处理、jsplumb、GooFlow 等框架都存在问题,如宽高限制、数据量大卡顿、闭源收费等。同事找到 VivaGraphJS 待验证,因新 dom 生成和局部刷新处理不精细,需找靠谱框架,作者还做了简单验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近公司需要对目标移动标点,适合react进行开发的很少,找了多种都会有各式各样的问题。

  • G6为基础的Canvas处理,对宽高进行了现在,当宽高到达一定的限度将直接罢工,同样数据量上去了,也是死的不要不要的。

  • jsplumb也是个不错的选择,然而当数据达到一定量后,画圆点是执行的局部刷新,然而线的处理确实全局刷新,这个就有点伤了,几千根线就卡死的不要不要的。

  • GooFlow就是那个放挖矿的那个,其实软件很好,可惜闭源了。现在有react版本,可以需要花钱买,不再是那个免费吃的了。

  • VivaGraphJS 同事去寻找其他可以实现的框架,有幸找到了个可以完成我们需求的,目前还在验证中。文档较少,努力查找中。

    那些出现各式各样的问题,主要的还是新dom的生成,以及dom的局部刷新处理的不够精细导致,数据量小的随便搞搞都没事,量大了,真的还是要找个靠谱的框架才行。

为了保证项目的进度,没办法在那边框架那边没有确认可以使用前,我也做了简单的验证。
下面直接上代码,写的简单,只是验证了局部刷新的问题

入口

import React, {Component} from 'react'
import Svg from './Svg';

class App extends Component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div style={{margin: 100}}>
                <button onClick={() => this.addCircle()}>添加圆点</button>
                <button onClick={() => this.addRect()}>添加矩形</button>
                <button onClick={() => this.addTestLine()}>直线测试</button>
                <br/>
                <Svg
                    init={{
                        width: 10000,    // 画布宽
                        height: 10000,    // 画布高
                    }}
                    style={{
                        border: "1px solid #c3c3c3",
                    }}
                    onRef={this.onRef}
                />
            </div>
        )
    }
    addCircle = () => {
        console.log("addCircle------->", this.clild);
        this.clild.addCircle();
    };

    addRect = () => {
        console.log("addRect------->");
        this.clild.addRect();
    };

    addTestLine = () => {
        console.log("addTestLine------->");
        this.clild.addTestLine();
    };
    onRef = (ref) => {
        this.clild = ref;
    }
}
export default App;

SVG 组件 主控制

import React, {Component} from 'react'
import "./App.css";
import Circle from './component/svg/Circle/Circle';
import Line from './component/svg/line/Line';

class Svg extends Component {
    constructor(props) {
        super(props);
        this.state = {
            // 圆
            circles: {},
            // 线
            lines: [],
        };
        // 保存线 绑定操作
        this.children = {};
        // 解决line 与 circle 映射管理,避免遍历操作
        this.lineMap = {}
    }
    render() {
        //  中途添加新的 可以 通过新的列表处理,避免大量原有的的数据遍历 这里就不进行处理
        //  将列表 分为多段 刷新验证
        let init = this.state.init;
        // console.log(this.state);
        let {width, height, id} = init;
        let circles = this.state.circles;
        let lines = this.state.lines;
        // console.log(circles);
        return (
            <svg width={width} height={height} version="1.1" id={id} style={this.state.style}
                 xmlns="http://www.w3.org/2000/svg"
                 ref={id}
            >
                {Object.keys(circles).map((name, key) => {
                    return (
                        <Circle
                            onClick={this.edit}
                            move={this.move}
                            key={name}
                            info={circles[name]}/>
                    );
                })}

                {
                    lines.map((line, _) => {
                        return <Line bindRef={this.bindRef} key={line.attributes.id} info={line}/>
                    })
                }
            </svg>
        )
    }


    componentWillMount() {
        this.props.onRef(this);
        this.setState({

            init: {...this.props.init},
            style: this.props.style,

        });
    }


    componentDidMount() {
    	// 随机生成 10000 个圆点
        for (let i = 0; i < 10000; i++) {
            this.addCircle2(i);
        }
    }

	// 测试生成的线
    addTestLine = () => {
        let circles = this.state.circles;

        let lines = [];
        console.log(circles.length);

        let circleKeys = Object.keys(circles);

        for (let i = 0; i < circleKeys.length - 1; i = i + 2) {

            let key1 = circleKeys[i];
            let key2 = circleKeys[i + 1];

            let line = {
                attributes: {
                    id: key1 + "-" + key2,
                    x1: circles[key1].cx,
                    x2: circles[key2].cx,
                    y1: circles[key1].cy,
                    y2: circles[key2].cy,
                },
                others: {
                    sourceId: key1,
                    targetId: key2,
                }
            };
            this.addLinesMaps(line);
            lines.push(line);
        }
        this.setState({
            lines
        })
    };

    addCircle2 = (num) => {
        let circles = this.state.circles;
        let id = new Date().getTime() + "" + num;
        circles[id] = {
            id: id,
            name: id,
            cx: randomNum(50, 9000),
            cy: randomNum(50, 9000),
            r: 20,
            stroke: "black",
            strokeWidth: 2,
            fill: "red",
            checked: new Date().getTime()
        };
        this.setState({
            circles
        });

    };


    addCircle = () => {
        console.log(this.children);
        let circles = this.state.circles;

        let id = new Date().getTime() + "";
        circles[id] = {

            id: id,
            name: id,
            cx: randomNum(50, 900),
            cy: randomNum(50, 900),
            r: 40,
            stroke: "black",
            strokeWidth: 2,
            fill: "red",
            checked: new Date().getTime(),

        };
        this.setState({
            circles
        });

    };
    // 绑定线操作
    bindRef = (ref, id) => {
        this.children[id] = ref;
    };
    addRect = () => {
        console.log("===");
    };
    edit = (info) => {
    };
	// 圆移动线的联动
    move = (info) => {
        let circleId = info.id;


        let lineInfo = this.lineMap[circleId];
        if (lineInfo === undefined) {
            return;
        }

        Object.keys(lineInfo).forEach(key => {
            let line = lineInfo[key];
            console.log(circleId === lineInfo[key].others.targetId);
            console.log(circleId === lineInfo[key].others.sourceId);
            let XY;
            if (circleId === lineInfo[key].others.targetId) {
                XY = {
                    x2: info.cx,
                    y2: info.cy
                }
            } else {
                XY = {
                    x1: info.cx,
                    y1: info.cy
                }
            }
            this.children[line.attributes.id].changeLine(XY);
        });
    };

    addLinesMaps = (lineInfo) => {

        let sourceInfos = this.lineMap[lineInfo.others.sourceId] || {};
        let targetInfos = this.lineMap[lineInfo.others.targetId] || {};
        
        let id = lineInfo.attributes.id;
        sourceInfos[id] = lineInfo;
        targetInfos[id] = lineInfo;

        this.lineMap = {
            ...this.lineMap,
            [lineInfo.others.sourceId]: sourceInfos,
            [lineInfo.others.targetId]: targetInfos,
        }
    }
}

function randomNum(Min, Max) {
    var Range = Max - Min;
    var Rand = Math.random();
    return (Min + Math.round(Rand * Range));
}

export default Svg;

import React, {Component} from 'react'

class Circle extends Component {
    constructor(props) {
        super(props);
        this.state = {
            checked: 0,
            info: this.props.info,
        }

    }
    
    render() {
        return (
            <circle
                ref="circleDom"
                {...this.state.info}
            />
        )
    }

	// 验证是否需要刷新
    shouldComponentUpdate(nextProps, nextState) {
        // console.log(this.state.checked === nextState.checked, this.state.checked, nextState.checked);
        return !(this.props.info.checked === nextProps.info.checked) || !(nextState.checked === this.state.checked);
    }

    componentDidMount() {
        let that = this;
        let c = that.refs.circleDom;
        c.onmousedown = function (ev) {	//鼠标按下时:
            let startX = ev.clientX;
            let startY = ev.clientY;
            document.onmousemove = function (ev) {	//鼠标拖动时:
                let x = ev.clientX;
                let y = ev.clientY;
                let moveX = x - startX;
                let moveY = y - startY;
                let cx = that.state.info.cx + moveX;
                let cy = that.state.info.cy + moveY;
                // 更新起始位置 避免移动错乱
                startX = ev.clientX;
                startY = ev.clientY;
                let info =
                    {
                        ...that.state.info,
                        cx: cx,
                        cy: cy,
                    };
                that.setState({
                    info,
                    checked: that.state.checked + 1,
                });
                that.props.move(info);

            };
            document.onmouseup = function () {
                document.onmousemove = document.onmouseup = null;
            }
        }
    }
}
export default Circle;

线处理

import React, {Component} from 'react'

class Line extends Component {
    constructor(props) {
        super(props);
        this.state = {
            checked: 0,
            info: this.props.info,
            attributes: this.props.info.attributes,
        }
    }


    render() {
        return (
            <line
                ref="circleLine"
                {...this.state.attributes}

                style={{stroke: "blue", strokeWidth: 2}}
            />
        )
    }

    componentWillMount() {
    	// 上传绑定信息
        this.props.bindRef(this, this.props.info.attributes.id);
    }
	// 验证刷新
    shouldComponentUpdate(nextProps, nextState) {
        return !(nextState.checked === this.state.checked);
    }

	// 圆线联动处理线
    changeLine = (XY) => {
        let attributes = this.state.attributes;
        attributes = {...attributes, ...XY};
        this.setState({
            attributes,
            checked: this.state.checked + 1
        })
    }

}
export default Line;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值