最近公司需要对目标移动标点,适合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;