需求:
1、创建100个节点,每个节点的权值内容为-1~1之间的随机数,保留四位小数
2、创建100个点,每个点的横纵坐标随机,点可以为负但不可重复。每个节点对应一个点。
3、将节点根据权值排序,首尾相连得到一个权值对,用N表示.
得到50对权值对,表示为“<N(3,4),N(5.6)>”.前后为两个权值的点的坐标
4、用散点图将每个节点在图中表示出来,点击某个节点对N后相应的两个点连接起来
5、最后生成N的txt文件
6、提供一个文件点击可直接运行
1、创建文件夹
因为第六条的限制,所以node环境不能用,vue和react的脚手架之类的都不能用,创建本地文件,基础的html,css和js文件。引入vue.js
作为基础,element-ui.css
,element-ui.js
让页面更好看,echarts.common.min.js
用于生成散点图以及相应的图形操作,FileSaver.js
用于最后的生成txt文件。这些文件在对应的官网中都可以下载到。
2、生成权值和坐标
在vue的data中创建空的数据,后面生成数据可全局使用。
Weight: [], //权值
SortWeight: [], //排序后权值
Coordinate: [], //坐标
随机数 Math.random()
可以生成0-1中间的任意数,但它默认是保留小数点后17位,如果要保存四位的话,我们可以先乘一万,再除以一万,这样就得到四位小数了。正反用三目运算符,(Math.random() < 0.5 ? -1 : 1)
就可以随机的得到正数或者负数了。
去重 因为权值可坐标都不能重复,所以需要去重,两个原理相同,这里只解释权值的去重。因为需要100个数,我们先创建一个空的数组,没有数据的话,数组对应位置是undefined,利用这个特性,如果数组的第100个(下标为99)的值一直是undefined,那么就一直继续循环。先随机得到一个数,然后放到当前循环的数组对应位置中,将数组转化为·Set集合,Set集合会自动去重。然后再转化为数组,如果重复了,那么当前循环的这个位置值还是undefined,那么就将i-1,继续随机这个位置的数。一直到最后,就可以得到100个不同的数据了。
//随机生成权值,范围在0-1之间,保留四位小数
getWeight() {
let Weight = [];
let WeightSet = new Set(Weight);
for (let i = 0; Weight[99] === undefined; i++) {
//生成四位保留四位的小数
Weight[i] =
(Math.random() < 0.5 ? -1 : 1) *
(Math.round(Math.random() * 10000) / 10000);
//去重
WeightSet = new Set(Weight);
Weight = [...WeightSet];
if (Weight[i] === undefined) {
i--;
}
}
this.Weight = Weight;
this.SortWeight = Weight.sort();
},
//随机生成坐标点
getCoordinate() {
let Coordinate = [];
let CoordinateSet = new Set(Coordinate);
for (let i = 0; Coordinate[99] === undefined; i++) {
Coordinate[i] = [
(Math.random() < 0.5 ? -1 : 1) * Math.round(Math.random() * 100),
(Math.random() < 0.5 ? -1 : 1) * Math.round(Math.random() * 100),
];
//去重
CoordinateSet = new Set(Coordinate);
Coordinate = [...CoordinateSet];
if (Coordinate[i] === undefined) {
i--;
}
}
this.Coordinate = Coordinate;
},
3、权值配对
sort排序,然后首位相结合,在得到节点对应的数据,整理成最后N的数据
//处理要输出的最终数据
getN_data() {
let N_data = [];
for (let i = 0; i <= 49; i++) {
let data = {};
let Weight1 = this.SortWeight[i];
let Weight2 = this.SortWeight[99 - i];
let x1, x2, y1, y2;
for (let j = 0; j < this.Weight.length; j++) {
if (this.Weight[j] === Weight1) {
x1 = this.Coordinate[j][0];
y1 = this.Coordinate[j][1];
}
if (this.Weight[j] === Weight2) {
x2 = this.Coordinate[j][0];
y2 = this.Coordinate[j][1];
}
}
data = {
Weight1,
Weight2,
x1,
x2,
y1,
y2,
};
N_data[i] = data;
}
this.N_data = N_data;
},
4、画图,描点
初始化散点图,横纵坐标为空,根据不同的值来动态生成横纵坐标,点的值就是动态生成的点。因为需要连线,使用markLine来实现,数据也是动态的,刚开始为空。
//初始化表格
eCharsInit() {
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById("main"));
// 绘制图表
myChart.setOption({
xAxis: {},
yAxis: {},
series: [
{
type: "scatter",
data: this.Coordinate,
markLine: {
silent: true, // 鼠标悬停事件, true悬停不会出现实线
symbol: "none", // 去掉箭头
data: this.connectLine,
},
},
],
});
},
五、按钮连线
数据很多,挨个连感觉效率很低,但是要求中写的是点击后对应连上,那干脆两个功能都给准备吧
连接功能就是在markLine的data中加入相应的两个点。单独连接时获取对应的坐标和权值,权值用于展示,点用于连接。添加数据不难,麻烦的是添加玩后在页面上显示出来。因为echars是渲染数据后,数据就确定了。解决方法就是再渲染一遍,之前的数据不变,加上刚写的数据,就有那种效果了。全部连接就是循环加入每个节点对的坐标,然后一起添加到data中,最后一起渲染。
//点击后添加连线
Connect(x1, y1, x2, y2, w1, w2) {
this.connectLine[this.num] = [
{
coord: [x1, y1],
lineStyle: {
width: 1,
type: "solid",
color: "#3E3E3E",
},
},
{
coord: [x2, y2],
lineStyle: {
width: 1,
type: "solid",
color: "#3E3E3E",
},
},
];
this.eCharsInit();
this.$message({
message:
"已连接权值为" +
w1 +
"的点{" +
x1 +
"," +
y1 +
"} 和权值为" +
w2 +
"的点{" +
x2 +
"," +
y2 +
"}",
type: "success",
});
this.num++;
},
//连接全部
ConnectAll() {
for (let i = 0; i < this.N_data.length; i++) {
this.connectLine[i] = [
{
coord: [this.N_data[i].x1, this.N_data[i].y1],
lineStyle: {
width: 1,
type: "solid",
color: "#3E3E3E",
},
},
{
coord: [this.N_data[i].x2, this.N_data[i].y2],
lineStyle: {
width: 1,
type: "solid",
color: "#3E3E3E",
},
},
];
}
this.eCharsInit();
this.$message({
message: "已全部连接",
type: "success",
});
},
六、文件生成
因为这里功能比较死板,所以直接写死了。如果需要的话,可以把data当做变量传入这个函数中来即可。
//下载文件
downloadText() {
let data = this.N_data;
var blob = new Blob([JSON.stringify(data)], {
type: "text/plain;charset=utf-8",
});
saveAs(blob, "export.txt");
},
代码上传到码云上了https://gitee.com/xu_ming_xi/node_connection
如果有需要的小伙伴可以自己下载下来看看。