d3.js+react实现算法可视化:排序篇

来源:互联网 发布:万网域名指向 编辑:程序博客网 时间:2024/06/06 05:18

前言:

知道d3.js已经很长时间了,直到去年才好好看了一些教程写了一些demo,当时还是3.x版。最近这几天在看react、react-router之类的,想自己写些东西出来,刚好想到该复习下d3.js,自己就做了这个算法可视化的demo,目前只做了排序,包括冒泡排序、插入排序、选择排序、归并排序、快速排序,之后有时间的话会继续做其他的数据结构与算法的可视化。

目前有很多算法可视化的网站,比如新加坡的National University of Singapore就有个visualgo:点击打开链接

visualgo的算法已经很齐全了,而且界面、交互都很不错,比起我的demo那就强太多了安静

我的demo演示地址:点击打开链接(起了个山寨味很浓的名字,VisualCompute)

使用npm+jspm搭建工程,使用react-router + d3.js 操作svg图像,顺便使用了Materialize当UI(关于select控件有个坑,onChange事件获取不到)。


基本思路:

由于react建议不直接操作dom,所以d3的选择器也就用不上了,svg都是由react进行render,react的方便之处在于每次更新state后会重新render一下dom,所以在排序的时候,每当数据发生变化,就更新state。d3的优势就在于可以进行比例变换,根据数据的大小,把svg的高度变换的一个指定的范围,这样免得图形过大或过小。而且d3还能生成动画,不过我这里暂时没用上。

所以比较关键的地方就在于,如何更新state?

对于一个排序算法,比如冒泡排序,每次数据发生变化时怎么样通知外界呢?通过回调函数?但是在算法可视化过程中要有能力暂停算法,然后更新dom,之后再继续运行算法(比如要支持用户手动点击“下一步”),所以我觉得纯粹使用callback似乎有些麻烦。刚开始想的是保存每一步中间结果,这样不仅可以让算法“下一步”,还能“上一步”观察结果,不过后来突然想到es6的generator,generator可以生成迭代器啊!而且还把控制权限交给了外界!有外界决定这个generator是否要运行下一步,感觉很适合。由于之前一直没有用过generator,想想还是趁此机会用下吧。


ES6 Generator

这里先把阮一峰老师的博客搬过来:Generator 函数的含义与用法

基本语法

  • 使用function*(){}定义,返回的是一个迭代器,函数内部使用yield产出数据,每次迭代器调用next会获得下一个yield产出的数据
function* Generator(){    yield 1;    let v = yield 2;    yield v;}var iterator = Generator();console.log(iterator);//输出{},只有调用next后才有数据console.log(iterator.next());//{ value: 1, done: false }console.log(iterator.next());//{ value: 2, done: false }console.log(iterator.next());//{ value: undefined, done: false }, yield并不能把值传给v,yield*+generator可以console.log(iterator.next());//{ value: undefined, done: true }
  • 使用yield*可以产出迭代器

function* Generator1(){    yield 1;    let v = yield* Generator2();//这里使用的是yield*,会把Generator2的控制器接入,如果使用yield,则还需要调用next一次后才能得到迭代器    yield v;}function* Generator2(){    yield 2;    return 3;//这里的return会传给Generator1中的v}var iterator = Generator1();console.log(iterator.next());//{ value: 1, done: false }console.log(iterator.next());//{ value: 1, done: false }console.log(iterator.next());//{ value: 3, done: false }console.log(iterator.next());//{ value: undefined, done: true }
下面就以冒泡排序来说明下demo中的实现


Generator+冒泡排序

  • 冒泡排序JS实现:

var data = [    {v:5,color:"blue"},    {v:6,color:"blue"},    {v:2,color:"blue"},    {v:0,color:"blue"},    {v:7,color:"blue"},    {v:3,color:"blue"},    {v:9,color:"blue"}];//这里定义一些svg柱状图的参数,包括高度和颜色两种属性function bubbleSort(data){    var length = data.length;    for(var i=0;i<length;i++){        for(var j=0;j<length-1;j++){            if(data[j].v>data[j+1].v){                //交换                let t = data[j].v;                data[j].v = data[j+1].v;                data[j+1].v = t;            }        }    }}bubbleSort(data);console.log(data.map(v=>v.v));//[ 0, 2, 3, 5, 6, 7, 9 ]

  • generator+冒泡

function* bubbleSort(data){    var length = data.length;    for(var i=0;i<length;i++){        for(var j=0;j<length-1;j++){            data[j].color = colorYellow;//把当前这步的数据标黄            yield data;//产出数据            if(data[j].v>data[j+1].v){                //交换                data[j].color = data[j+1].color = colorRed;//把要交换的数据标红                yield data;//产出数据                let t = data[j].v;                data[j].v = data[j+1].v;                data[j+1].v = t;                yield data;//产出交换位置后的数据            }            resetColor(data);//重置数据color颜色属性        }    }}

这个generator版的冒泡排序可以有外界调用next方法进行下一步运算,而且会把data返回出来,由于data中的一些数据发生变化,这时候通过react的setstate方法可以实现dom的更新,调用示例如下:

 /**********处理各种算法************/        var iter = bubbleSort(data);        var go = function() {            let currentData = iter.next();            if(!currentData.done){//更新state                this.setState( (prevState) => {return {"data":currentData.value}});                setTimeout(go,500);            }        }.bind(this);        setTimeout(go,0);



冒泡的最后效果如下:


其他排序都是差不多的思路,只不过在快速排序和归并排序中用到了yield* 。


 源码

demo的源码可以在本文开头提到的连接上复制下来,也可以在这里找到:https://git.oschina.net/liuyaqi/VisualCompute

要使用npm install和jspm install

demo演示地址:点击打开链接

原创粉丝点击